Compare commits

..

918 Commits

Author SHA1 Message Date
Christoffer Lerno
0de47d7c83 Ensure panic functions are never stripped. 2023-06-02 23:19:54 +02:00
Christoffer Lerno
cfd21f8ca2 Windows thread pool. 2023-06-02 23:19:54 +02:00
Christoffer Lerno
d0e8944c56 Updated task pool. 2023-06-02 21:58:25 +02:00
Christoffer Lerno
3e54d13b62 Prefer def 2023-06-02 20:08:45 +02:00
Christoffer Lerno
b30d130d92 Configurable Linux crt/crtbegin paths. 2023-05-31 21:26:23 +02:00
Christoffer Lerno
4cf98dab93 Add special ubuntu-20 release 2023-05-31 12:42:31 +02:00
Christoffer Lerno
ea1a5435bb Dead strip on "strip unused" 2023-05-30 16:42:15 +02:00
Christoffer Lerno
275e3c6a09 Update with CPU type. 2023-05-30 16:42:15 +02:00
Christoffer Lerno
9de02efa01 Exclude main methods from dllexport. 2023-05-28 15:59:15 +02:00
Christoffer Lerno
e0cfb39d79 Add DLL export for exported functions on win32. 2023-05-28 15:00:46 +02:00
Christoffer Lerno
d4259368a2 Remove call convention. 2023-05-26 14:22:50 +02:00
Christoffer Lerno
07b107ff5e Better handling of attribute definition errors. Resolves #753 2023-05-26 11:31:27 +02:00
Christoffer Lerno
b794c893d6 Dynamic dispatch. 2023-05-25 22:28:45 +02:00
Christoffer Lerno
2e498a426e Improved the README example somewhat. 2023-05-22 09:47:46 +02:00
Christoffer Lerno
0778537540 Update mac versions to test 15 and 16. Update release version to 16. 2023-05-21 22:06:24 +02:00
Christoffer Lerno
ddd0497922 Better lowering of distinct types. Noreturn function call expr recognized as a "jump" for escape analysis. Preferring "def" in libs. To upper / to lower for ascii. Initial dynlib support. 2023-05-21 21:41:01 +02:00
Tonis
a877d4458c Improve Matrix identity functions and add Quaternion to matrix function (#765)
* Edit matrix identity fn and add quaternion to matrix fn

* Change matrix identity macros to constants

---------

Co-authored-by: Tonis <tanton@paysure.solutions>
2023-05-16 11:50:01 +02:00
Christoffer Lerno
3a725d1348 Better error on missing ';' in certain cases. 2023-05-15 08:45:10 +02:00
Christoffer Lerno
353a072b75 Fix for getting the correct generic type of consts. Fix of late initialization of structs using compound literals. 2023-05-14 17:23:45 +02:00
Christoffer Lerno
8eddbfb708 Fix to net::os::posix. Remove "\s" 2023-05-12 16:55:15 +02:00
Christoffer Lerno
021bcdcf21 Add "is_initialized" to check if a map has been initialized. 2023-05-10 13:12:31 +02:00
Christoffer Lerno
bff7b492a2 Further bitstruct cast fixes. Updated code. 2023-05-10 13:03:15 +02:00
Christoffer Lerno
4d0f73a8f5 Consistent naming in allocators. Fix where cast from char array -> bitstruct would not work. 2023-05-10 10:30:37 +02:00
Christoffer Lerno
6210522c75 Update error message, disable 17 from CI 2023-05-08 18:48:40 +02:00
Christoffer Lerno
13f808b552 Added acos/asin(h) and atanh 2023-05-08 10:50:05 +02:00
Christoffer Lerno
dc30c8edc2 Fix complaints of broken compilers. 2023-05-08 00:18:39 +02:00
Christoffer Lerno
ee5ad170e0 Simplify detection of initialized variable. 2023-05-08 00:13:12 +02:00
Christoffer Lerno
172d561f07 Change syntax of $if, $assert, $include, $echo. Introduces $error 2023-05-06 12:18:00 +02:00
Christoffer Lerno
3dd6675e1b Fixed const vector codegen. Missing math comparisons. 2023-05-06 02:29:26 +02:00
Christoffer Lerno
db8c46d6c5 Addition of "distinct" and "inline" as keywords. Removal of "alias" keyword. 2023-05-04 08:48:17 +02:00
Christoffer Lerno
6fc38bbcb9 Accidental change. 2023-04-28 23:36:45 +02:00
Christoffer Lerno
184cc19d36 Further grammar fixes. 2023-04-28 19:11:57 +02:00
Christoffer Lerno
e25c06a065 Fixes to the grammar. 2023-04-27 12:26:58 +02:00
Christoffer Lerno
f2f514da74 Fixes to the grammar. 2023-04-26 17:24:35 +02:00
Christoffer Lerno
619fa26c26 Updated LLVM for Windows 2023-04-25 16:09:04 +02:00
Christoffer Lerno
e8642d6797 Fixes to access grammar. "delete" => "remove" 2023-04-24 09:10:35 +02:00
Christoffer Lerno
d1c2fbd79f Fix for MSVC 2023-04-21 17:47:32 +02:00
Christoffer Lerno
2a79e0f1cf Introduce def as a trial. Fixup of timeit. 2023-04-21 17:42:38 +02:00
Christoffer Lerno
c847650579 Introduce def as a trial. Fixup of timeit. 2023-04-21 16:03:28 +02:00
Christoffer Lerno
edd2f1c717 Updated timeit. 2023-04-21 15:57:48 +02:00
Christoffer Lerno
0a12686237 Remove acornvm 2023-04-21 15:51:00 +02:00
Christoffer Lerno
8059dc1539 delete_if, retain_if, rindex_of, compact, compact_count added to List. 2023-04-21 14:45:25 +02:00
Christoffer Lerno
809321e20c Updated grammar. Removal of elif. Removal of ':' ';' in some ct statements. Empty faults is now an error. Remove "define" for types. Remove "private". Better errors on incorrect bitstruct syntax. Introduction of wildcard type rather than optional wildcard. Removal of scaled vector type. mkdir and rmdir. Disallow define @Foo() = { @inline }. Add handling for @optreturn and change it to @return!. Restrict interface style functions. Updated x64 ABI. stdlib updates to string. Removed deprecated functions. Update how variadics are implemented. Extended error messages. x86 ABI fixes. Shift check fixes. '!' and '?' are flipped. No trailing ',' allowed in functions. Fix to string parsing. Allow l suffix. Simplifying flatpath. any replaces variant, anyfault replaces anyerr. Allow getting the underlying type of anyfault. De-duplicate string constants. Fix of readme. Extended list. Fix of "(MyEnum)x + 1". Clock and DateTime types. Fixes to array concat. 2023-04-21 10:56:39 +02:00
Christoffer Lerno
d14e778232 Use different readdir on macOS depending on arch. 2023-04-10 09:54:02 +02:00
WraithGlade
18c1b20ea0 Fixed two typos and merged them properly. (#758)
* Fixed typo: "do" --> "does".

* Fixed typo: missing "is".
2023-04-02 17:25:16 +02:00
Christoffer Lerno
ad7ee06635 Grammar updates. Updated enum member parsing. 2023-03-22 21:46:47 +01:00
Christoffer Lerno
0f80d985fa Fix sema errors on flexible array slices. 2023-03-22 12:56:15 +01:00
Christoffer Lerno
316af36723 Add dstringwriter. 2023-03-22 12:23:47 +01:00
Christoffer Lerno
9850adfa56 Rename OUT_OF_DISK => OUT_OF_SPACE. Allow byte writer to take a buffer. 2023-03-22 11:52:45 +01:00
Christoffer Lerno
172ae8a3a5 Only rudimentary parsing of "private" in prefix location. Make contract handling more correct. 2023-03-21 23:56:49 +01:00
Christoffer Lerno
07e3ced84e Deprecate @extname 2023-03-21 16:41:26 +01:00
Christoffer Lerno
1c4ab48da7 Update for mingw 2023-03-21 14:06:11 +01:00
Christoffer Lerno
0a826d588f Update 2023-03-21 00:59:41 +01:00
Christoffer Lerno
93fa135d28 Deprecation of define Type = ... 2023-03-20 22:56:06 +01:00
Christoffer Lerno
9c145996b0 $elif deprecated. 2023-03-20 16:54:45 +01:00
Christoffer Lerno
02d073675a Fix to deprecated syntax. 2023-03-20 11:21:55 +01:00
Christoffer Lerno
3beed8a0f1 No exceptions compiling C++ 2023-03-20 09:51:57 +01:00
Christoffer Lerno
5851de30ab Try enabling 17 again. 2023-03-20 09:51:57 +01:00
Christoffer Lerno
5ee0d52ff1 Deprecate :; in $if etc. 2023-03-20 01:03:54 +01:00
Christoffer Lerno
cc87c77af3 Add deprecation notice on use of "private" 2023-03-19 23:50:00 +01:00
Christoffer Lerno
24147a85f7 Allow unary plus. 2023-03-19 22:46:28 +01:00
Christoffer Lerno
954521228e Make casts explicit when casting to pointer offset. Better localization of error when narrowing fails. New printf formatting based on musl plus %a output. 2023-03-19 21:15:44 +01:00
Christoffer Lerno
9fa634b78b is_finite / is_nan / is_inf, frexp native. 2023-03-18 21:17:18 +01:00
Christoffer Lerno
48a35b3277 Signbit, tests of frexp. 2023-03-18 19:13:17 +01:00
Christoffer Lerno
a041c53cdd Add frexp. 2023-03-18 16:54:39 +01:00
Christoffer Lerno
9f068ce84d Shorter code. 2023-03-18 14:44:35 +01:00
Christoffer Lerno
8b0df0ee11 try? / catch? 2023-03-17 22:49:48 +01:00
Ox512
1b667cbc93 Update the default project template (#750) 2023-03-15 19:40:49 +01:00
Christoffer Lerno
7075f834dd Remove null-casts for anyerr. 2023-03-15 16:28:48 +01:00
Christoffer Lerno
e2b9a35dfe Add tentative ".ordinal" on faults. Allow anyerr and fault to be initialized with null. 2023-03-15 14:32:00 +01:00
Christoffer Lerno
3cb94a2857 Temporarily disable LLVM 17. Generic module contracts enabled. 2023-03-14 23:25:52 +01:00
Christoffer Lerno
3237f87a09 Added some Win32 declarations. 2023-03-14 11:37:23 +01:00
Christoffer Lerno
1b27264f07 Allow type inference on enum comparisons. Add chdir. Fix bug when command was missing. Allow {} on basic types. 2023-03-13 16:25:03 +01:00
Christoffer Lerno
fb761b0cc5 Added OnStack allocator. Added dirname, basename and extension to path functions. 2023-03-13 11:51:27 +01:00
Christoffer Lerno
4ffeada3c7 Updated stdlib. Prefer file::open. Fix to slice assign with distinct types. 2023-03-12 00:33:16 +01:00
Christoffer Lerno
2607062cb6 Updated Path. Some work towards loading dirs. 2023-03-11 18:32:44 +01:00
Christoffer Lerno
7a2d73c690 mproved Path code. 2023-03-10 18:46:21 +01:00
Christoffer Lerno
d2a16961cf More refactorings in the stdlib. More Path functions. Updated Win32 format for types. Fix bug with codegen of defer if ... More string functions. 2023-03-09 20:44:27 +01:00
Christoffer Lerno
39dd3e40a6 Remove attribute list from test. 2023-03-08 16:23:12 +01:00
Christoffer Lerno
1480b8f872 Update examples and contracts. 2023-03-08 15:57:42 +01:00
Christoffer Lerno
b94c647ead Distinct String 2023-03-08 14:32:07 +01:00
Christoffer Lerno
9b81623680 Make string literals default to char[] 2023-03-08 11:24:11 +01:00
Christoffer Lerno
cfba19ab77 Remove deprecated to avoid warnings for VarString. Fix issue casting subarrays to distinct types. 2023-03-08 00:13:48 +01:00
Christoffer Lerno
89de0a70d2 "@ensure" now correctly only runs on non-optional results. Subtypes now merge to a single type. Beginning deprecation of "std::core::str". Refreshed String functions. Consistent use of ".using" parameter. Functions moved to string methods. Tests on more string methods. Fixes to split, rindex_of. 2023-03-08 00:13:48 +01:00
Christoffer Lerno
33cc2d889b More const modification detection. Grab the version number into CMake. 2023-03-07 14:35:12 +01:00
Christoffer Lerno
ab1c025c05 Fix issue for wasm 2023-03-07 10:12:03 +01:00
Christoffer Lerno
7008dab113 Add primitive and incomplete object printout. First version of json parser. 2023-03-07 09:53:01 +01:00
Christoffer Lerno
300f4d38ab Addition of object type. Fixes to const union initialization. 2023-03-06 23:58:13 +01:00
Christoffer Lerno
9eee250b10 Add DString init. 2023-03-06 09:12:52 +01:00
Christoffer Lerno
a4231823df Add DString init. 2023-03-05 23:28:04 +01:00
Christoffer Lerno
d1626ada6f List no longer uses a temp allocator by default. Incorrect check in the temp allocator removed. Added DString. 2023-03-05 23:14:16 +01:00
Christoffer Lerno
6ac99ed83c Improve debug info. 2023-03-05 20:50:04 +01:00
Tonis
ff4c35fae1 Added Matrix identity macros and few matrix unit tests (#742)
* Add matrix identity macros

* Add matrix some matrix unit tests

---------

Co-authored-by: Tonis <tanton@paysure.solutions>
2023-03-05 20:39:55 +01:00
Dmitry Atamanov
549e1b6029 Added std::collections::enummap. 2023-03-04 22:41:22 +01:00
Christoffer Lerno
1035de3b36 Remove all locations when enums are implicitly lowered to integers. Remove the menagerie of flattening functions. Incidentally this also fixes a bunch of not-yet tested errors with distinct types. 2023-03-04 22:14:29 +01:00
Christoffer Lerno
306677300a Fix of enum alignment / size when based in distinct types. 2023-03-04 01:35:23 +01:00
Christoffer Lerno
f9923de7f9 Remove implicit cast from enum to int. Allow enums to use distinct types as the backing type. enum += 1 returns enum type. 2023-03-04 01:03:57 +01:00
Christoffer Lerno
488472ecbb Improve enum add/sub. 2023-03-03 17:43:13 +01:00
Christoffer Lerno
ea3b32f68b Further casting cleanup. 2023-03-03 14:43:32 +01:00
Christoffer Lerno
a2911292d8 Simplify narrowing semantics. 2023-03-02 22:04:15 +01:00
Christoffer Lerno
3449d2ea88 Remove all array pointer decay. 2023-03-02 19:47:24 +01:00
Christoffer Lerno
3372f36e9d Fix subarray casts to distinct types and constants. 2023-03-02 13:14:11 +01:00
Christoffer Lerno
07e4aab48b Fix of bool -> float vector cast. Correctly widen C style varargs for distinct types and optionals. 2023-03-02 11:35:05 +01:00
Christoffer Lerno
0dcad6f5cf Improve and simplify casts. 2023-03-01 22:43:50 +01:00
Christoffer Lerno
0314f9534f Improve enum checks on enum conversions. 2023-03-01 13:56:36 +01:00
Christoffer Lerno
f8208f946b Remove "generic" code paths. 2023-03-01 12:01:43 +01:00
Christoffer Lerno
b9dbefbe1b Fix bug when initializing nested struct / unions. Fix of mult of 2x2 matrix. Cleanup of cast. 2023-02-28 20:43:43 +01:00
Christoffer Lerno
6188a8b5df Fix bad code in assert 2023-02-28 17:50:01 +01:00
Christoffer Lerno
9db845903e Cleanup and allow complex array length inference, e.g. "int[*][2][*] a = ..." 2023-02-28 17:37:17 +01:00
Christoffer Lerno
cc19168c7b Some general cleanup. 2023-02-28 01:15:30 +01:00
Christoffer Lerno
1c83a484da Print warnings on deprecated. 2023-02-27 18:59:25 +01:00
Christoffer Lerno
bd8bff85d6 Add tracking allocator. Fix substruct issue. 2023-02-27 17:05:27 +01:00
Christoffer Lerno
dd4edfb747 Updated malloc/calloc/realloc/free deprecation of old helper functions. Add checks to prevent incorrect alignment on types when using malloc. Better errors from $assert. Added @deprecated. Fixed issue using named arguments after varargs. 2023-02-27 15:03:27 +01:00
Christoffer Lerno
8ad8af861e Fix to nested $if analysis. 2023-02-27 01:10:46 +01:00
Christoffer Lerno
feaf8b3b2c Update HashMap functionality. Fix check when appending to VarString. Fix issue with decls accidentally invalidated during $checked eval. Fold optional when casting slice to pointer. 2023-02-27 00:10:01 +01:00
Christoffer Lerno
10272dbf38 Delay C abi lowering until requested on-demand to prevent circular dependencies. 2023-02-24 22:01:55 +01:00
Christoffer Lerno
2f255ac5c3 Sha1. 2023-02-24 10:49:01 +01:00
Christoffer Lerno
a7ce0f95e6 Refactor allocator locations. 2023-02-24 00:29:24 +01:00
Christoffer Lerno
fc0cad2894 Remove abs from libc. Create abstract random. 2023-02-24 00:00:36 +01:00
Christoffer Lerno
2a6339a25e Updated native variants of file handling. Fixed $if folding chain. 2023-02-23 18:00:34 +01:00
Christoffer Lerno
f86aa136cb Updated fopen. ZString.len does not output number of Char32. Add example. 2023-02-23 10:42:36 +01:00
Christoffer Lerno
b175b9318a Fix conversion if (int x = foo()). Initial stream api. Extended enumset. 2023-02-22 17:06:06 +01:00
Christoffer Lerno
8f5676b488 Add defer catch/try. Fix missing defer invoked on return a > 0 ? Foo.ABC! : 1 2023-02-21 20:10:03 +01:00
Christoffer Lerno
b5a2b5c68a Added a short project suggestion list. 2023-02-21 15:54:20 +01:00
Christoffer Lerno
afa41f0c10 Updated name mangling. 2023-02-20 17:48:47 +01:00
Christoffer Lerno
b6ff6bae8e Update version. 2023-02-20 16:02:50 +01:00
Christoffer Lerno
e3416a1c40 Allow @test with modules. Change name mangling for non exports. 2023-02-20 16:02:30 +01:00
Christoffer Lerno
d35d50555e ipv4/ipv6 parsing and back to string. 2023-02-20 09:31:11 +01:00
Christoffer Lerno
34eac23e23 Fix when comparing const values > 64 bits. 2023-02-20 08:03:36 +01:00
Christoffer Lerno
19963e4e19 Fix attributes for nested bitstructs. Add some functions to std::net 2023-02-19 23:35:02 +01:00
Christoffer Lerno
62fbf4da47 Add simple bitstruct. 2023-02-18 16:59:52 +01:00
Christoffer Lerno
6e8c69cd52 Add & ^ | to bitstructs. 2023-02-18 16:21:30 +01:00
Christoffer Lerno
ea163636d3 Add defaults to compare_exchange, small fix in printf. Disallow obviously wrong code that returns the pointer to a variable on the stack. 2023-02-18 12:21:15 +01:00
Christoffer Lerno
3da9008fdc $$DATE is fixed. 2023-02-18 00:03:46 +01:00
Christoffer Lerno
9061699adf Fix of $vasplat when used multiple times. 2023-02-17 20:31:17 +01:00
Christoffer Lerno
d6a1cecb9d Fix of LLVM codegen for optionals. 2023-02-17 13:45:16 +01:00
Christoffer Lerno
baa2e474b5 Added $$atomic_store and $$atomic_load. 2023-02-17 13:22:12 +01:00
Christoffer Lerno
a0a5c940f1 Add --strip-unused. 2023-02-16 22:11:42 +01:00
Christoffer Lerno
0aa776d61b Allow @export on modules, to implicitly @export all in the module section. Add expect on error comparisons, expecting them to be false by default. Support @extern on modules. 2023-02-16 13:05:09 +01:00
Christoffer Lerno
c26655a05a Remove usize/isize completely. 2023-02-16 00:48:37 +01:00
Christoffer Lerno
818396b6f3 Updated module visibility import visibility. Fixes to @local 2023-02-15 09:47:51 +01:00
Dmitry Atamanov
4519eebe4d Added @likely, @unlikely and @expect macros. (#727) 2023-02-15 00:02:01 +01:00
Christoffer Lerno
adc424ba1d Fixed missing check on &var.myFunction 2023-02-14 23:44:05 +01:00
Christoffer Lerno
b85d521dc1 Fixed missing check on &var.myFunction 2023-02-14 21:56:56 +01:00
Christoffer Lerno
8b099293a3 Fix no-entry. Make printf more strict and also fix distinct printing. 2023-02-14 20:39:15 +01:00
Christoffer Lerno
8d306ce64d Fix no-entry. 2023-02-14 20:13:22 +01:00
Christoffer Lerno
df77b692d6 Support "typedef" 2023-02-14 16:49:27 +01:00
Christoffer Lerno
b7e19b75d0 Improve error reporting for define and function definition. 2023-02-14 15:49:36 +01:00
Christoffer Lerno
61e26d8188 Fixes to $include 2023-02-14 12:27:17 +01:00
Christoffer Lerno
03cd56e46b Add @local and fix visibility issues for generic methods. 2023-02-14 12:17:56 +01:00
Christoffer Lerno
8184fba34b Delay type evaluation further. Current analysis shows that this code should be safe, but there may be some issues lurking. 2023-02-14 09:27:03 +01:00
Christoffer Lerno
3cfef690d3 Support (void)foo for any type. 2023-02-13 13:52:48 +01:00
Christoffer Lerno
5e457be605 Implement more @export / @private improvements. Make @private default… (#729) 2023-02-13 08:31:40 +01:00
Christoffer Lerno
3b49b87784 Fix for nolibc (eg wasm). Add multi global declarations. Simplicity wasm export / import. Prevent "extern" functions to have a function body. 2023-02-11 11:16:31 +01:00
Christoffer Lerno
2fa3ae7bc9 Restore CMake settings. 2023-02-11 02:05:37 +01:00
Christoffer Lerno
1548cd06ef Enable local multi-declarations. Fix of builtin argument checking. Migrate to @noinit. 2023-02-11 02:03:02 +01:00
Christoffer Lerno
18de9a146b Fix copysign and make floatparse more strict. 2023-02-11 00:41:44 +01:00
Christoffer Lerno
ddd8be0f38 Fix global noinit. Add @noinit. With tests. 2023-02-11 00:10:02 +01:00
Christoffer Lerno
74d868d113 Fix of bug in !floatval codegen. Added float parsing. 2023-02-10 22:17:55 +01:00
Christoffer Lerno
b3f15a867c Added "clear" to VarString 2023-02-10 08:40:08 +01:00
Christoffer Lerno
0a0cc4b5df Header printout complete. "@export" added but might get removed. 2023-02-09 23:16:18 +01:00
Christoffer Lerno
f313e90e28 Some work on headers. Fix unsigned negation on consts. 2023-02-09 09:15:51 +01:00
Christoffer Lerno
a6b9405e21 Fix @expect. 2023-02-07 15:45:46 +01:00
Christoffer Lerno
508cc8f29f Update comment. 2023-02-07 15:40:56 +01:00
Christoffer Lerno
1843870dfd Fix MSVC 2023-02-07 11:16:35 +01:00
Christoffer Lerno
926dbfc535 Fix "int $f = 1;" and int $f; 2023-02-07 00:22:31 +01:00
Christoffer Lerno
cc189a8166 Support user defined attributes in generic modules. Fix bug with user defined attributes with parameters. Fix bug that would overwrite attributes when parsing user defined attributes. 2023-02-06 23:31:03 +01:00
Christoffer Lerno
c89dbba6d1 Conform to the aarch64 ABI when passing invalid vectors. 2023-02-06 22:54:59 +01:00
Christoffer Lerno
9e4203e267 Fix missing "max type" cases. 2023-02-06 21:53:33 +01:00
Christoffer Lerno
6cef75b608 Removes win x86 target. Add win aarch64. Fixes to jump buffer sizes. Fix returning bool[2] in the SysV ABI. Array comparison now works. Prevent flexible array comparisons. Prevent zero size unions. 2023-02-06 18:09:31 +01:00
Christoffer Lerno
3c4796d65a Insert nullcheck for & params on the callee side. 2023-02-05 23:18:35 +01:00
Christoffer Lerno
e0252a6d7a Make it possible to use @deprecated in docs. 2023-02-05 22:13:50 +01:00
Christoffer Lerno
505543e9b4 Remove import of std::array 2023-02-05 20:59:42 +01:00
Christoffer Lerno
86e085e0c0 Move collection types. Improve linked list interface. Update map.destroy => map.free 2023-02-05 20:55:47 +01:00
Christoffer Lerno
4a102698b2 Add tests to math and add info in readme how to contribute. 2023-02-05 14:30:06 +01:00
Christoffer Lerno
5a65a57e42 Catch macro infinite recursion. Fix of tan function. 2023-02-05 00:21:26 +01:00
Christoffer Lerno
38b44a7265 Fix of tan. 2023-02-04 23:32:17 +01:00
Christoffer Lerno
0f7d21330a Optimize optional return. 2023-02-04 19:48:42 +01:00
Christoffer Lerno
dce171670f Use printn rather than println. Add string methods for copying. 2023-02-04 19:48:42 +01:00
Christoffer Lerno
6b928c7a3d Add saturated math and expect macros. 2023-02-03 00:25:29 +01:00
Christoffer Lerno
6407eb47a4 Remove of @extname in stdlib. 2023-02-02 21:53:37 +01:00
Christoffer Lerno
3b3dd334e0 Remove $if in thread 2023-02-02 21:41:57 +01:00
Christoffer Lerno
3b3773663a Adding sincos / libc tan/tanf. 2023-02-02 19:29:29 +01:00
Christoffer Lerno
0f4d20f168 Fix issue with hex floats with f being assumed to be double. Added cos sincos sincosf pow2 pow2f to nolibc. 2023-02-02 13:20:52 +01:00
Christoffer Lerno
1d8e341572 Wasm allocator is now __heap_base aware. Make builds overridable from the project. 2023-02-01 16:42:45 +01:00
Kenta
f9b6f1da0e Update trig.c3
Add cosf
2023-02-01 16:42:16 +01:00
Christoffer Lerno
6a3219ad43 Make thread allocator take the thread allocator by default for allocating initial memory. Add some int128 methods. Fix attribute parsing. 2023-02-01 14:06:14 +01:00
Christoffer Lerno
f916aa9189 More support for working with win32 types. 2023-01-31 20:54:42 +01:00
Christoffer Lerno
c665a431ad Make wasm use .wasm for executables. 2023-01-31 19:46:53 +01:00
Christoffer Lerno
4ea81fe636 More riscv tests. Fix missing zero/signext attributes on calls. Fixes and simplification to riscv ABI. 2023-01-31 14:09:32 +01:00
Christoffer Lerno
0afd55425a More ABI cleanup and fixes to RISC-V ABI + tests for riscv32. 2023-01-30 23:46:06 +01:00
Kenta
dbb759713f Update build-with-docker.sh
Improve the bash script by taking user input straight to a variable with read -p. 
Clean the code up.
2023-01-30 23:44:40 +01:00
Kenta
61cc8163f9 Update docker script and dockerfile
Using Ubuntu 23 throws an error "groupadd: GID '1000' already exists" when trying to build. Ubuntu 22 works fine.

There should be no difference between building C3 on Ubuntu 22 vs 23.

To avoid issues raised it's best to move to single Ubuntu version that builds the compiler.
2023-01-30 21:47:37 +01:00
Christoffer Lerno
adf84e38d0 Update LLVM versions. 2023-01-30 13:54:28 +01:00
Christoffer Lerno
ae4658933a Add missing version bump. 2023-01-30 12:39:46 +01:00
Christoffer Lerno
539d733ceb Remove LLVM 14 support. Simplify ABI lowering. 2023-01-30 12:39:04 +01:00
Christoffer Lerno
124a18a486 Fix bug with enum associated values. 2023-01-29 19:15:13 +01:00
Christoffer Lerno
3046a9f0c2 Fix File.printf 2023-01-29 17:58:17 +01:00
Christoffer Lerno
a77e0bf7b3 Add LLVM 17 to CI. 2023-01-29 14:44:03 +01:00
Christoffer Lerno
84582f86b6 Cleanup. 2023-01-29 14:20:58 +01:00
Christoffer Lerno
3f5fb5da92 Fix of variant type. 2023-01-29 13:59:07 +01:00
Christoffer Lerno
2fb85e5e95 Fix of variant type. 2023-01-29 13:58:57 +01:00
Christoffer Lerno
e604d40fd3 Added $$FILEPATH 2023-01-29 13:26:34 +01:00
Christoffer Lerno
77608e137e Updated winmain handling 2023-01-29 13:18:43 +01:00
Christoffer Lerno
52d7e58c19 Make Windows use wWinMain always. Support /SUBSYSTEM:WINDOWS 2023-01-29 01:32:35 +01:00
Christoffer Lerno
5ea15eb708 Make Windows use wWinMain always. Support /SUBSYSTEM:WINDOWS 2023-01-29 01:32:35 +01:00
Christoffer Lerno
f89bf9ea2f Update clamp. 2023-01-28 03:52:07 +01:00
Christoffer Lerno
4ddb9d9fbc Fix tests. 2023-01-28 03:52:07 +01:00
Christoffer Lerno
1095f098ef Reduce formatter register formatter register type memory usage. 2023-01-28 01:23:15 +01:00
Christoffer Lerno
9f63f77f22 Update precedence clarification rules for ^|& 2023-01-28 00:54:01 +01:00
Christoffer Lerno
9a08c9d821 Fixes to wasm and function attributes. 2023-01-28 00:37:46 +01:00
Christoffer Lerno
445239b418 Add simple heap allocator and update lambda and #lazy checking. 2023-01-27 14:40:54 +01:00
Christoffer Lerno
03b659373a Make the quine use "String" rather than char[] 2023-01-26 21:19:16 +01:00
Christoffer Lerno
4561bf5a85 Add quine example. 2023-01-26 21:17:08 +01:00
Christoffer Lerno
6eb65d5b37 Add memory-env option. 2023-01-26 21:16:47 +01:00
Christoffer Lerno
6a73c8e90e Clean lib before copy. 2023-01-26 16:28:47 +01:00
Christoffer Lerno
5c7d859fdb Some updates to builtin checking. 2023-01-26 16:24:18 +01:00
Christoffer Lerno
a95710c93f Add no-entry to project/command line. Add "link-args" to project. Add @wasm and @extern attributes. Added $$wasm_memory_size and $$wasm_memory_grow builtins. 2023-01-26 12:02:09 +01:00
Christoffer Lerno
39801a304d Improved support for freestanding. 2023-01-25 11:27:57 +01:00
Christoffer Lerno
a22ebbb0ef Improved support for freestanding. 2023-01-25 11:10:37 +01:00
Christoffer Lerno
f37f779e5a Fix in utf8to16 conversion. 2023-01-24 14:20:37 +01:00
Christoffer Lerno
b508a43f8f Add lambdas. 2023-01-24 10:15:23 +01:00
Christoffer Lerno
c9e1e2d763 Replace $$shufflevector with $$swizzle and $$swizzle2. Add builtin swizzle accessors. 2023-01-22 01:12:55 +01:00
Christoffer Lerno
1e18e576c7 Fix int[] -> void* casts. 2023-01-21 20:17:48 +01:00
Christoffer Lerno
5151586450 Updated complex / matrix. Added quaternion math, vectors. Possible to add and mult scalar with vector. Fix where negating a float vector would be lowered incorrectly. Fix where $typeof(x) { ... } would not be valid compound literal. Fix where var would not be recognized as starting a declaration (e.g. in if (var x = ...) 2023-01-21 00:59:33 +01:00
Christoffer Lerno
e09628b664 Added easings. Move of math to own folder. 2023-01-19 22:49:30 +01:00
Christoffer Lerno
92507ee388 More work on TB 2023-01-19 20:32:19 +01:00
Christoffer Lerno
1ab304dc64 Fix of overalignment on deref. 2023-01-19 12:29:17 +01:00
Christoffer Lerno
e284d49dd9 Initial add of tilde backend. 2023-01-19 00:08:31 +01:00
Christoffer Lerno
6da6288ad8 Support roundevenf/roundeven 2023-01-14 15:24:49 +01:00
Dmitry Atamanov
925e4e6e46 Added native option to --x86vec help. 2023-01-14 15:09:22 +01:00
Christoffer Lerno
bd12ef0a53 Add version bump. 2023-01-13 01:10:02 +01:00
Christoffer Lerno
2123e81e8e Make more of the builtins do promotion from int to double for convenience. 2023-01-13 01:09:27 +01:00
Dmitry Atamanov
50b0958fb6 Add Loong arch deps for LLD >= 16 2023-01-12 18:43:05 +01:00
Christoffer Lerno
026861051a Make LLVM 15 compile in CI again. (#699)
* Fixes to LLVM 15 CI
2023-01-12 18:41:51 +01:00
Christoffer Lerno
dc16f65c8e Change printfln to printfn. Make LLVM 15 tests default. 2023-01-11 18:00:08 +01:00
Christoffer Lerno
3298ff2e15 Fix of typo. 2023-01-11 00:53:57 +01:00
Christoffer Lerno
aded1cb736 Use 15.0.6 on windows. Exclude 14 on linux. Update docker build. 2023-01-11 00:52:43 +01:00
Christoffer Lerno
da65de2d01 Add compare_exchange. Rudimentary threads (subject to change) 2023-01-10 20:46:39 +01:00
Christoffer Lerno
c9e40cfa37 Cast cleanup, removing structural casts. 2023-01-09 00:08:29 +01:00
Christoffer Lerno
43dc2d650c Use "String" consistently for "char[]" (#694)
Use "String" consistently for "char[]". Fix win32 return value.
2023-01-07 22:50:33 +01:00
Christoffer Lerno
5b2b4e900f Add download links. 2023-01-07 15:12:05 +01:00
Christoffer Lerno
6c9de52de7 Revert. 2023-01-07 15:02:52 +01:00
Christoffer Lerno
98f3decc1a Set release to real and not prerelease. 2023-01-07 14:59:57 +01:00
Christoffer Lerno
c485a89940 Updated README 2023-01-07 02:39:26 +01:00
Christoffer Lerno
73000680e5 Update readme 2023-01-07 02:18:03 +01:00
Christoffer Lerno
009bbeb48f Using C files now correctly places object files in build folder. 2023-01-07 01:38:44 +01:00
Christoffer Lerno
38be3d57dd Vendor fetch. 2023-01-06 16:49:45 +01:00
Christoffer Lerno
ad48770977 Updated errno. Socket constants and some functions added. Fix error when a macro returns a void! and that macro is in turn set to a return. Removed too permissive casts to subarrays. 2023-01-06 12:46:58 +01:00
Dmitry Atamanov
8390655d79 Fixes $$get_rounding_mode() for LLVM > 15. 2023-01-05 12:56:18 +01:00
Christoffer Lerno
4330740cf8 Support printing of object files. Issue #687 2023-01-04 23:16:47 +01:00
Christoffer Lerno
4a99190f96 C3L zip support. Version bump. 2023-01-03 22:40:35 +01:00
Christoffer Lerno
f8a505754d Added arcfour crypto. 2022-12-30 16:34:31 +01:00
Christoffer Lerno
23a78a9ae5 Updated nbodies. Fixed sum/product on floats. 2022-12-30 02:41:17 +01:00
Christoffer Lerno
bf222557fb Handle missing tests. 2022-12-29 21:13:58 +01:00
Christoffer Lerno
aa33536ab1 Remove test global when not in testing mode. 2022-12-29 20:40:23 +01:00
Christoffer Lerno
7b1fc87566 Bump to 0.4.0 2022-12-29 19:51:00 +01:00
Christoffer Lerno
a6f2eb22a9 Add "native" for AVX features. 2022-12-29 18:40:38 +01:00
Christoffer Lerno
ef7365224f Added $include. 2022-12-29 16:12:03 +01:00
Christoffer Lerno
f122d290f1 Add macos sdk / min version to pass to the linker. 2022-12-29 01:06:35 +01:00
Christoffer Lerno
90f5c24d4c Fix parsing @_foo 2022-12-28 16:36:41 +01:00
Christoffer Lerno
bcbcc3100f Fix of accidentally added typo. 2022-12-28 00:39:21 +01:00
Christoffer Lerno
f6e798c283 Friendlier int parsing and promotion. 2022-12-28 00:37:52 +01:00
Christoffer Lerno
f622b3bd4c Cleanup of naming change to optional. Fix of builtin optional handling. 2022-12-27 23:55:23 +01:00
Christoffer Lerno
fe54b84aed Version bump 2022-12-27 14:30:43 +01:00
Christoffer Lerno
f46697bc54 Updated casts (#684)
Cast code rework. Remove llvm_set_bool and friends.
2022-12-27 14:16:47 +01:00
Christoffer Lerno
e2b4a19c81 Add python overwrite. 2022-12-23 17:03:38 +01:00
Christoffer Lerno
9e77fe7787 Add python-tk 2022-12-23 16:56:26 +01:00
Christoffer Lerno
7223435df6 Update build for mac. 2022-12-23 14:20:09 +01:00
Christoffer Lerno
fe9ace713b Fix str::concat 2022-12-23 14:06:02 +01:00
Christoffer Lerno
03cdc8e3b1 Add "get_ref" to list. 2022-12-21 15:59:29 +01:00
Christoffer Lerno
352e09970c Math max/min now take multiple arguments. 2022-12-20 16:45:35 +01:00
Christoffer Lerno
e8a8ac8bc1 Fix bit or / xor / and on bool vectors. 2022-12-20 03:24:24 +01:00
Christoffer Lerno
334c004f1e Fix of atan2 2022-12-19 13:00:27 +01:00
Christoffer Lerno
aa534e2b3f Fix to vector comparisons. 2022-12-18 20:58:20 +01:00
Christoffer Lerno
2ee2bc3129 Fix bugs related to distinct types. 2022-12-18 00:21:02 +01:00
Christoffer Lerno
b88e5a8079 Add atan and atan2 2022-12-17 21:29:45 +01:00
Christoffer Lerno
c339261d1e Added vector dot functions. Comparison now yields bool vector. Cast between vector types. 2022-12-17 01:51:03 +01:00
Christoffer Lerno
8008fb2c18 Do not auto-deref subscripting. 2022-12-15 23:46:26 +01:00
Dmitry Atamanov
10219eccb2 Added compile-test command to help. 2022-12-14 23:04:40 +01:00
Christoffer Lerno
1a510207e8 Update string.c3 2022-12-14 23:03:45 +01:00
Nikos Plugachev
a21236d661 Improved string concat 2022-12-14 23:03:45 +01:00
Nikos Plugachev
a749a4d265 Added String.concat, List.clear 2022-12-14 23:03:45 +01:00
Christoffer Lerno
b5afa98507 Updated vector comparison behaviour and semantics. 2022-12-14 23:02:01 +01:00
Christoffer Lerno
abf0f64ac0 Share method extensions across modules by default. Fix bug in string split. 2022-12-14 13:18:01 +01:00
Christoffer Lerno
dcf0b4c580 Fix an issue with pointer debug info creation. Improve generated parameterized name. Version bump. 2022-12-13 19:20:14 +01:00
Christoffer Lerno
a9ed514fe5 Fix bug in defer from macros. Ensure debug location on panic functions. Add getcwd. 2022-12-13 07:50:41 +01:00
Dmitry Atamanov
8b0d409695 Add inlined memcpy and memset builtins and macros. (#668) 2022-12-12 11:45:01 +01:00
Christoffer Lerno
50d2a04c48 Default to threads = cpus(). Correct multithreaded benchmark with threads. 2022-12-11 23:35:02 +01:00
Christoffer Lerno
1864c69f31 Fixes to inc/dec vector element. Improvements to unit tests. Updated task threading. Single threaded by default due to poor LLVM perf. 2022-12-10 04:24:56 +01:00
Christoffer Lerno
af0174f360 Some work on io libs. 2022-12-09 08:45:02 +01:00
Christoffer Lerno
2a4d43d7c7 Fix issue when using ct types as body parameters. 2022-12-09 00:00:33 +01:00
Christoffer Lerno
13cb637cb4 Fix missing early resolution on demand of parameterized types. 2022-12-08 19:48:36 +01:00
Christoffer Lerno
de4bfe470e Attempt supporting setjmp on MSVC 2022-12-07 22:16:09 +01:00
Christoffer Lerno
eaaa5362a5 Output messages at compile time. 2022-12-07 18:48:55 +01:00
Christoffer Lerno
1ea5625183 CT variables now follow CT scopes. It's now allowed to mutate CT variables in deeper runtime scopes. 2022-12-07 16:32:45 +01:00
Christoffer Lerno
f7659776fc Fix problem when taking address of method. 2022-12-07 11:58:21 +01:00
Christoffer Lerno
ed99e09c4e Correctly detect & lowering in the case of &foo[1].b using operator overloading. 2022-12-07 10:53:26 +01:00
Christoffer Lerno
e9181a75e6 Improve errors when a variable fails to properly analyse. 2022-12-06 20:29:07 +01:00
Christoffer Lerno
963b8f28ef Fix of multiple returns in macros retaining ct values. Correctly require ; after endfor etc. 2022-12-06 20:09:40 +01:00
Christoffer Lerno
5721fcc224 Fix of string function. 2022-12-06 17:48:30 +01:00
Christoffer Lerno
287de8f499 Fix issue with aliasing @-macros. 2022-12-05 17:38:27 +01:00
Christoffer Lerno
44dfeb621d Fix body arguments: (@foo(;int x) and mismatch on canonical types) 2022-12-05 16:23:00 +01:00
Christoffer Lerno
eb87eb1987 Allow an expression list be an lvalue if the last value is an lvalue. Fix indexing from back for [] overloads. 2022-12-05 15:42:24 +01:00
Christoffer Lerno
c15fb7460c Add functions for splitting strings. 2022-12-04 23:01:53 +01:00
Christoffer Lerno
927ad2001f Incorrect check for sqrt. 2022-12-04 01:13:47 +01:00
Christoffer Lerno
7647378e7c Fix missing check when macro method incorrectly has a raw vararg argument. 2022-12-03 23:07:37 +01:00
Christoffer Lerno
10b0b5f9c7 Fix map.c3 2022-12-03 22:07:04 +01:00
Christoffer Lerno
299ec1814b Add native string -> int conversions. Fix to getline and add tgetline. 2022-12-01 16:13:52 +01:00
Christoffer Lerno
07700ed2c5 Add str::trim and String::tcopy_str. Version bump. 2022-12-01 13:19:08 +01:00
Christoffer Lerno
0ae586585a Fix .values .names .elements on "fault" types. Version bump. 2022-11-25 11:11:27 +01:00
Christoffer Lerno
285299dcd5 Some simple stdlib tests. 2022-11-20 18:16:21 +01:00
Dmitry Atamanov
2fefed5bda Create simple main.c3 by init command 2022-11-19 14:49:50 +01:00
Christoffer Lerno
da1a45f718 Remove iptrdiff and uptrdiff. Bump version to 0.3.100 2022-11-17 23:44:54 +01:00
Dmitry Atamanov
bbef94b0fd Fix project creation 2022-11-16 21:05:26 +01:00
Christoffer Lerno
c093f16fd0 Fix missing check on foreach indexing. 2022-11-14 15:05:19 +01:00
Dmitry Atamanov
5ff726d8d1 Added $$get_rounding_mode and $$set_rounding_mode builtins. (#655) 2022-11-14 13:07:32 +01:00
Christoffer Lerno
49eacb8824 More support for test. Panic function update. 2022-11-14 11:48:12 +01:00
Christoffer Lerno
450113d161 Fix of mac CI build. 2022-11-14 09:58:07 +01:00
Christoffer Lerno
998c56533b Update test case. 2022-11-14 09:48:34 +01:00
Christoffer Lerno
73619817ba Update panic function and early work on tests. Optimize ABI lowering. 2022-11-03 19:38:34 +01:00
Christoffer Lerno
70f6ad1b27 Added "values" module. 2022-10-31 14:40:58 +01:00
Dmitry Atamanov
e070bf22ee Added pow macros to math module. 2022-10-31 14:09:38 +01:00
Dmitry Atamanov
b086c85d9f Add LLVM 15 to CI 2022-10-31 10:54:32 +01:00
Dmitry Atamanov
66d87b25a3 Added more math macros: cosec, cosech, cosh, cotan, cotanh, hypot, sec, sech, sinh, sqr, sqrt, tan, tanh. 2022-10-30 20:31:32 +01:00
Dmitry Atamanov
d9e81c6035 Fix vector slices assignment 2022-10-30 19:54:14 +01:00
Christoffer Lerno
98ae13c03d Add exact and overflow builtins. 2022-10-28 01:14:10 +02:00
Christoffer Lerno
d9ea953b5c Fix $c >>= 2 2022-10-25 10:50:56 +02:00
Christoffer Lerno
4cdfac092b Add missing \n to program finish on run. 2022-10-24 17:15:51 +02:00
Christoffer Lerno
1ef43cbc13 Remove "directives" folder. 2022-10-24 17:13:24 +02:00
Christoffer Lerno
093bc2089a Fix to project creation. 2022-10-24 17:11:44 +02:00
Christoffer Lerno
9b5e2b8578 Remove fix for LLVM 16 2022-10-24 16:10:26 +02:00
Christoffer Lerno
2d377ada45 Fix in parameter check. 2022-10-23 23:41:03 +02:00
Christoffer Lerno
02374c6aab Generic modules also accepts integers and booleans. 2022-10-23 19:16:38 +02:00
Dmitry Atamanov
155c8862c9 Remove duplicates in help 2022-10-22 16:10:15 +02:00
Christoffer Lerno
f73b507ccb Use project.json instead of .c3p. List project properties. Check project property usage. Change names: lib-dir -> dependency-search-paths, libs -> dependencies, linker-libs -> linked-libraries, linkerlib-dir -> linker-search-paths, csources -> c-sources. Several settings have an xxxx-add, xxxx-override pair for the target, gaining fine grained control over the override. 2022-10-22 15:32:01 +02:00
Christoffer Lerno
26e4662c3b Project updates. 2022-10-22 01:04:43 +02:00
Christoffer Lerno
ede224662c Fix of broken bitstruct init #641. Version bump. 2022-10-21 11:45:34 +02:00
Christoffer Lerno
bd0e8f1ef1 Allow imports anywhere in the module outside of ct statements. 2022-10-20 23:54:36 +02:00
Christoffer Lerno
e6a5f98606 float.min will now give -float.max 2022-10-20 21:54:40 +02:00
Christoffer Lerno
e15dbd4907 Improve conversion functions. 2022-10-20 20:32:33 +02:00
Christoffer Lerno
ae7aa65f35 Add conversion functions for i128 2022-10-20 19:35:29 +02:00
Christoffer Lerno
d13b7ac96a Enable support for int128 across all platforms. 2022-10-20 18:03:02 +02:00
Christoffer Lerno
03fe2b575d Chain optionals with optionals using ??. Version bump. 2022-10-20 12:11:24 +02:00
Christoffer Lerno
f86ef8a743 Remove tscoped. Replace str_index_of with "starts_with". Updated copy_zstring/copy. Fixed utf conversion functions. Initial work on "Path". Lexer fix on \\. ABI fix using distinct types. (bool)"" now works correctly. Bug in $if with switches/loops as the first statement fixed. Version bump. 2022-10-20 10:55:14 +02:00
Christoffer Lerno
7d58ce0dcb Take code from stdin. Version bump. 2022-10-18 19:45:56 +02:00
Christoffer Lerno
f8f249ee2c Update compilation linux. 2022-10-18 16:39:33 +02:00
Christoffer Lerno
0adb15139f Fix bug in implicit fmul. 2022-10-18 14:02:20 +02:00
Christoffer Lerno
76ee384a4c Fix of ternary / elvis where legs are bool or optional. 2022-10-18 14:02:20 +02:00
Christoffer Lerno
b1ed066e55 Fix coerce store / load to properly rely on alloc size like Clang does it. Version bump. 2022-10-15 22:12:52 +02:00
Christoffer Lerno
160659c4e3 Optimize sret / byval. Fixes to types and failables. 2022-10-15 02:45:19 +02:00
Dmitry Atamanov
dfe3128b16 Add --benchmarking and --testing switches. (#591)
Added `--benchmarking` and `--testing` switches. Update env.c3
2022-10-13 14:25:45 +02:00
Christoffer Lerno
8f269a4f13 Improve text. 2022-10-13 14:05:34 +02:00
Christoffer Lerno
1ae251478b Fix templates for init. 2022-10-13 14:03:30 +02:00
Christoffer Lerno
effec3a1f6 Fix of bug where missing return wasn't detected. 2022-10-13 13:34:54 +02:00
Christoffer Lerno
4d08fee30e Add bolt-15 to linux cmake. 2022-10-13 11:25:24 +02:00
Christoffer Lerno
5d9a7ab0a6 Extend "var" to allow type inference on variables. 2022-10-13 09:37:52 +02:00
Christoffer Lerno
5e184f04e7 List now has correct alignment and takes memory allocator initializer. Bugfix of aligned allocations. 2022-10-12 22:35:36 +02:00
Christoffer Lerno
b2b1a3489a Add hashmap function. 2022-10-12 15:21:40 +02:00
Christoffer Lerno
fc41179636 Minor indention change. 2022-10-12 11:53:49 +02:00
Dmitry Atamanov
959c418e8b Add a lot of math functions and macros. (#626) 2022-10-12 11:52:19 +02:00
Christoffer Lerno
9424bba49f Version bump. More generous wildcard length matching and conversions. 2022-10-12 11:46:58 +02:00
Christoffer Lerno
314369d069 Version bump, add -O0+ and -O1+, let stdlib override nostdlib 2022-10-10 22:54:22 +02:00
Christoffer Lerno
ec3d77f4bd Fix for nolibc & linux. 2022-10-10 16:04:48 +02:00
Christoffer Lerno
ab78663f3c Add usz and isz. 2022-10-10 15:44:03 +02:00
Christoffer Lerno
348495b4c8 Added an initial nolibc 2022-10-10 15:44:03 +02:00
Christoffer Lerno
6523982f14 Clarify source and license on msvc script. 2022-10-10 15:02:36 +02:00
Christoffer Lerno
df8595cd64 Fix of bug where using && on a member ref would result in an assert. 2022-10-10 14:51:52 +02:00
Christoffer Lerno
febd11fa95 Fix of crashing bug with member types. Version bump. 2022-10-10 11:55:46 +02:00
Christoffer Lerno
feba7b8ed2 Rename muladd and fmuladd and insert it at codegen. 2022-10-10 11:14:57 +02:00
Dmitry Atamanov
3624c2a72c Added $$muladd built-in. 2022-10-10 11:14:57 +02:00
Christoffer Lerno
d5f965e137 Fix test. 2022-10-08 22:10:56 +02:00
Christoffer Lerno
70a429f832 Fix error using compile time var before assignment. 2022-10-08 21:46:56 +02:00
Dmitry Atamanov
7fa129932d Added $$memmove built-in. 2022-10-08 21:41:11 +02:00
Christoffer Lerno
73ac0b8ea0 Fixed incorrect test. 2022-10-08 14:55:36 +02:00
Christoffer Lerno
407ed5a63d Fix vector init and float promotion. Fix of 1.0f. 2022-10-08 14:30:17 +02:00
Christoffer Lerno
fa064276bc Disallow $offsetof / $alignof on types. Version bump. 2022-10-08 12:41:40 +02:00
Christoffer Lerno
c84f82559c Disallow typeof of member type. 2022-10-07 22:28:30 +02:00
Christoffer Lerno
bb20a38cdb Updated membersof. "Type.kind" renamed "Type.kindof" 2022-10-07 14:41:10 +02:00
Christoffer Lerno
f010f6a926 Add qnameof, nameof, extnameof to types. 2022-10-06 19:31:52 +02:00
Christoffer Lerno
ec1a5d97c9 Support for memberof/returns/params. 2022-10-06 17:18:16 +02:00
Christoffer Lerno
870e716f59 Missing support for bitstruct ".inner" 2022-10-06 15:23:16 +02:00
Christoffer Lerno
d33ff212a7 Fix double @@ in attribute listing. 2022-10-06 14:55:49 +02:00
Christoffer Lerno
ee533c5500 Update listable type properties. 2022-10-06 14:52:44 +02:00
Christoffer Lerno
a281dbe812 Added $$TIME, $$DATE, $$FUNCTION. Builtin defines listed. 2022-10-06 11:57:59 +02:00
Dmitry Atamanov
1d39fc475f Added max and min macros to builtin_comparison module. 2022-10-05 23:13:38 +02:00
Dmitry Atamanov
f5a1894876 Add std::ascii module. (#611)
Added `std::ascii` module.
2022-10-05 23:11:41 +02:00
Christoffer Lerno
db06f99445 "[]=" now works as overload. Improved eval resolution. Added $$FUNCPTR 2022-10-05 22:58:28 +02:00
Christoffer Lerno
05d4ec55f6 Fix of $$FUNC in methods. 2022-10-05 10:17:11 +02:00
Christoffer Lerno
dcfcf460a5 Support LLVM 15 MacOS. 2022-10-05 10:17:11 +02:00
Christoffer Lerno
eb86b83bd7 Prevent ct_eval from returning an rvalue. 2022-10-04 22:41:07 +02:00
Christoffer Lerno
3d844b8722 Fix of printf registration. 2022-10-03 16:01:27 +02:00
Christoffer Lerno
6bd72c2ec4 Improve printf. 2022-10-03 15:42:55 +02:00
Christoffer Lerno
4783946476 Version bump. Updated printf using "Formatter". Fix to initializers. 2022-10-03 13:06:57 +02:00
Christoffer Lerno
9f7ed00f04 Fixes to function pointer conversion. 2022-10-02 00:22:21 +02:00
Christoffer Lerno
f05ffc84d8 Minor fixes. 2022-10-01 22:14:07 +02:00
Christoffer Lerno
55f7046da8 Fix of static init with debug turned on. 2022-10-01 17:53:08 +02:00
Christoffer Lerno
c5b9b6c761 Single module compilation. 2022-09-30 13:56:08 +02:00
Christoffer Lerno
5029dc703e Updated error message. 2022-09-30 08:44:28 +02:00
Christoffer Lerno
cbb731b42b Fix alignment on temp allocators. 2022-09-29 22:18:12 +02:00
Christoffer Lerno
258a6ba97a Bug fixes, addition of hash map implementation. (#605)
* Simple hash map. Fix of bug preventing cast of typeid. Allow declarations in global "$checks". Fix to non-constant default args. Correctly duplicate macro contracts. Allow typeid to add methods. Fix printing of subarrays. Fix bug when printing a function with a module. Fix bug with initializer and creating local variables. Add the compile-only option to the help.
2022-09-29 20:19:31 +02:00
Christoffer Lerno
e1b5b0b60c Implement static finalize / initialize. Version bump. 2022-09-28 17:18:20 +02:00
Christoffer Lerno
58647043f4 Fix bug with $$clz, version update. 2022-09-26 11:48:12 +02:00
Christoffer Lerno
379f9e60bf More checks for $switch 2022-09-25 17:17:56 +02:00
Christoffer Lerno
a4d4c27ca6 General reorganization, set any empty expression statement to nop. Version bump. 2022-09-24 21:22:29 +02:00
Christoffer Lerno
81bea9bad6 Updated untyped lists. 2022-09-24 14:35:58 +02:00
Christoffer Lerno
52f3948026 Cleanup of untyped lists. 2022-09-23 10:47:56 +02:00
Christoffer Lerno
46c182f3d1 Fix of bug in nextcase semantic checking. Cleanup and refactoring. 2022-09-22 16:14:39 +02:00
Christoffer Lerno
cc71b96c38 Rearranging code somewhat. 2022-09-22 00:05:22 +02:00
Christoffer Lerno
ad18d9ba48 Add .hash to integer types. Fixup of make and tmake. Updated map.c3 to become a working example. Fix bug in subarray -> pointer conversion. Search extension methods in std::core. Fix slice <-> slice copy. 2022-09-21 17:26:36 +02:00
Christoffer Lerno
4d5821408d Add @castable and @convertible builtin macros, removes the need $castable and $convertible 2022-09-20 22:18:00 +02:00
Christoffer Lerno
48ee567f81 Add types::is_indexable. 2022-09-20 18:45:43 +02:00
Christoffer Lerno
321c713687 Fix of $checks. 2022-09-20 18:42:39 +02:00
Christoffer Lerno
be5c82cfa6 Add "$checks". Fix where $y++ could appear inside a runtime scope. 2022-09-20 18:32:36 +02:00
Christoffer Lerno
4fa4b2a631 Refactored builtins. Added reduce operations and powi. Version bump. 2022-09-19 14:58:48 +02:00
Christoffer Lerno
9b14340a57 Added prefetch builtin. 2022-09-18 23:27:42 +02:00
Christoffer Lerno
e7fad16d0f Added stringify tests and $typefrom. Bump to 0.3.50. 2022-09-18 20:52:25 +02:00
Christoffer Lerno
62e3b8063e Fix stringify span. 2022-09-18 02:25:22 +02:00
Christoffer Lerno
c7f0f58e82 Fix stringify bug. 2022-09-18 01:26:06 +02:00
Christoffer Lerno
35549c21bc A brainf**k example. 2022-09-18 01:20:17 +02:00
Christoffer Lerno
6220bda4a3 Use @operator([]), @operator(&[]) and @operator([]=) instead of names. 2022-09-17 23:33:27 +02:00
Christoffer Lerno
81a2474f75 Add methods to basic types. Remove "intvec" and "floatvec" overloads. Update bits and math to use the new functionality. Introduces [<*>] and [<>] types. Bump version to 0.3.47 2022-09-17 22:00:28 +02:00
Christoffer Lerno
28f2247602 Fix of mistake in commit. 2022-09-17 13:17:24 +02:00
Jean-Baptiste Perrier
1541354587 Extending JSON utils (#574)
* Adding json free, to_str, removing parser dependency from new_object.
* Adding support for BOOL in json_to_str
* Replace NULL check by assert
* Bugfixing json parser
Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2022-09-17 12:20:38 +02:00
Christoffer Lerno
a66c0942f8 Adding saturated builtins. Remove LLVM 12 support. Remove old llvm optimizer use. 2022-09-17 12:03:08 +02:00
Dmitry Atamanov
fd9d300b06 Added Int128BE, UInt128BE, Int128LE and UInt128LE. 2022-09-17 01:53:21 +02:00
Dmitry Atamanov
b60862ec7a Added cpuid, rdtsc and rdtscp. 2022-09-16 23:12:03 +02:00
Christoffer Lerno
c8166f6fdb Adding $$reverse. Bump to 0.3.45 2022-09-16 14:53:17 +02:00
Christoffer Lerno
4d27150952 Adding $$shufflevector. Bump to 0.3.44 2022-09-16 13:39:03 +02:00
Christoffer Lerno
d4aec525f5 Fixes to asm. Added additional x86 instructions. 2022-09-16 00:41:19 +02:00
Christoffer Lerno
ba9b203c52 Cleanup. 2022-09-15 09:50:46 +02:00
Dmitry Atamanov
7a797d0f28 foreach(_r) for vectors 2022-09-15 00:16:29 +02:00
Christoffer Lerno
755d6f93b7 Extends = to work with macros and allows => alternative. Disallow statements. 2022-09-14 23:30:39 +02:00
Jean-Baptiste Perrier
7c2555bd3b Remove comments 2022-09-14 23:16:23 +02:00
Jean-Baptiste Perrier
469ec54282 Add single function declaration 2022-09-14 23:16:23 +02:00
Christoffer Lerno
1af41ee394 Fix min/max. Also fix of printf 2022-09-14 12:22:57 +02:00
Christoffer Lerno
fa51402a16 Added x64 instructions. 2022-09-13 22:40:49 +02:00
Christoffer Lerno
0805750f52 Add hlt instruction, 2022-09-13 19:18:42 +02:00
Christoffer Lerno
03c627f646 Fix asm string bug. 2022-09-13 19:15:17 +02:00
Christoffer Lerno
3a09f71830 Added initial intvec/floatvec operator 2022-09-11 22:52:01 +02:00
Jean-Baptiste Perrier
3d110850df More asm instructions (#563)
* Add mul + sub asm instructions

* Fix clobbers
2022-09-09 23:42:27 +02:00
Christoffer Lerno
b16e0b377e In/out checks. 2022-09-09 23:37:03 +02:00
Christoffer Lerno
fa89ea7b79 Add checks on enum conversion in safe mode. Bump to 0.3.38. 2022-09-09 17:18:27 +02:00
Christoffer Lerno
a0a2e27127 $vasplat() implemented. $vacount removed (). Improved dynscope implementation. Version bump to 0.3.37. 2022-09-09 12:19:47 +02:00
Christoffer Lerno
e31967cc65 Fix missing const folding when evaluation nameof. 2022-09-09 11:10:26 +02:00
Christoffer Lerno
58f8e00376 Incorrectly disallowed "inner" on enum. Bump to 0.3.36 2022-09-09 10:59:00 +02:00
Christoffer Lerno
581e2adbf0 Fix bug doing $foreach over a const list. 2022-09-09 10:17:35 +02:00
Christoffer Lerno
e4e1541cd7 Add macro method test update LLVM version for MSVC. 2022-09-08 15:47:52 +02:00
Christoffer Lerno
8eefce235f Rename $vaarg functions. Bump version to 0.3.35. 2022-09-07 12:32:43 +02:00
Christoffer Lerno
7cb03de16b Refactored asm code and added some initial aarch64 asm. 2022-09-06 22:53:46 +02:00
Christoffer Lerno
4fbdb79b65 Add some fixes to asm + test case. 2022-09-05 22:19:45 +02:00
Dmitry Atamanov
97de44b424 Added is_vector macro. 2022-09-05 15:12:23 +02:00
Dmitry Atamanov
72c7da404e Added $$LLVM_VERSION define. (#552) 2022-09-05 14:29:12 +02:00
Christoffer Lerno
5adf09dd53 Fix issues with INLINE and MSVC. 2022-09-05 14:28:05 +02:00
Christoffer Lerno
bdc7833b83 Inline asm first checkin. 2022-09-05 13:57:15 +02:00
Christoffer Lerno
1a9a71f4c4 Inline asm first checkin. 2022-09-05 13:29:03 +02:00
Christoffer Lerno
757ca3716e Remove unused tokens. 2022-08-30 16:25:50 +02:00
Dmitry Atamanov
7a07b8ff13 Add new built-ins (#551)
* Added new built-ins: $$copysign, $$exp2, $$floor, $$llrint, $$llround, $$lrint, $$lround, $$nearbyint, $$rint, $$round, $$roundeven
* Disable $$llrint, $$llround, $$lrint and $$lround
2022-08-30 09:50:28 +02:00
Dmitry Atamanov
bbc3792e7c Sort some things alphabetically 2022-08-29 15:17:41 +02:00
Christoffer Lerno
a7110b52eb Fix missing script on macos. 2022-08-29 14:46:06 +02:00
Dmitry Atamanov
5d15ec23bb Rename $$fabs to $$abs 2022-08-29 12:51:02 +02:00
Christoffer Lerno
fffb8a1d0c Update CMake for MSVC. Fix packaging for macos. 2022-08-29 12:47:53 +02:00
Christoffer Lerno
54d6b1a4ec Fix $$min/$$max for vector. 2022-08-29 10:23:16 +02:00
Christoffer Lerno
bb9c8fb93e Fix of macro/fn use of operator overloading for foreach. 2022-08-28 23:33:39 +02:00
Christoffer Lerno
b863ae2ec5 Fix of binary literals. Bump version to 0.3.32. Renamed rotate_right and rotate_left to rotl and rotr which are the commonly used names. 2022-08-28 22:40:40 +02:00
Dmitry Atamanov
27a0e12979 Add rotate_left and rotate_right macros 2022-08-28 21:20:54 +02:00
Christoffer Lerno
dabe5769dd More programs for test. 2022-08-28 20:35:25 +02:00
Christoffer Lerno
e82a7e7918 Allow $$max and $$min to also work on ints. 2022-08-28 19:14:26 +02:00
Christoffer Lerno
73fd9371eb Exclude LLVM 12 from new optimizer. 2022-08-28 16:51:33 +02:00
Christoffer Lerno
4a296032ab Add fshl fshr + new optimizer. 2022-08-28 16:22:10 +02:00
Christoffer Lerno
5386b6fe50 Improve error message #543 2022-08-28 01:00:11 +02:00
Christoffer Lerno
c9ae0779e7 Locals lookup is no longer hacked to be used for member search. Fixed bug where you could break out of an expression block using a labelled break. Bump to 0.3.29 2022-08-28 01:00:11 +02:00
Dmitry Atamanov
e33c81f85b Increase verbosity of asm output 2022-08-27 18:34:55 +02:00
Christoffer Lerno
a4e603442b Version bump 2022-08-27 02:25:38 +02:00
Christoffer Lerno
6b1370ba76 Support varargs for macros. Bugfix member access. Support macro varargs. 2022-08-27 02:25:08 +02:00
Christoffer Lerno
52bcf4654a Reduce max params to 127. Hint when using more. Allow function pointers to have default and named parameters. Bump version to 0.3.27 2022-08-25 17:55:25 +02:00
Christoffer Lerno
57e1084734 Improvements to parameter parsing. Update "splat" name. Allow more orderings of named parameters. Allow named args after variant. 2022-08-24 20:06:08 +02:00
Christoffer Lerno
821b6a7ffa Updated pointer offset handling. Version bump. This fixes variant[2] { &&1, &&2 } init. 2022-08-20 11:43:00 +02:00
Christoffer Lerno
1858600449 Fixes issue where functions could not be found if local and imported names clashed. 2022-08-18 20:03:59 +02:00
Christoffer Lerno
656faa55bf Add a concat/tconcat function. 2022-08-18 16:57:22 +02:00
Christoffer Lerno
b2a975945a Fix debug info subarray/variant. 2022-08-18 15:04:12 +02:00
Christoffer Lerno
f6ff3a0756 Update with LLVM 16 compatibility. 2022-08-17 22:29:23 +02:00
Christoffer Lerno
d95e0b4733 General refactoring and fix of compile time folding of pointers. 2022-08-17 21:54:47 +02:00
Christoffer Lerno
fd642e333b Fix tests. 2022-08-17 17:32:28 +02:00
Christoffer Lerno
377f3eeb2e Allow const access into arrays and structs. 2022-08-17 17:04:27 +02:00
Christoffer Lerno
08c9400e02 Fix bug with private methods in generic modules. 2022-08-17 11:45:58 +02:00
Christoffer Lerno
afe466d7b3 Added sinf/cosf. Minor refactoring to matrix lib. Added complex type. 2022-08-17 01:07:17 +02:00
Christoffer Lerno
ae51214c47 Added clz, ctz, popcount, bswap, bitreverse. 2022-08-16 21:18:14 +02:00
Dmitry Atamanov
15586b3076 Add foreach_r tests 2022-08-16 16:51:06 +02:00
Christoffer Lerno
9a3e7fd34b Add the ability to build static libraries. Bump version to 0.3.18 2022-08-16 09:11:02 +02:00
Christoffer Lerno
9a69a13b04 Fixes to or/and with rethrow. 2022-08-15 14:34:18 +02:00
Christoffer Lerno
0a17857737 Update of the msvc_sdk script. 2022-08-15 10:52:33 +02:00
Christoffer Lerno
31d151aa30 Added MSVC download script to prebuilts. Implicitly use msvc_sdk if available. Bump version to 0.3.17 2022-08-14 23:42:18 +02:00
Christoffer Lerno
63d9853bd3 Fixes to bitstruct. 2022-08-14 18:25:47 +02:00
kstrb
b72718ba2a Update README.md 2022-08-14 17:46:55 +02:00
kstrb
213745fccd Add batch file 2022-08-14 17:46:55 +02:00
Christoffer Lerno
1a98fa0efe Update linker args for Linux. Fix problem with int return mains. Bump to 0.3.16 2022-08-13 21:49:47 +02:00
Christoffer Lerno
fc943a98ef Foreach_r, bump to 0.3.15 2022-08-13 03:04:09 +02:00
Christoffer Lerno
7805fb8d1c More codegen cleanup. 2022-08-13 00:19:09 +02:00
Christoffer Lerno
d93c7090f6 Adding wrapper functions to a lot of the LLVM calls. 2022-08-12 23:20:35 +02:00
Christoffer Lerno
44df6eb75b Cleanup. 2022-08-12 18:13:24 +02:00
Christoffer Lerno
5cacc41925 Add global builder / updated codegen. 2022-08-12 01:50:48 +02:00
Christoffer Lerno
571728a42e Bump version to 0.3.14. Better non-lvalue errors. Dereferenced optional pointers are not lvalues. 2022-08-12 00:33:09 +02:00
Christoffer Lerno
b1c78edc5e Bump version to 0.3.13. Set codeview flag for Windows debug. 2022-08-11 22:31:26 +02:00
Christoffer Lerno
852c08e5fe Better handling of foreach lowering. 2022-08-11 21:25:11 +02:00
kstrb
7092a9f825 Use debug libraries when doing a debug build on Windows 2022-08-11 21:15:56 +02:00
Christoffer Lerno
f513b6237f Improved error messages for foo(void), foo(int!) declarations. 2022-08-11 12:59:55 +02:00
Dmitry Atamanov
48fb342834 Fix typo in asm-out description [skip ci] 2022-08-11 11:35:03 +02:00
Dmitry Atamanov
24e216e034 Use LLVM's tools in CI 2022-08-11 11:30:15 +02:00
Christoffer Lerno
d4c1a08fcd Rename "PREV" to "NOTE". Bump of version. 2022-08-11 11:17:48 +02:00
Andersama
ac7823cabb Improve navigation of messages 2022-08-11 11:08:55 +02:00
Christoffer Lerno
af9b99bd5a Fix macro calls to make CT parameters modifiable. 2022-08-11 11:04:44 +02:00
Christoffer Lerno
90dfc24491 General cleanup and rearranging code. 2022-08-11 02:20:22 +02:00
Christoffer Lerno
aa337049ea Fix -l and -L build options. 2022-08-10 22:21:29 +02:00
Christoffer Lerno
92b4eeaa35 Update codegen for failable folding. Fixes chained init, e.g. "int! a = b = TestErr.FOO!" 2022-08-10 21:32:29 +02:00
Christoffer Lerno
5ac35a49fd Minor cleanup. Fix of do { } while(1) loops. Bump to 0.3.9 2022-08-10 16:37:27 +02:00
Christoffer Lerno
7a835418a7 Documented the --nostdlib option. 2022-08-10 14:11:23 +02:00
Christoffer Lerno
1bf3bb51bf Add asm output option using --emit-asm. 2022-08-10 11:41:40 +02:00
Christoffer Lerno
fd3591c8b7 Temporarily disable LLVM 16 2022-08-10 11:17:13 +02:00
Christoffer Lerno
cfcf5cbc16 Bump to 0.3.7. Fixes issue with bitoperations on booleans. 2022-08-10 11:01:14 +02:00
Christoffer Lerno
9473adbc35 Added $$sysclock() builtin. 2022-08-10 01:24:16 +02:00
Christoffer Lerno
3eee9ddc06 Change exe name to follow the module of the main. Detect missing / multiple main functions. 2022-08-10 01:14:07 +02:00
Christoffer Lerno
ebd7611164 Allow compile time pointer manipulation. Ternary bug fixes. 2022-08-09 12:05:49 +02:00
kstrb
fba4df8f84 Automated releases should be "Pre-release" 2022-08-08 19:58:38 +02:00
Christoffer Lerno
28c7db7414 Updated the readme with precompiled binaries. 2022-08-08 16:54:27 +02:00
Christoffer Lerno
167bb85a7e Fix uploading linux debug + macos 2022-08-08 15:47:39 +02:00
Christoffer Lerno
29918fd126 Add debug version for windows. 2022-08-08 15:19:54 +02:00
Christoffer Lerno
f881d195b0 Update the release CI. 2022-08-08 15:18:39 +02:00
Christoffer Lerno
ccda6dba40 Change artifact uploads. 2022-08-08 14:42:00 +02:00
kstrb
31d0bb9684 add lib/ to release artifacts, release job should only run on master 2022-08-07 19:03:27 +02:00
kstrb
1c46f7050c Update CI to upload release artefacts 2022-08-07 17:24:46 +02:00
Christoffer Lerno
878bbed929 Fix bug using $switch. Added mem::equals. Fix to dynamic arena allocator. 2022-08-05 19:00:32 +02:00
Christoffer Lerno
f93c20ca34 $$syscall cleanup. 2022-08-05 12:24:33 +02:00
Christoffer Lerno
9866c7f9e9 Added $$syscall builtin. 2022-08-05 12:01:50 +02:00
Christoffer Lerno
00b880e35e Nicer plus_minus 2022-08-05 01:01:28 +02:00
Christoffer Lerno
398e19d727 Corrected default alignment on temp alloc. Added str_index_of. Added simple getline. Added a simple calculator. Allow [1..] to create a zero length slice. Added some initial macro contracts. Fix accessing enum functions. Support for @checked. Bump to 0.3.4 2022-08-05 00:42:52 +02:00
Christoffer Lerno
046469843c @pool on temp usage in File.open(). Add LLVM 15. 2022-08-04 12:56:47 +02:00
Christoffer Lerno
3461f08a53 Consume a byte to make the temp allocator correctly reset large allocs. 2022-08-04 02:46:15 +02:00
Christoffer Lerno
db5816edd6 Fix to temp allocator reset. 2022-08-04 02:34:51 +02:00
Christoffer Lerno
6d2ab0c985 Fix ordering of @builtin. malloc <-> alloc, malloc, calloc, realloc, free builtins. 2022-08-04 01:49:36 +02:00
Christoffer Lerno
f966250185 Allow .names to be used on enum type directly at compile time. 2022-08-03 21:23:49 +02:00
Christoffer Lerno
cc8884d3d1 Allocators separated into aligned and non aligned operations. 2022-08-03 20:53:37 +02:00
Christoffer Lerno
550bca79e9 Updated memory allocator. Fix in printf printing pointers. Added aligned_alloc to libc module. Renamed MemoryArena -> ArenaAllocator. New temp allocator. @pool(), @scoped, @tscoped macros. Bump to 0.3.2. 2022-08-02 14:42:46 +02:00
Christoffer Lerno
272f134e78 Update for LLVM 16 2022-08-01 17:23:23 +02:00
Christoffer Lerno
a109efd14c Fix bug with ternary failables. 2022-08-01 11:22:58 +02:00
Christoffer Lerno
b48ae91cbb Better error on Foo::BAR 2022-07-31 10:48:01 +02:00
Christoffer Lerno
14df5c2ed9 0.3.0 mark. 2022-07-30 03:24:36 +02:00
Christoffer Lerno
04695489b4 Bump version to 0.2.27 2022-07-30 02:55:32 +02:00
Christoffer Lerno
331f9b23f8 Attributes correctly checks for recursive definitions now. Added a max bitstruct size. 2022-07-30 02:55:32 +02:00
Christoffer Lerno
9886d381c0 Update allocator and resolution. 2022-07-30 02:55:32 +02:00
Christoffer Lerno
12c17b62cf Allow any expression as default expression. 2022-07-30 02:55:32 +02:00
Christoffer Lerno
2698ba1a94 Fix of expr location in args. 2022-07-27 21:21:36 +02:00
Christoffer Lerno
6f5f5feb97 Fixing distinct, typedef and bitstruct copying. Fix where global constants did not need to be constant. Bump to 0.2.26 2022-07-27 18:12:27 +02:00
Christoffer Lerno
64d883cb99 Fix bug on runtime "nameof" with optional values. Fixed issues with integer to enum casts. 0.2.25. Added enum_by_name. 2022-07-27 00:46:02 +02:00
Christoffer Lerno
1adc8b8264 Moved bitcast to builtin module. 2022-07-26 23:42:03 +02:00
Christoffer Lerno
c02ce5ce2a Better error on all upper parameter names. "Upper case" -> "uppercase" 2022-07-26 22:01:23 +02:00
Christoffer Lerno
e36e4b60e4 Fprintf changes. 2022-07-26 16:12:22 +02:00
Christoffer Lerno
1d808be4b9 Fix stdout defaults. 2022-07-26 15:07:37 +02:00
Christoffer Lerno
7065c28a08 Some fixes to string. Added fprintf and String.printf. Updated boolerr example. 2022-07-26 13:44:08 +02:00
Christoffer Lerno
da4df9d626 Less libc in example. 2022-07-26 02:21:49 +02:00
Christoffer Lerno
a7e4dda360 Added examples. 2022-07-26 02:10:18 +02:00
Christoffer Lerno
4471ccff13 String works with printf. Example cleanup. 2022-07-26 01:15:36 +02:00
Christoffer Lerno
cdff5c3e26 Dev (#500)
Single code path for kind/inner/len/sizeof on type and typeid. Fix of #493. Bump to 0.2.24. Remove ´func´ deprecated keyword. Unify builtin access. Enum and fault name reflection.
2022-07-26 00:56:59 +02:00
Christoffer Lerno
cc1bc58ed0 Allow using enums for indexing. 2022-07-24 15:39:11 +02:00
Christoffer Lerno
812bd8b3d0 Added $converable / $castable. Simplify and corrected if try/catch parsing. Fix bug with { [A] = 1 } 2022-07-24 15:10:04 +02:00
Christoffer Lerno
7e0a29ef40 Fix constant typeid comparisons. Allow methods to use & and * and constants. Improved error messages. Updated String type with generic append. 2022-07-24 15:10:04 +02:00
Christoffer Lerno
c1de3f059e Updated error messages. 2022-07-23 21:08:17 +02:00
Christoffer Lerno
62c1d2ddb5 Slicing a distinct type now returns the distinct type. 2022-07-23 20:46:21 +02:00
Christoffer Lerno
b313bec69d Fix to "typeid.kind". Conversion unsigned int -> enum fixed. @autoimport -> @builtin. Comparison macros. Bump to 0.2.21 2022-07-22 17:09:49 +02:00
Zhang Li Hui
036859c0c8 Added installation info for arch linux 2022-07-22 01:20:30 +02:00
Christoffer Lerno
56a6e0b112 Fix bug preventing implicit & on optionals. Updated priority queue to return optionals. Changed the list API to have snake case on methods. Bump to 0.2.20 2022-07-21 22:21:50 +02:00
Christoffer Lerno
18f7f35e80 More tests. 2022-07-21 21:20:03 +02:00
Christoffer Lerno
1d572f3e7c Allow distinct printthrough in printf. Added tests. 2022-07-21 18:13:11 +02:00
Christoffer Lerno
002ee006c1 More efficient int[] a = {}; Disallow zero length arrays. Bump to 0.2.19 (#489)
More efficient int[] a = {}; Disallow zero length arrays. Bump to 0.2.19. Improve error on mistyped types.
2022-07-20 22:25:03 +02:00
Christoffer Lerno
8afbccd3fe Fix bug in extension methods for generic types and typedefs. 2022-07-20 13:24:55 +02:00
David Kopec
6576725ed8 Add Binary-Heap Based Priority Queue to Standard Library (#481)
Priorityqueue
2022-07-20 12:22:43 +02:00
Christoffer Lerno
d3a053e049 Updated mangling and bump to 0.2.18 2022-07-20 12:22:03 +02:00
Christoffer Lerno
4afec24434 More advanced introspection. 2022-07-20 12:22:03 +02:00
Christoffer Lerno
29edd6e54e Fix to extension methods in other modules. Version 0.2.17 2022-07-19 14:17:00 +02:00
Christoffer Lerno
547d30eb1e Disallow complist as a regular macro parameter. 2022-07-18 01:22:29 +02:00
Christoffer Lerno
6cf3c9f46b Fix in nested block handling. @maydiscard and @nodiscard annotations. If the common type of int[x] and int[y] is int[] 2022-07-17 19:48:24 +02:00
Christoffer Lerno
4beb7eff8f Add support for : slices. Version bumped to 0.2.16 2022-07-17 19:48:24 +02:00
Christoffer Lerno
48a31cfa48 Fix of error where {| |} with value return could have no final return. 2022-07-17 19:48:24 +02:00
Christoffer Lerno
cd1138447e Update math.matrix.c3
Change module to std::math.
2022-07-16 15:33:09 +02:00
PixelRifts
c29ad77cdb Matrix Math Library 2022-07-16 15:33:09 +02:00
Christoffer Lerno
1c15ebe6d2 Fix of bug using ".len" 2022-07-16 12:09:12 +02:00
Christoffer Lerno
a68efec5e8 Added swap and list swap to stdlib. 2022-07-14 14:58:11 +02:00
Christoffer Lerno
3f6b0646b3 An initial printf. Added type.inner and type.len. Bump to 0.2.15 2022-07-14 02:43:53 +02:00
Christoffer Lerno
28a8e17690 Vararg abi fix. Version bumped to 0.2.14 2022-07-13 14:19:09 +02:00
Christoffer Lerno
2a7d46844a Fix "libs" in project creation. 2022-07-13 09:50:51 +02:00
Christoffer Lerno
92542ac1f9 Fix bug with bit struct initialization and zeros. Allow float mod. Add float.max/min. Version bumped to 0.2.13 2022-07-13 00:13:34 +02:00
Christoffer Lerno
59b41f8deb Reduce size of memory pages used. 2022-07-12 13:09:45 +02:00
Christoffer Lerno
abfccb5576 Fix issues with union of const. Bump version 0.2.12 2022-07-11 17:58:11 +02:00
Christoffer Lerno
ea5d7cd2e7 Fixes initialization of anonymous structs. Bump version 0.2.11 2022-07-10 23:29:05 +02:00
Christoffer Lerno
ca21b1daac Allow [in] to be used on subarray types. Added more to "conv" module. 2022-07-09 19:32:39 +02:00
Christoffer Lerno
9fdd66af42 Fix of distinct void* and null. Version bumped to 0.2.10 2022-07-08 17:16:44 +02:00
Christoffer Lerno
d403912ec7 Add linker and linked dir arguments to build files. 2022-07-08 14:52:58 +02:00
Christoffer Lerno
05f222616e Fix of default project creation target format. 2022-07-07 18:17:41 +02:00
Christoffer Lerno
253dbf3603 Remove std::mem 2022-07-07 18:14:36 +02:00
Christoffer Lerno
cfbfc29e84 Fix of $sizeof(Type) => Type.sizeof 2022-07-07 15:51:40 +02:00
Christoffer Lerno
bb020a1752 Add a windows install instruction. 2022-07-07 15:02:48 +02:00
Christoffer Lerno
c8a614e43f LLVM 15 compatibility fixes (#465)
More variant code. Fixes to typekind. Fixes to macro with failable returns. Remove use of LLVMConstInsert etc. Version 0.2.8
2022-07-06 16:41:52 +02:00
Christoffer Lerno
bb28f6e61c Fix stack setting after error return. Some fixes to examples. 2022-07-02 10:54:40 +02:00
Christoffer Lerno
b1d83e2ccd Auto-import std::core. Fix module assignment of declarations. Introspection improvements. Deref null error panics in safe mode. Support for LLVM 15 2022-06-29 21:57:35 +02:00
Christoffer Lerno
df41caabdd Global @align fixed #446. 2022-06-04 23:19:27 +02:00
Christoffer Lerno
2f5d51c92c Attempt to add more native file handling for MSVC (#459)
* Fix clean and update MSVC function calls.
2022-06-04 21:32:35 +02:00
Christoffer Lerno
224390ce5a Make builtins loaded by default. 2022-06-04 01:41:23 +02:00
Christoffer Lerno
09d50ebf6c New import rules. 2022-06-04 01:41:23 +02:00
Christoffer Lerno
2d608a4d51 Change TB dir and do debug printout by default. 2022-05-22 14:54:18 +02:00
Christoffer Lerno
d511f150a7 Add lld linking for FreeBSD. 2022-05-22 14:54:18 +02:00
Christoffer Lerno
f4dc4f64f2 Change TB dir and do debug printout by default. 2022-05-21 20:09:09 +02:00
Christoffer Lerno
6035cb4600 Update TB 2022-05-21 19:57:11 +02:00
Christoffer Lerno
3d1eaad6b9 Update file_utils.c 2022-05-18 22:30:07 +02:00
Christoffer Lerno
ca2bb505b6 Merge pull request #453 from matkuki/patch-1
Update CMakeLists.txt
2022-05-16 15:07:07 +02:00
matkuki
8b4b4273cc Update CMakeLists.txt
Fixes compilation error on Visual Studio 2022
2022-05-16 14:10:28 +02:00
Christoffer Lerno
7c91c56f3d Updated cmake with latest win-llvm (#451)
Updated cmake with latest win-llvm
2022-05-15 19:41:12 +02:00
Christoffer Lerno
5edafc5b2f Test for #149 ct lists. 2022-05-12 19:13:49 +02:00
Christoffer Lerno
dbb0dc302d Add instructions for building on Unix variants. 2022-05-12 09:53:05 +02:00
Christoffer Lerno
e09e5c06d3 User defined attributes. 2022-05-11 20:55:09 +02:00
Christoffer Lerno
b0c55ff777 Support enum associated values. 2022-05-11 20:55:09 +02:00
Christoffer Lerno
60832019bd Merge pull request #445 from c3lang/dev
Updated enums, macro usage / naming
2022-05-09 14:06:34 +02:00
Christoffer Lerno
42b5445225 Fixes enum set with new ordinal based enums. 2022-05-09 12:25:33 +02:00
Christoffer Lerno
9691d50a6f @ is now part of the name of an attribute or a macro. Macros without '@' must be function-like. 2022-05-08 22:16:33 +02:00
Christoffer Lerno
29a9769651 Ordinal based enums. 2022-05-08 21:39:00 +02:00
Christoffer Lerno
15e1db78a7 Remove unused code. 2022-05-02 16:35:14 +02:00
Christoffer Lerno
2f23d56a12 0.2.1 Update
0.2.1 SysV ABI fix for passing certain things by struct. Fix implicit…
2022-04-27 17:06:26 +02:00
Christoffer Lerno
22ee082d00 0.2.1 SysV ABI fix for passing certain things by struct. Fix implicitly converting to float in the case of myfloat *= -1. Prefer inferred constant over global. Allow locals to shadow global variables. 2022-04-27 16:43:25 +02:00
Christoffer Lerno
212bc7d9af Merge pull request #437 from c3lang/dev
0.2.0. Build system improvements. Target changes x64-windows -> windo…
2022-04-26 13:40:43 +02:00
Christoffer Lerno
890c4bc435 0.2.0. Build system improvements. Target changes x64-windows -> windows-x64, x64-darwin -> macos-x64. Improved mac support. LLD linking for Mac, Windows, Linux. Cross linking for Mac, Windows. Clean up string use. Fix of debug handling of multiple compilation units per module. MSVC CI 2022-04-26 13:20:33 +02:00
Christoffer Lerno
7df7dd2933 Merge pull request #436 from data-man/ci_testing
Use Ninja in CI
2022-04-17 11:09:00 +02:00
data-man
ada8652209 Use Ninja in CI 2022-04-16 15:25:48 +05:00
Christoffer Lerno
bf8288ed1c Merge pull request #435 from data-man/fix_typo
Fix typo in sema_expr.c [skip ci]
2022-04-16 11:19:30 +02:00
data-man
9b6e4f9d11 Fix typo in sema_expr.c [skip ci] 2022-04-16 13:12:14 +05:00
Christoffer Lerno
ecdcd8f959 0.1.1: Add -L and -l parameters, also incomplete support of .c3i files. 2022-04-15 18:52:36 +02:00
Dmitry Atamanov
828724f593 Add more vector tests (#430)
* Add more vector tests
* Added .ll output.
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2022-04-14 09:55:11 +02:00
data-man
f6eb20f725 Fix resource leak in check_file function 2022-04-13 10:17:14 +02:00
data-man
ade4065480 Add LLVM 13 to MacOS CI 2022-04-10 09:50:53 +02:00
Christoffer Lerno
b19cef4bc1 Removed some accidental commits. 2022-04-09 20:13:41 +02:00
Dmitry Atamanov
151cbfd706 Semi-implemented foreach for vectors (#423)
Implement foreach for vectors
2022-04-09 20:09:35 +02:00
Christoffer Lerno
b99db4be24 Allocators. Rename of "optenum" to fault. Memcpy and memset added. Cleanup of declaration use. 2022-04-09 20:07:59 +02:00
Christoffer Lerno
8743223dd6 Allocators. Rename of "optenum" to fault. Memcpy and memset added. Cleanup of declaration use. 2022-04-09 20:07:59 +02:00
Christoffer Lerno
2e2a1ca21a Fix defer with static variables. 2022-03-31 10:11:37 +02:00
Christoffer Lerno
34bd5fa6da Fix of macro defer with bodies. 2022-03-28 10:19:24 +02:00
Christoffer Lerno
131bf5ed34 Cleanup and change of varcast. 2022-03-27 13:22:04 +02:00
Christoffer Lerno
78134316b7 Addin @varcast 2022-03-27 13:09:06 +02:00
Christoffer Lerno
b31629c5e8 Removal of "or_else jump". Fixes to defer & macros/blocks, optimized failable return. @noscope macros removed. Disallow meaningless defer.
Correctly show error on return or rethrow inside of a defer. Fix copying an access expression. Removing scoped expr.
2022-03-26 20:16:36 +01:00
data-man
bbfc2fc8ab Update checkout action to v3 2022-03-23 21:15:52 +01:00
gdm85
1940f6967c Add support for podman
Use a tmpfs when running
Prefix podman images with 'localhost/'
2022-03-23 18:44:46 +01:00
data-man
adf2b2e818 Update build-with-docker.sh [skip ci] 2022-03-23 16:48:46 +01:00
Christoffer Lerno
6a9b14d107 Added regression test for errors. 2022-03-21 18:18:14 +01:00
Christoffer Lerno
8b8a8d81db Fix of issue with "a ?? false" 2022-03-21 13:22:59 +01:00
data-man
d1fadf6428 Don't replace digits to underscore in created files 2022-03-20 19:41:30 +01:00
Christoffer Lerno
05d8e6c7b8 Fix debug panic for vector shift and division. 2022-03-20 19:39:31 +01:00
Christoffer Lerno
6789fab93c Panic function that may be redefined. Trap and stacktrace builtins. Bug using builtin fixed. Fixes to using $$LINE and friends. Produces a stacktrace on error. 2022-03-18 12:43:17 +01:00
Christoffer Lerno
3490814d73 Simplify scope flags. 2022-03-13 22:49:10 +01:00
Christoffer Lerno
3799dbb082 Use declid instead of decl* in the decltable. 2022-03-12 14:45:54 +01:00
Christoffer Lerno
60d7c8aa14 Add timings. 2022-03-12 02:35:07 +01:00
Christoffer Lerno
55598b2de8 Remove multi-line strings. 2022-03-12 00:40:06 +01:00
Christoffer Lerno
4fe2a70ee1 Only add dll visibility on external functions for win32. 2022-03-11 13:27:01 +01:00
Christoffer Lerno
ab56b2d047 Only add dll visibility on non-local functions for win32. 2022-03-11 13:05:46 +01:00
Christoffer Lerno
1061b4e1dd Make doc an AST type. 2022-03-11 12:24:07 +01:00
Christoffer Lerno
e02362de0c Resolve contracts as they are encountered. 2022-03-11 11:17:39 +01:00
Christoffer Lerno
c414459075 Fix bug with missing debug location. 2022-03-10 15:55:44 +01:00
Christoffer Lerno
8f6fb8b7d7 Move body param as decl. Fix lexer bug with docs. 2022-03-10 14:54:52 +01:00
Christoffer Lerno
fc296ea579 Extracted macro declaration body to separately allocated field. 2022-03-10 14:54:52 +01:00
Christoffer Lerno
4258fe4d01 Simple check for [in] and [out] parameters. 2022-03-10 12:46:45 +01:00
Christoffer Lerno
0565e87e5e Support '@pure' and parse the optreturn tag. 2022-03-10 09:46:30 +01:00
Christoffer Lerno
6a48f81485 Smaller Decl and Expr. Compressed TypeInfo. 2022-03-09 19:10:11 +01:00
Christoffer Lerno
322d714305 Dev (#404)
Remove 'errtype' name and reduce Expr / TypeInfo memory footprint.
2022-03-08 23:38:27 +01:00
Christoffer Lerno
069a2d40cb Changing how defer works. Remove of undef. Simplify ensure. 2022-03-08 14:15:31 +01:00
Christoffer Lerno
9b0dfe8ba3 Cleanup: remove CT_ELIF ast node. 2022-03-04 17:08:47 +01:00
Christoffer Lerno
2802b2b96d Ensure, $eval and $evaltype, @unreachable() replaces $unreachable. <Type>.sizeof 2022-03-04 15:49:17 +01:00
Christoffer Lerno
4f4bc80953 Symtab has a new hash map type. New, small, sourcespan, external symbols simplified. Updated docs. 2022-03-01 10:32:52 +01:00
data-man
e45853c0cb Update README [skip ci] 2022-03-01 09:55:34 +01:00
Christoffer Lerno
f54a93890a Remove the EXPR_CONST_IDENTIFIER 2022-02-18 13:56:13 +01:00
Christoffer Lerno
6b4e4f6114 Update formatting to consistently use tabs. 2022-02-18 12:56:17 +01:00
Christoffer Lerno
bf5683b41c General cleanup, more native platform detection. 2022-02-18 09:54:12 +01:00
Christoffer Lerno
73351e0aa9 Updated version for implicit imports. 2022-02-16 20:42:39 +01:00
Christoffer Lerno
df3e51c17e Implicit imports. 2022-02-16 20:41:54 +01:00
data-man
d11d80e896 Update --list-precedence table 2022-02-15 18:55:02 +01:00
Christoffer Lerno
7226bff6ea Use JSON, unifying settings. 2022-02-15 18:53:54 +01:00
Christoffer Lerno
fd82f9685f Make target from string case dependent. 2022-02-15 18:53:54 +01:00
Christoffer Lerno
c70d6716da Added second missing unreachable. 2022-02-08 11:54:19 +01:00
Christoffer Lerno
e57c2710d9 Added missing unreachable. 2022-02-08 11:48:43 +01:00
Christoffer Lerno
d42193dbd6 Fixes the alloca alignment on x64 for alignment > 16. Improved indexing into ConstInitializer adding const indexing into a const array during compile time. Add a temporary enum values result. 2022-02-08 11:41:34 +01:00
Dmitry Atamanov
9f9f24c3cc CI config refactoring (#389) 2022-02-08 11:05:38 +01:00
data-man
f3e326fcd9 CI config refactoring 2022-02-05 22:26:53 +01:00
Christoffer Lerno
ee32a5fd47 Added $stringify. Remove of editor_plugin folder. 2022-02-02 15:22:28 +01:00
Christoffer Lerno
06917f2e65 Added "guess a number". Fix CT enum / int comparison. Fix some array pointer decay scenarios. Infer type of expression blocks. Correctly merge anyerr. 2022-02-01 22:46:32 +01:00
Christoffer Lerno
ba66aaaf12 Foreach overload is now done using attributes (@operator(elementat), @operator(elementref) and @operator(len)) rather than reserved functions. 2022-01-29 16:45:03 +01:00
data-man
9639ad6a73 Move fail-fast above 2022-01-29 10:38:19 +01:00
Christoffer Lerno
cf56825d26 Fixed bug when using indexing on a generic type. Made array::list::List work with [] and foreach. 2022-01-26 21:27:13 +01:00
Christoffer Lerno
e5bcb74822 Added levenshtein.c3 to working files. Fixes to reverse indexing. Added min/max functions. Tentatively removed "opaque" 2022-01-26 17:40:01 +01:00
Christoffer Lerno
8eb295bf5b Cleanup. 2022-01-25 17:01:49 +01:00
Christoffer Lerno
bd6c3db413 Disable MSYS tests and add MacOS tests. 2022-01-25 16:58:08 +01:00
Christoffer Lerno
21fd2c4485 Update to LLVM fix. 2022-01-25 15:14:22 +01:00
Christoffer Lerno
bc8fbdb54a Fix LLVM 14 compatibility 2022-01-25 15:11:02 +01:00
Christoffer Lerno
8922399c36 Add $for 2022-01-24 22:52:01 +01:00
Christoffer Lerno
1e7ad2e241 Add $foreach 2022-01-24 18:53:44 +01:00
Christoffer Lerno
4f212f7634 Remove a gazillion imports. 2022-01-24 14:53:38 +01:00
Christoffer Lerno
2c5ae858b8 Re-add () to ct constructs. 2022-01-24 00:52:43 +01:00
Christoffer Lerno
ef95c1a630 Fix $switch. And make top level $switch work. 2022-01-24 00:01:54 +01:00
Christoffer Lerno
b2be8349ed Add typeless varargs. 2022-01-23 19:43:57 +01:00
Christoffer Lerno
01e9086666 Add precedence listing. 2022-01-22 17:31:33 +01:00
Christoffer Lerno
66b763193d Use O1 on CXX release to avoid issues. 2022-01-21 02:22:58 +01:00
Christoffer Lerno
36e40e59cb Added enumset to stdlib. 2022-01-20 20:00:22 +01:00
Christoffer Lerno
dce33ba5b5 Fixing #380 where const aggregates were copied by value. 2022-01-20 16:52:28 +01:00
Christoffer Lerno
ec291d4a9d This allows testing optnum, optenum, errnum and resnum as alternatives to errtype. 2022-01-19 23:21:17 +01:00
Christoffer Lerno
e6ad9c324d This fixes the bug with "case 3 .. 1" #357 2022-01-19 23:11:03 +01:00
Christoffer Lerno
3450016978 This adds the ability to list keywords, operators etc. implementing #350. 2022-01-19 13:11:12 +01:00
Christoffer Lerno
827499ca80 Update version to reflect recent changes. 2022-01-18 23:32:59 +01:00
Christoffer Lerno
556be2ff7f "@autoimport" attribute #366 2022-01-18 23:16:48 +01:00
Christoffer Lerno
8adb0faa06 Multi module import support. #354 2022-01-18 22:53:31 +01:00
Christoffer Lerno
b6450861d2 Cast no longer needs (type)(expr) format. Ordinary C (type)expr now works. 2022-01-18 22:32:12 +01:00
Christoffer Lerno
1e56948a22 Prepare removing "func" 2022-01-18 14:15:44 +01:00
Christoffer Lerno
e4e8abbc6c Dev (#376)
Errno updates with errno for linux/win/macos. Updated $$ syntax to also match compiler constants.
2022-01-18 01:31:14 +01:00
Christoffer Lerno
3f60443d66 Update errno. 2022-01-17 22:28:06 +01:00
Christoffer Lerno
f53dd95aa7 Update errno. 2022-01-17 22:24:52 +01:00
Christoffer Lerno
1743036104 Added some File functionality. 2022-01-17 22:16:29 +01:00
Christoffer Lerno
a2fa61f58b Updated licensing information. 2022-01-17 17:18:22 +01:00
Christoffer Lerno
4059d22315 Remove unnecessary load in LLVM statement lowering. Added more tests from Clang. 2022-01-17 17:08:28 +01:00
Christoffer Lerno
05f0059b1b Fix float conversion. Strings as array initializers work much better. 2022-01-17 16:06:17 +01:00
Christoffer Lerno
bc3b58b3e3 Optimized ternary where both branches are empty. 2022-01-17 12:01:27 +01:00
Christoffer Lerno
99ea0afcbf Optimize multiple int casts on ptrtoint, into a single cast. Fixed incorrect widening cast from signed -> unsigned. 2022-01-17 00:45:26 +01:00
Christoffer Lerno
23461b179f More splitting of the AbiArgInfo to make it easier to find duplicate behaviour. 2022-01-16 16:21:55 +01:00
Christoffer Lerno
d916f111b3 Fixes a | b when a | b are boolean. Fixes to RISCV and x64 ABI implementations. 2022-01-16 01:13:43 +01:00
Christoffer Lerno
0f4a43717e Fixes to the aarch64 ABI. 2022-01-15 01:37:43 +01:00
Christoffer Lerno
c5a862f4d1 Fix to the x64 ABI. More tests. 2022-01-14 21:30:29 +01:00
Christoffer Lerno
fb22a36aa9 Fix of #374. Even more "or" LLVM 14 fixes. 2022-01-13 21:51:19 +01:00
Christoffer Lerno
889bc27800 Skip "or with zero" 2022-01-13 21:38:24 +01:00
Christoffer Lerno
eed5b7db54 Correctly call x64 varargs. 2022-01-13 21:22:50 +01:00
Christoffer Lerno
5683fe3f8c Add parsing for escaping and remove "fault" token name. Allow excluding stdlib. Fixes to the x64 abi: no narrowing done for i32 results, assuming too many registers, more tests. 2022-01-12 23:56:59 +01:00
data-man
684ad9e663 Disable mimalloc by the option 2022-01-11 17:32:06 +01:00
Christoffer Lerno
a6b29bccb7 Somewhat smaller SymEntry. Different mixing function in stables. 2022-01-10 22:05:56 +01:00
Christoffer Lerno
71623a1874 Minor bugfix. 2022-01-10 18:59:29 +01:00
Christoffer Lerno
16179d2513 Make it easier to match LLVM 14 output. 2022-01-10 14:50:57 +01:00
Dmitry Atamanov
fd8cd6a8e2 Use several LLVM versions in CI (#369)
* Use several LLVM versions in CI
2022-01-10 14:40:55 +01:00
Christoffer Lerno
180b17b213 Updated with latest TB. 2022-01-10 14:38:31 +01:00
Christoffer Lerno
4764981708 Update cols for warning and prev as well. 2022-01-10 14:08:23 +01:00
Disheng Su
3cf057ff95 Print file's full path and col number in compiler's error message 2022-01-10 14:06:29 +01:00
Christoffer Lerno
ea3b50d039 Codegen with better annotations on globals. 2022-01-10 13:58:15 +01:00
Christoffer Lerno
041c096801 Refactored some of the llvm lowering. Work on TB. Disabled mimalloc due to issues with LLVM. 2022-01-09 21:23:37 +01:00
Dmitry Atamanov
d942dfbc99 Support build with mimalloc (#348)
* Support build with mimalloc

* Use latest stable tag
2022-01-07 19:18:25 +01:00
Christoffer Lerno
a176ae353b Refactored function pointer. 2022-01-07 19:13:07 +01:00
Christoffer Lerno
8a840746f6 Compound statements are linked lists. Remove unused "global_symbols" "public_symbols" "qualified_symbols". Reduce max hash map load. 2022-01-05 19:40:44 +01:00
Christoffer Lerno
869aa7ed76 Do not store direct parameters that are only read. 2022-01-04 23:16:32 +01:00
Christoffer Lerno
1d5ff71b21 Rearchitecture contexts. Fixing macro resulution, hash arguments and defers. 2022-01-04 20:45:58 +01:00
Christoffer Lerno
439ae3e468 Satisfy GCC checks. 2022-01-03 12:35:42 +01:00
Christoffer Lerno
8f2ae41ea5 Removed allocation of AbiType. Separate string arena. 2022-01-03 12:19:46 +01:00
Christoffer Lerno
fd1353f0f1 Removed scratch hash table use for parameter checks. Faster symtab setup. Scrubbed the lexer somewhat without changing behaviour. 2022-01-01 22:08:07 +01:00
Christoffer Lerno
bbeed12600 Removing unused hash table functions. Removal of macro DECL_NEW. Smaller initial vector size. Remove unnecessary memclear. 2021-12-30 16:41:00 +01:00
Christoffer Lerno
0a7200ac24 Symtab default size change, some minor cleanup. 2021-12-29 23:36:15 +01:00
Christoffer Lerno
f509c85514 Update hello_world 2021-12-29 14:38:47 +01:00
Christoffer Lerno
06d3bc8915 Release symtab after use. 2021-12-29 13:34:36 +01:00
Christoffer Lerno
a0be188902 Lexing updated with simpler code. Note: may have bugs. 2021-12-27 15:45:54 +01:00
Christoffer Lerno
0a9a014e4a Builtins now properly work with $defined. A builtin may not be an rvalue. 2021-12-25 21:08:08 +01:00
Christoffer Lerno
85ee021585 Adding missing symtab size option. 2021-12-24 23:15:50 +01:00
Christoffer Lerno
f6de1f7b74 Somewhat clearer code around lexing. 2021-12-24 01:09:21 +01:00
Christoffer Lerno
cf61f427d4 Add noalias to sret. 2021-12-23 13:26:55 +01:00
Christoffer Lerno
eb54e46569 Add necessary header for cmalloc 2021-12-22 13:09:01 +01:00
Christoffer Lerno
cf0a04977a Prevent circular initializers. 2021-12-22 01:03:15 +01:00
thsm
1a9b8095b6 added {| |}, etc to vscode syntax highlighter 2021-12-22 00:41:36 +01:00
pyxel
012f258fa2 add vscode syntax plugin 2021-12-21 22:52:53 +01:00
Christoffer Lerno
eefe782dd6 Fix constant evaluation of | & ^ >> << 2021-12-20 21:35:36 +01:00
Christoffer Lerno
6f77fdf800 Update assert for WASM part 3 2021-12-20 02:43:36 +01:00
Christoffer Lerno
8f6dd64483 Update assert for WASM part 2 2021-12-20 02:35:01 +01:00
Christoffer Lerno
04c3efc3c3 Update assert for WASM 2021-12-20 00:32:27 +01:00
Christoffer Lerno
4575e3dd8d Fix fseek/ftell to work correctly. 2021-12-20 00:23:21 +01:00
Christoffer Lerno
b5b625e54a Make sure normal flags aren't ignored for release. 2021-12-20 00:05:51 +01:00
Christoffer Lerno
52cc4f8ba5 Move compiler plain malloc calls to function to make them easy to track. 2021-12-19 23:11:32 +01:00
Christoffer Lerno
4e559e886c Add some simple string functions. And fix bug relating to typedefs. 2021-12-19 21:04:43 +01:00
Dmitry Atamanov
802398dd44 Show option like clang (#355) 2021-12-19 17:23:42 +01:00
Christoffer Lerno
7c76fd02c6 Fix test data 2021-12-19 16:33:07 +01:00
Christoffer Lerno
dee0199f10 Add compiler options to simplify external tool usage. 2021-12-19 16:25:48 +01:00
Christoffer Lerno
5cbc619f13 .obj / .exe default suffix now depends on target, not compiler platform. 2021-12-19 13:11:43 +01:00
pyxel
01902b523a Use supplied output name if available 2021-12-19 12:21:32 +01:00
pyxel
157d1a959b Add output name to build options 2021-12-19 12:21:32 +01:00
Christoffer Lerno
4b3232ead6 Some initial wasm support. 2021-12-19 12:18:43 +01:00
Christoffer Lerno
cc8d266827 Temp fix of CI 2021-12-18 22:21:54 +01:00
Christoffer Lerno
6d6d410d13 Update nano type/keywords. 2021-12-18 17:19:09 +01:00
Hanna
6b34a8c82e add kakoune editor resource 2021-12-18 17:00:30 +01:00
data-man
c39c7c3147 Update actions/checkout to v2 2021-12-17 20:43:00 +01:00
data-man
d360e97439 CMakeLists.txt refactoring 2021-12-17 16:08:35 +01:00
Christoffer Lerno
d305f9da49 Remove manual addition of ConstGEP2 etc 2021-12-17 16:00:18 +01:00
Christoffer Lerno
572aafe8b9 Remove use of LLVMBuildCall 2021-12-17 15:45:09 +01:00
Christoffer Lerno
c52629d60f Main arguments are now slices if desired. 2021-12-17 01:28:57 +01:00
Christoffer Lerno
497eef5328 Removed code relating to empty structs. 2021-12-14 21:37:25 +01:00
Christoffer Lerno
680b077eb1 Flexible array member added, zero sized structs removed. 2021-12-14 19:00:33 +01:00
Christoffer Lerno
5ddbf50e83 Add unwrapping to variable for variant in switch. 2021-12-12 15:15:38 +01:00
thsm
908ac220c6 add cflags 2021-12-10 16:12:54 +01:00
Christoffer Lerno
958db2f594 Fix issue when compiling without cc set. 2021-12-09 23:54:57 +01:00
Christoffer Lerno
8aa00b015b Add support from compiling c from c3c. 2021-12-09 23:38:57 +01:00
Christoffer Lerno
379a66a14b Add switch(variant) 2021-12-09 22:52:48 +01:00
Christoffer Lerno
0887d1e7dc Allow /* without termination. 2021-12-09 10:32:30 +01:00
Christoffer Lerno
4a6f587c9f Incorrectly would do constant indexing into global when index was not constant. 2021-12-09 10:26:46 +01:00
Christoffer Lerno
e54679c01e Remove string literals. 2021-12-09 02:15:05 +01:00
Christoffer Lerno
4bc47a195b Introduce array pointer decay. 2021-12-09 00:49:50 +01:00
Christoffer Lerno
b003b05d5d Support variant.ptr and subarray.ptr 2021-12-08 16:14:29 +01:00
Christoffer Lerno
b066c25432 Remove reminder logs unless --debug-log on. Fix UB in test. 2021-12-07 22:20:35 +01:00
Christoffer Lerno
bbda3a679f Fixed issue where &foo[1] was not considered a constant when foo is a global. 2021-12-06 23:13:48 +01:00
Christoffer Lerno
49efa4fafc Missing \n in output. 2021-12-06 09:19:37 +01:00
Christoffer Lerno
4153cbe16d Change semantics of widening. 2021-12-05 23:41:45 +01:00
Christoffer Lerno
d6d4c0a912 Fix of init error. 2021-12-05 22:50:27 +01:00
Christoffer Lerno
06124ddb9f Support variant type. Add fault alias to anyerr. Fix missing .len on string literals. 2021-12-05 22:34:21 +01:00
Christoffer Lerno
e1fc028694 Remove eager readahead. 2021-12-04 01:20:27 +01:00
Christoffer Lerno
a9c2e59f60 Fix incorrect check in macro arguments. 2021-12-04 01:08:16 +01:00
Christoffer Lerno
eb81c00ada Fix failed parsing of ct_assert 2021-12-04 01:00:33 +01:00
Christoffer Lerno
26325f0fd2 Incorrect size of binop_preq_req array. 2021-12-04 00:30:47 +01:00
Christoffer Lerno
910b2179f9 Updates to base64 parsing. 2021-12-04 00:25:55 +01:00
Christoffer Lerno
e20e6071c8 Fix hex sequence escape. 2021-12-03 17:29:25 +01:00
Christoffer Lerno
cae1933f0f Early escape in reading invalid escape sequence. 2021-12-03 17:06:52 +01:00
Christoffer Lerno
bc2d789c2e Filter \r before lexing. 2021-12-03 16:43:37 +01:00
Christoffer Lerno
2c802878bb Fix overread on base64. 2021-12-03 16:00:20 +01:00
Christoffer Lerno
c4595d3024 Fix overread on failed string escape. 2021-12-03 15:55:22 +01:00
Christoffer Lerno
20ea6421cc Fix displaying error on first column. 2021-12-03 13:49:28 +01:00
Christoffer Lerno
f3c3636ac7 Fix bug in parsing exponent. 2021-12-03 13:07:05 +01:00
Christoffer Lerno
5a467f11ca Fix bug in parsing of binary, oct and hex 2021-12-03 11:41:44 +01:00
Christoffer Lerno
eef5e1b498 Fix detection of EOF and \n in string. 2021-12-03 00:48:12 +01:00
Christoffer Lerno
60b17004a4 Backtrack on finding ` with "next" 2021-12-03 00:31:46 +01:00
Christoffer Lerno
6ff05bae6d Backtrack on finding eof with "next" 2021-12-03 00:02:47 +01:00
Christoffer Lerno
5d30189626 Move float parsing to parser. 2021-12-02 23:42:52 +01:00
Christoffer Lerno
1bb9c2d249 Allow the compiler to be called multiple times (while leaking) 2021-12-02 22:50:38 +01:00
Christoffer Lerno
e31d189837 Fix of issue of with generic typedefs. 2021-12-01 16:24:59 +01:00
Christoffer Lerno
aa7da00323 Support for ranged case. 2021-11-30 15:35:56 +01:00
Theo
74fa81336f Smarter windows library path detection (#339)
add support for visual studio 2022 preview, switch to using smarter windows library path detection
2021-11-30 09:44:51 +01:00
Christoffer Lerno
3242bcabc0 Do-while uses for codegen and somewhat smaller llvm codegen 2021-11-28 01:35:09 +01:00
Christoffer Lerno
164a1ef59d Initial foreach implementation based on index 2021-11-27 00:57:27 +01:00
Christoffer Lerno
17a03bc104 Added rudimentary operator overload. 2021-11-27 00:57:27 +01:00
Christoffer Lerno
8d4d3b4d5b Ensure value semantics. 2021-11-22 22:11:03 +01:00
Christoffer Lerno
9a7b9bb7a4 Remove methods from "distinct" 2021-11-22 21:13:08 +01:00
Christoffer Lerno
1ba03f75c2 Fix debug parameter definition. Array foreach GEP. 2021-11-22 21:03:00 +01:00
Christoffer Lerno
7595f2e17b Fix of incorrect visibility. Use of LLVMInstructionRemoveFromParent. 2021-11-22 14:14:07 +01:00
C34A
90898d6ed6 add support for visual studio 2022 preview 2021-11-22 08:00:53 +01:00
Christoffer Lerno
aafa0d08a2 Removing -Wconversions 2021-11-22 00:07:06 +01:00
Christoffer Lerno
3c9761c946 Explicit insert 2021-11-22 00:04:01 +01:00
Christoffer Lerno
05c9b7cb33 More explicit conversion and width changes to some field. 2021-11-21 23:37:54 +01:00
Christoffer Lerno
bb39cf20c0 Removal of #pragma mark, explicit conversions. 2021-11-21 21:43:06 +01:00
Christoffer Lerno
dad8cd24b1 No implicit narrowing. 2021-11-21 21:19:12 +01:00
Christoffer Lerno
39b1b59011 Removal of #pragma mark for better compiler compatibility 2021-11-21 21:15:05 +01:00
Christoffer Lerno
685688f884 Change arena macro. 2021-11-21 21:11:19 +01:00
Christoffer Lerno
a1b69a3f50 Casting between vector and array. Check f16 overflow. 2021-11-20 03:44:50 +01:00
Christoffer Lerno
f1f268df4a Fix of test. 2021-11-19 15:39:02 +01:00
Christoffer Lerno
6febd8a143 Optimized assign of failable. 2021-11-19 13:29:43 +01:00
Christoffer Lerno
8cc8c321a2 Optimized rethrow expr. Fix to "Foo! x = {}" which would break. Remove unnecessary zeroing failables. Variables that are undefined do not have failable status zeroed. 2021-11-19 13:06:43 +01:00
Christoffer Lerno
0358724451 Minor updates to TB integration. 2021-11-19 10:15:08 +01:00
Christoffer Lerno
86072ae21f Start integration of tiny backend. 2021-11-19 09:52:14 +01:00
Christoffer Lerno
0af448ee71 Fix case of very long literal int type values. Fix to bigint compile time truncation. Check decl enum implicit overflow. 2021-11-19 09:11:58 +01:00
Christoffer Lerno
a4a6ea16ce Use element initialization for <= 16 bytes structs and arrays. 2021-11-18 23:23:55 +01:00
Christoffer Lerno
4d4bbbdebc Parses attribute defines. Removal of incremental array parsing. Labels in macros should now work correctly. Volatile and attribute are no longer keywords. Checked a few todos. On test failure, return -1 2021-11-18 20:36:45 +01:00
Christoffer Lerno
bfde58b9a5 Optimized macro codegen on simpler code. 2021-11-18 09:32:10 +01:00
Christoffer Lerno
7b04e7cf85 Added experimental "scoping" and fixed (simple) debug symbols 2021-11-17 23:56:09 +01:00
Christoffer Lerno
b7fa3549a3 Fixed method extern declaration for LLVM. 2021-11-17 11:46:50 +01:00
Christoffer Lerno
974cd0acc5 Rollback global. 2021-11-17 10:37:23 +01:00
Christoffer Lerno
b52b42d4da Complete transition to fn. Introduce global/threadlocal 2021-11-16 17:46:44 +01:00
Christoffer Lerno
e2621617f1 Changes to example. 2021-11-13 21:33:13 +01:00
Christoffer Lerno
d3fed67dbe Changes to example. 2021-11-13 21:30:09 +01:00
Christoffer Lerno
91996e5973 Updated with some additional explanatory comments. 2021-11-13 21:26:53 +01:00
Christoffer Lerno
3156fcb4aa Updated with some additional explanatory comments. 2021-11-13 21:24:59 +01:00
Christoffer Lerno
8309d84fdb Updated with some explanatory comments. 2021-11-13 20:58:24 +01:00
Christoffer Lerno
4efb433934 Updated README with example of generic modules. 2021-11-13 20:20:31 +01:00
Christoffer Lerno
7142ce2f0c Updated README 2021-11-13 19:43:50 +01:00
Christoffer Lerno
a436a9b069 Updated README 2021-11-13 19:31:01 +01:00
Christoffer Lerno
fb56d380cc Builtins work. Math library exposes some builtins. Volatile store / load. 2021-11-13 12:31:45 +01:00
Christoffer Lerno
7bd76c973c Placeholders for builtins. Updated character literal parsing, supporting 1-16 characters. More test cases. 2021-11-12 10:13:15 +01:00
Christoffer Lerno
42465039e9 Updated character parsing. 2021-11-10 21:58:17 +01:00
Christoffer Lerno
df0b1df1df Better compatibility with LLVM < 13 (#319)
* Update wrapper to add type attribute on LLVM < 13
2021-11-09 22:53:13 +01:00
Christoffer Lerno
137b474f44 Update tester to read utf-8 2021-11-08 22:22:01 +01:00
Christoffer Lerno
4662133893 Updates to bitstruct 2021-11-08 22:22:01 +01:00
Christoffer Lerno
15f902579b Removed virtual 2021-11-08 22:22:01 +01:00
Christoffer Lerno
4e47f0b624 Fixes to bitstruct and work on correct behaviour when embedded in structs. 2021-11-08 22:22:01 +01:00
Christoffer Lerno
4f09b0c351 Bitstruct implementation. 2021-11-05 11:59:30 +01:00
Christoffer Lerno
29e7af843a Remove old try-catch. Top down promotion for binary etc. Prevent non-simple widening. Introducing wildcard failable. Move LLVM GEP usage. Regcall test and fix. Optimized slice assign. 2021-10-26 18:40:41 +02:00
Christoffer Lerno
e2a3000c39 Cleanup of casts 2021-10-22 00:59:13 +02:00
Christoffer Lerno
9942be54dc More optimized memclear. Added helper function on array gep. 2021-10-21 23:37:14 +02:00
Christoffer Lerno
b87e14cba8 Enum const inlining. 2021-10-21 15:13:11 +02:00
Christoffer Lerno
4ca7ba048b Updated to use ?, ?? and !! instead of !!, else and "else $unreachable" 2021-10-20 23:27:13 +02:00
Christoffer Lerno
1b38e24a9f Make it an error to do x < 0 when x is an unsigned integer. 2021-10-20 18:53:43 +02:00
Christoffer Lerno
f284a1c575 Updated readme. 2021-10-20 17:50:38 +02:00
Christoffer Lerno
86723540f3 $switch over values. 2021-10-20 17:49:07 +02:00
Christoffer Lerno
aa239c6a87 Allow "fn" instead of "func". Rewrote $switch over types. 2021-10-20 16:30:45 +02:00
data-man
09d9968e97 Update dirent.h to latest 2021-10-20 13:36:29 +02:00
Christoffer Lerno
76e4eea4a8 Fix missing support for array[] as return types. Fix bug with &&-folding. Added tests. 2021-10-20 11:57:41 +02:00
Christoffer Lerno
43a4967987 Fix macro return value handling. #252 2021-10-20 00:59:44 +02:00
Christoffer Lerno
ab5bacc78a Fix MingW CI 2021-10-20 00:38:21 +02:00
Christoffer Lerno
af34eec61d Pushed small win fix. 2021-10-19 23:12:25 +02:00
Christoffer Lerno
f48661d35e Removed use of basename / dirname. 2021-10-19 23:09:05 +02:00
Christoffer Lerno
5592d19152 Widen VarDeclKind enum in VarDecl 2021-10-19 21:23:49 +02:00
Christoffer Lerno
55f676dd5c Fix to testscript. 2021-10-19 20:57:47 +02:00
Christoffer Lerno
96571e2b65 Update tester to avoid adding CR on win. 2021-10-19 20:48:48 +02:00
Christoffer Lerno
4a3f5c4eb3 Update gitattributes 2021-10-19 20:38:28 +02:00
Christoffer Lerno
1db23defbc Restore strcompare MSC 2021-10-19 18:32:31 +02:00
Christoffer Lerno
6c9b5fd30d Update MSVC path max. 2021-10-19 18:32:31 +02:00
Christoffer Lerno
8e93642535 LLVM 13 compatibility. Move Ubuntu CI to LLVM 12. 2021-10-19 18:32:31 +02:00
Christoffer Lerno
d36fc9b19e Formatting. 2021-10-19 16:47:25 +02:00
C34A
5dea48101f MSVC compatibility 2021-10-19 13:04:17 +02:00
Christoffer Lerno
b4df56db54 Removed original_type, pure, removed bigint, added i128 type, lots of fixes to $Foo, reassigning ct type, catch/macro, "!", removed type inference. 2021-10-19 10:39:02 +02:00
Christoffer Lerno
1b086e06f1 Fix of miscompilation 2021-10-07 13:53:11 +02:00
Christoffer Lerno
aca5adebd5 Fix for token widening and added clarifying casts for sema checks. 2021-10-03 22:18:26 +02:00
Christoffer Lerno
dc097fe130 Widen token type 2021-10-03 21:48:49 +02:00
Christoffer Lerno
ae371d105d Add vector ++ -- ! ~ - | & ^ ops and allow .c3t to be compiled. 2021-09-30 13:54:51 +02:00
data-man
de6ea0d1ae Better support LLD 14+ 2021-09-30 11:15:44 +02:00
Christoffer Lerno
020eba720b Added initial (incomplete) support for vectors. 2021-09-28 14:19:28 +02:00
Christoffer Lerno
0a4f35154a Addeded some hash functions. Fix of visibility resolution for macros. std lib is now loaded from a directory. 2021-09-22 10:27:10 +02:00
Christoffer Lerno
fb9be722bc Fix issue with default macro arguments. Test of ?? instead of else. Added libc 2021-09-20 22:25:01 +02:00
Christoffer Lerno
e4c7dde30b Fix of shadowing bug. Allow pointer and subarrays to be constant initialized. Compile time values may now pass around anything considered compile time constant. It's possible to index into an initializer list at compile time. (Some work still remains on this) 2021-09-18 01:13:42 +02:00
data-man
1b103a3e22 Support LLVM 14+ 2021-09-16 18:05:13 +02:00
Christoffer Lerno
b87b67ebbb Factorial macro example. 2021-09-11 02:34:09 +02:00
Christoffer Lerno
17dcb742c6 Fixed codegen for subarray global initializers. Adding fasta example. 2021-09-11 00:44:35 +02:00
Christoffer Lerno
b7e423adc2 Update .len for subarray to not require () 2021-09-10 19:44:27 +02:00
Christoffer Lerno
0aef2810c8 Added fasta example. 2021-09-10 19:27:42 +02:00
Christoffer Lerno
2b2be6b491 Added mandelbrot example code. 2021-09-10 16:12:54 +02:00
Christoffer Lerno
49d13c23bb Fix issue with grouped expressions in macros. Adding spectral norml code example. 2021-09-10 15:47:31 +02:00
Christoffer Lerno
bcda6d71c9 AST printout removed. Split initializer types. Const list functionality. 2021-09-10 15:47:23 +02:00
Christoffer Lerno
c76e8e0713 Added nbodies example 2021-09-10 10:30:20 +02:00
Christoffer Lerno
29b3535460 Added fannkuch example. 2021-09-10 09:20:27 +02:00
Christoffer Lerno
fd1eafe5bf Fix base64 and hash examples. 2021-09-08 23:54:56 +02:00
Christoffer Lerno
468921225d Compound literal uses Foo {} now. 2021-09-08 16:43:02 +02:00
Benjamin Stigsen
50853bb5de Create .gitattributes
Add C language syntax highlighting to C3
2021-09-08 10:52:20 +02:00
1033 changed files with 126940 additions and 42124 deletions

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
$ cat .gitattributes
* text=auto
*.c3 linguist-language=C
*.c3t linguist-language=C

View File

@@ -6,66 +6,171 @@ on:
pull_request:
branches: [ master ]
env:
LLVM_RELEASE_VERSION: 16
jobs:
build-msvc:
runs-on: windows-latest
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [ Release, Debug ]
defaults:
run:
shell: cmd
steps:
- uses: actions/checkout@v3
- name: CMake
run: |
cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build --config ${{ matrix.build_type }}
- name: Compile and run some examples
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\hello_world_many.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\time.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
- name: Build testproject
run: |
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log run hello_world_win32
- name: Build testproject lib
run: |
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
- name: Vendor-fetch
run: |
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
- name: run compiler tests
run: |
cd test
python3.exe src/tester.py ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
- name: Compile run unit tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -g1 --safe
- name: upload artifacts
uses: actions/upload-artifact@v3
with:
name: c3-windows-${{ matrix.build_type }}
path: build\${{ matrix.build_type }}\c3c.exe
build-msys2-mingw:
runs-on: windows-latest
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: git binutils mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-llvm mingw-w64-x86_64-polly mingw-w64-x86_64-python mingw-w64-x86_64-lld
install: git binutils mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python
- shell: msys2 {0}
run: |
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-15.0.3-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-15.0.3-1-any.pkg.tar.zst
- name: CMake
run: |
mkdir build && cd build
cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build
../../build/c3c run --debug-log
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c.exe test_suite/
build-msys2-clang:
runs-on: windows-latest
#if: ${{ false }}
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: msys2/setup-msys2@v2
with:
msystem: CLANG64
update: true
update: false
install: git binutils mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-toolchain mingw-w64-clang-x86_64-python
- name: CMake
run: |
mkdir build && cd build
cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build
../../build/c3c run --debug-log
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
@@ -75,27 +180,418 @@ jobs:
build-linux:
runs-on: ubuntu-latest
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [16]
steps:
- uses: actions/checkout@v1
- name: (Ubuntu) Download LLVM
- uses: actions/checkout@v3
- name: Install common deps
run: |
sudo apt-get install zlib1g zlib1g-dev clang-11 libllvm11 llvm-11 llvm-11-dev llvm-11-runtime liblld-11-dev liblld-11 python3
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
- name: Install Clang ${{matrix.llvm_version}}
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
if [[ "${{matrix.llvm_version}}" < 17 ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
else
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
fi
sudo apt-get update
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
- name: CMake
run: |
mkdir build && cd build
cmake .. -DLLVM_DIR=/usr/lib/llvm-11/cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
cmake -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
../build/c3c compile examples/factorial_macro.c3
../build/c3c compile examples/fasta.c3
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/hash.c3
../build/c3c compile examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/nbodies.c3
../build/c3c compile examples/spectralnorm.c3
../build/c3c compile examples/swap.c3
../build/c3c compile examples/contextfree/boolerr.c3
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit -g1 --safe
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build
../../build/c3c run --debug-log
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --forcelinker
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == 16
run: |
mkdir linux
cp -r lib linux
cp msvc_build_libraries.py linux
cp build/c3c linux
tar czf c3-linux-${{matrix.build_type}}.tar.gz linux
- name: upload artifacts
if: matrix.llvm_version == 16
uses: actions/upload-artifact@v3
with:
name: c3-linux-${{matrix.build_type}}
path: c3-linux-${{matrix.build_type}}.tar.gz
build-linux-ubuntu20:
runs-on: ubuntu-20.04
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [16]
steps:
- uses: actions/checkout@v3
- name: Install common deps
run: |
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
- name: Install Clang ${{matrix.llvm_version}}
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
if [[ "${{matrix.llvm_version}}" < 17 ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
else
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
fi
sudo apt-get update
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
- name: CMake
run: |
cmake -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
../build/c3c compile examples/factorial_macro.c3
../build/c3c compile examples/fasta.c3
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/hash.c3
../build/c3c compile examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/nbodies.c3
../build/c3c compile examples/spectralnorm.c3
../build/c3c compile examples/swap.c3
../build/c3c compile examples/contextfree/boolerr.c3
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit -g1 --safe
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --forcelinker
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == 16
run: |
mkdir linux
cp -r lib linux
cp msvc_build_libraries.py linux
cp build/c3c linux
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz linux
- name: upload artifacts
if: matrix.llvm_version == 16
uses: actions/upload-artifact@v3
with:
name: c3-ubuntu-20-${{matrix.build_type}}
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
build-mac:
runs-on: macos-latest
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [15, 16]
steps:
- uses: actions/checkout@v3
- name: Download LLVM
run: |
brew install llvm@${{ matrix.llvm_version }} ninja curl
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
- name: CMake
run: |
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit -g1 --safe
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --forcelinker
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
run: |
mkdir macos
cp -r lib macos
cp msvc_build_libraries.py macos
cp build/c3c macos
zip -r c3-macos-${{matrix.build_type}}.zip macos
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
uses: actions/upload-artifact@v3
with:
name: c3-macos-${{matrix.build_type}}
path: c3-macos-${{matrix.build_type}}.zip
release:
runs-on: ubuntu-latest
needs: [build-msvc, build-linux, build-mac]
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v3
- name: delete tag
continue-on-error: true
uses: actions/github-script@v6
with:
script: |
github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'tags/latest',
sha: context.sha
})
- name: create tag
uses: actions/github-script@v6
with:
script: |
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/latest',
sha: context.sha
})
- uses: actions/download-artifact@v3
- run: cp -r lib c3-windows-Release
- run: cp -r lib c3-windows-Debug
- run: cp msvc_build_libraries.py c3-windows-Release
- run: cp msvc_build_libraries.py c3-windows-Debug
- run: cp install_win_reqs.bat c3-windows-Release
- run: cp install_win_reqs.bat c3-windows-Debug
- run: zip -r c3-windows-Release.zip c3-windows-Release
- run: zip -r c3-windows-Debug.zip c3-windows-Debug
- id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: latest
release_name: latest
draft: false
prerelease: true
- name: upload windows
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-windows-Release.zip
asset_name: c3-windows.zip
asset_content_type: application/zip
- name: upload windows debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-windows-Debug.zip
asset_name: c3-windows-debug.zip
asset_content_type: application/zip
- name: upload linux
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-linux-Release/c3-linux-Release.tar.gz
asset_name: c3-linux.tar.gz
asset_content_type: application/gzip
- name: upload linux debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-linux-Debug/c3-linux-Debug.tar.gz
asset_name: c3-linux-debug.tar.gz
asset_content_type: application/gzip
- name: upload ubuntu 20
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz
asset_name: c3-ubuntu-20.tar.gz
asset_content_type: application/gzip
- name: upload ubuntu 20 debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz
asset_name: c3-ubuntu-20-debug.tar.gz
asset_content_type: application/gzip
- name: upload macos
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-macos-Release/c3-macos-Release.zip
asset_name: c3-macos.zip
asset_content_type: application/zip
- name: upload macos debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-macos-Debug/c3-macos-Debug.zip
asset_name: c3-macos-debug.zip
asset_content_type: application/zip

9
.gitignore vendored
View File

@@ -52,7 +52,6 @@ Module.symvers
Mkfile.old
dkms.conf
/build/
/cmake-build-debug/
.idea/
/resources/grammar.tab.c
/resources/grammar.vcg
@@ -60,3 +59,11 @@ dkms.conf
/resources/y.tab.c
/resources/y.tab.h
/bin/
#visual studio files
.vs/
.vscode/
out/
/cmake-build-debug/
/cmake-build-release/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "tilde-backend"]
path = tilde-backend
url = https://github.com/c3lang/tilde-backend

View File

@@ -1,173 +1,388 @@
cmake_minimum_required(VERSION 3.10)
project(c3c)
cmake_minimum_required(VERSION 3.15)
SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
# Grab the version
file(READ "src/version.h" ver)
if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
message(FATAL_ERROR "Compiler version could not be parsed from version.h")
endif()
#set(CMAKE_BUILD_TYPE Release)
#set(CMAKE_CXX_FLAGS_RELEASE "-O3")
# Set the project and version
project(c3c VERSION ${CMAKE_MATCH_1})
message("C3C version: ${CMAKE_PROJECT_VERSION}")
find_package(LLVM REQUIRED CONFIG)
# Enable fetching (for Windows)
include(FetchContent)
include(FeatureSummary)
set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
# We use C11 and C++17
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if(MSVC)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHsc")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHsc")
else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fno-exceptions")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fno-exceptions")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
endif()
#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1 -fsanitize=undefined")
#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O1 -fsanitize=undefined")
#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined")
#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fsanitize=undefined")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
option(C3_USE_MIMALLOC "Use built-in mimalloc" OFF)
option(C3_USE_TB "Use TB" OFF)
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
set(C3_USE_MIMALLOC OFF)
if(C3_USE_MIMALLOC)
option(MI_BUILD_TESTS OFF)
option(MI_BUILD_SHARED OFF)
option(MI_PADDING OFF)
option(MI_DEBUG_FULL OFF)
FetchContent_Declare(
mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
GIT_TAG ${C3_MIMALLOC_TAG}
)
FetchContent_MakeAvailable(mimalloc)
endif()
if (NOT WIN32)
find_package(CURL)
endif()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
if (${C3_LLVM_VERSION} VERSION_LESS 15 OR ${C3_LLVM_VERSION} VERSION_GREATER 17)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
endif()
endif()
find_package(Git QUIET)
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
# Update submodules as needed
option(GIT_SUBMODULE "Check submodules during build" ON)
if(GIT_SUBMODULE)
message(STATUS "Submodule update")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
endif()
endif()
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "16")
endif()
FetchContent_Declare(
LLVM_Windows
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows_debug)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_debug_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
else()
message("Loading Windows LLVM libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
endif()
find_package(LLVM REQUIRED CONFIG)
find_package(LLD REQUIRED CONFIG)
else()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
else()
find_package(LLVM REQUIRED CONFIG)
endif()
endif()
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
add_definitions(${LLVM_DEFINITIONS})
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(LLVM_LINK_COMPONENTS
AllTargetsAsmParsers
AllTargetsCodeGens
AllTargetsDescs
AllTargetsDisassemblers
AllTargetsInfos
AllTargetsCodeGens
Analysis
AsmPrinter
BitReader
Core
DebugInfoPDB
InstCombine
IrReader
LibDriver
Linker
LTO
MC
MCDisassembler
native
nativecodegen
Object
Option
ScalarOpts
Support
Target
TransformUtils
native
nativecodegen
AsmPrinter
Linker
LTO
WindowsManifest
DebugInfoPDB
LibDriver
Option
IrReader
)
WindowsDriver
)
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
file(COPY ${CMAKE_SOURCE_DIR}/resources/lib DESTINATION ${CMAKE_BINARY_DIR})
find_library(LLD_COFF NAMES lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_COMMON NAMES lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_CORE NAMES lldCore.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_WASM NAMES lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_MINGW NAMES lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_ELF NAMES lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_DRIVER NAMES lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_READER_WRITER NAMES lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_MACHO NAMES lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_YAML NAMES lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS})
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(lld_libs
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_DRIVER}
${LLD_READER_WRITER}
${LLD_MACHO}
${LLD_YAML}
${LLD_CORE}
)
else()
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
endif()
if (APPLE)
set(lld_libs ${lld_libs} xar)
endif ()
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
add_library(miniz STATIC dependencies/miniz/miniz.c)
add_executable(c3c
src/main.c
src/build/builder.c
src/build/build_options.c
src/build/project_creation.c
src/utils/errors.c
src/utils/file_utils.c
src/compiler/lexer.c
src/compiler/tokens.c
src/compiler/symtab.c
src/compiler/parser.c
src/compiler_tests/tests.c
src/compiler_tests/benchmark.c
src/utils/malloc.c
src/compiler/compiler.c
src/compiler/semantic_analyser.c
src/compiler/source_file.c
src/compiler/diagnostics.c
src/compiler/ast.c
src/compiler/bigint.c
src/compiler/context.c
src/compiler/sema_expr.c
src/compiler/enums.h
src/compiler/sema_casts.c
src/compiler/target.c
src/compiler/c_abi_internal.h
src/compiler/codegen_general.c
src/compiler/compiler.c
src/compiler/compiler.h
src/compiler/types.c
src/compiler/module.c
src/compiler/llvm_codegen.c
src/utils/stringutils.c
src/compiler/dwarf.h
src/compiler/context.c
src/compiler/copying.c
src/compiler/llvm_codegen_stmt.c
src/compiler/llvm_codegen_expr.c
src/compiler/diagnostics.c
src/compiler/dwarf.h
src/compiler/enums.h
src/compiler/float.c
src/compiler/headers.c
src/compiler/lexer.c
src/compiler/libraries.c
src/compiler/linker.c
src/compiler/llvm_codegen.c
src/compiler/abi/c_abi_aarch64.c
src/compiler/abi/c_abi.c
src/compiler/abi/c_abi_riscv.c
src/compiler/abi/c_abi_wasm.c
src/compiler/abi/c_abi_win64.c
src/compiler/abi/c_abi_x64.c
src/compiler/abi/c_abi_x86.c
src/compiler/llvm_codegen_debug_info.c
src/compiler/llvm_codegen_module.c
src/compiler/llvm_codegen_type.c
src/compiler/llvm_codegen_expr.c
src/compiler/llvm_codegen_function.c
src/compiler/llvm_codegen_c_abi.c
src/build/builder.c
src/utils/toml.c src/build/project.c
src/compiler/sema_name_resolution.c
src/target_info/target_info.c
src/compiler/llvm_codegen_instr.c
src/compiler/llvm_codegen_module.c
src/compiler/llvm_codegen_stmt.c
src/compiler/llvm_codegen_type.c
src/compiler/llvm_codegen_value.c
src/compiler/module.c
src/compiler/number.c
src/compiler/parse_expr.c
src/compiler/parse_global.c
src/compiler/parser.c
src/compiler/parser_internal.h
src/compiler/parse_stmt.c
src/compiler/parse_global.c
src/compiler/sema_passes.c
src/compiler/sema_internal.h
src/compiler/sema_casts.c
src/compiler/sema_decls.c
src/compiler/sema_types.c
src/compiler/sema_expr.c
src/compiler/sema_internal.h
src/compiler/sema_name_resolution.c
src/compiler/sema_errors.c
src/compiler/sema_builtins.c
src/compiler/sema_initializers.c
src/compiler/semantic_analyser.c
src/compiler/sema_passes.c
src/compiler/sema_stmts.c
src/compiler/number.c
src/compiler/linker.c
src/compiler/sema_types.c
src/compiler/source_file.c
src/compiler/symtab.c
src/compiler/target.c
src/compiler/sema_asm.c
src/compiler_tests/benchmark.c
src/compiler_tests/tests.c
src/compiler/tokens.c
src/compiler/types.c
src/main.c
src/utils/errors.c
src/utils/file_utils.c
src/utils/find_msvc.c
src/utils/malloc.c
src/utils/stringutils.c
src/utils/taskqueue.c
src/utils/json.c
src/build/project.c
src/utils/vmem.c
src/utils/vmem.h
src/utils/whereami.c
src/utils/taskqueue.c
src/compiler/llvm_codegen_c_abi_x86.c
src/compiler/c_abi_internal.h
src/compiler/llvm_codegen_c_abi_x64.c
src/compiler/llvm_codegen_c_abi_win64.c
src/compiler/llvm_codegen_c_abi_aarch64.c
src/compiler/headers.c
src/compiler/llvm_codegen_c_abi_riscv.c
src/compiler/llvm_codegen_c_abi_wasm.c)
src/utils/cpus.c
src/utils/unzipper.c
src/compiler/decltable.c
src/compiler/mac_support.c
src/compiler/llvm_codegen_storeload.c
src/compiler/windows_support.c
src/compiler/codegen_asm.c
src/compiler/asm_target.c
src/compiler/llvm_codegen_builtins.c
src/compiler/expr.c
src/utils/time.c
src/utils/http.c
src/compiler/sema_liveness.c)
target_compile_options(c3c PRIVATE -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
if (C3_USE_TB)
file(GLOB tilde-sources
tilde-backend/src/tb/*.c
tilde-backend/src/tb/codegen/*.c
tilde-backend/src/tb/bigint/*.c
tilde-backend/src/tb/objects/*.c
tilde-backend/src/tb/system/*.c
tilde-backend/src/tb/debug/cv/*.c
tilde-backend/src/tb/opt/*.c
tilde-backend/src/tb/x64/*.c
tilde-backend/src/tb/wasm/*.c
tilde-backend/src/tb/aarch64/*.c
)
target_sources(c3c PRIVATE
src/compiler/tilde_codegen.c
src/compiler/tilde_codegen_instr.c
src/compiler/tilde_codegen_value.c
src/compiler/tilde_codegen_storeload.c
src/compiler/tilde_codegen_expr.c
src/compiler/tilde_codegen_stmt.c
src/compiler/tilde_codegen_type.c
src/compiler/tilde_codegen_abi.c
src/compiler/tilde_codegen_storeload.c)
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=1)
target_link_libraries(c3c tilde-backend)
add_library(tilde-backend STATIC ${tilde-sources})
target_include_directories(tilde-backend PRIVATE
"${CMAKE_SOURCE_DIR}/tilde-backend/src/" "${CMAKE_SOURCE_DIR}/tilde-backend/include")
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/tilde-backend/include/")
else()
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=0)
endif()
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/src/")
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/src/")
message(STATUS "Found LLD ${lld_libs}")
target_include_directories(miniz PUBLIC
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
#target_link_libraries(c3c m ${llvm_libs} c3c_wrappers lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO lldYAML)
target_link_libraries(c3c m ${llvm_libs} c3c_wrappers ${lld_libs})
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
if(C3_USE_MIMALLOC)
target_link_libraries(c3c mimalloc-static)
endif()
if (WIN32)
if (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILE_ID STREQUAL "GNU")
target_link_options(c3c PRIVATE -pthread)
target_link_libraries(c3c Winhttp.lib)
endif()
if (CURL_FOUND)
target_link_libraries(c3c ${CURL_LIBRARIES})
target_include_directories(c3c PRIVATE ${CURL_INCLUDES})
target_compile_definitions(c3c PUBLIC CURL_FOUND=1)
else()
target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
endif()
if(MSVC)
message("Adding MSVC options")
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(c3c PUBLIC /MTd)
target_compile_options(c3c_wrappers PUBLIC /MTd)
target_compile_options(miniz PUBLIC /MTd)
if (C3_USE_TB)
target_compile_options(tilde-backend PUBLIC /MTd)
endif()
else()
target_compile_options(c3c PUBLIC /MT)
target_compile_options(c3c_wrappers PUBLIC /MT)
target_compile_options(miniz PUBLIC /MT)
if (C3_USE_TB)
target_compile_options(tilde-backend PUBLIC /MT)
endif()
endif()
else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
if (WIN32)
target_compile_definitions(c3c PRIVATE USE_PTHREAD=1)
target_compile_options(c3c PRIVATE -mlong-double-64)
endif()
endif()
install(TARGETS c3c DESTINATION bin)
feature_summary(WHAT ALL)

15
CMakeSettings.json Normal file
View File

@@ -0,0 +1,15 @@
{
"configurations": [
{
"name": "build",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "-DLLVM_DIR=C:\\llvm\\llvm\\build\\lib\\cmake\\llvm -DCMAKE_CXX_FLAGS:STRING=\"${CMAKE_CXX_FLAGS} /J\"",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
}
]
}

View File

@@ -116,7 +116,8 @@ more tests as you go along.
### Don't bring in dependencies
External libraries has maintainability issues. Try to depend on as few libraries
as possible. Currently c3c only depends on LLVM and libc.
as possible. Currently, c3c only depends on LLVM and libc with an optional
dependency on libcurl.
Do use rewrites of subsets of other libraries to bring in functionality, but avoid
copying in libraries that needs to be updated separately.

20
LICENSE_STDLIB Normal file
View File

@@ -0,0 +1,20 @@
Copyright (c) 2022 Christoffer Lernö and contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in 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:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
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 AUTHORS 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 IN THE SOFTWARE.

345
README.md
View File

@@ -1,10 +1,22 @@
# C3 Language
C3 is a C-like language trying to be "an incremental improvement over C" rather than a whole new language.
C3 owes a lot to the ideas of the [C2 language](http://c2lang.org): to iterate on top of C without trying to be a
whole new language.
C3 is a programming language that builds on the syntax and semantics of the C language,
with the goal of evolving it while still retaining familiarity for C programmers.
C3 tries to be an alternative in the C/C++ niche: fast and close to the metal.
It's an evolution, not a revolution: the C-like
for programmers who like C.
Precompiled binaries for the following operating systems are available:
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
- MacOS x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-mac-with-precompiled-binaries).
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
![vkQuake](https://github.com/c3lang/c3c/blob/master/resources/images/vkQuake.png?raw=true)
Thanks to full ABI compatibility with C, it's possible to mix C and C3 in the same project with no effort. As a demonstration, vkQuake was compiled with a small portion of the code converted to C3 and compiled with the c3c compiler. (The fork can be found at https://github.com/c3lang/vkQuake)
### Design Principles
- Procedural "get things done"-type of language.
@@ -15,29 +27,97 @@ C3 tries to be an alternative in the C/C++ niche: fast and close to the metal.
- Avoid "big ideas" & the "more is better" fallacy.
- Introduce some higher level conveniences where the value is great.
C3 owes its inspiration to the [C2 language](http://c2lang.org): to iterate on top of C without trying to be a
whole new language.
### Example code
Create a `main.c3` file with:
```c++
module hello_world;
import std::io;
The following code shows [generic modules](http://www.c3-lang.org/generics/) (more examples can be found at http://www.c3-lang.org/examples/).
func void main()
```c++
module stack <Type>;
// Above: the parameterized type is applied to the entire module.
struct Stack
{
io::printf("Hello, world!\n");
usz capacity;
usz size;
Type* elems;
}
// The type methods offers dot syntax calls,
// so this function can either be called
// Stack.push(&my_stack, ...) or
// my_stack.push(...)
fn void Stack.push(Stack* this, Type element)
{
if (this.capacity == this.size)
{
this.capacity *= 2;
if (this.capacity < 16) this.capacity = 16;
this.elems = mem::realloc(this.elems, Type.sizeof * this.capacity);
}
this.elems[this.size++] = element;
}
fn Type Stack.pop(Stack* this)
{
assert(this.size > 0);
return this.elems[--this.size];
}
fn bool Stack.empty(Stack* this)
{
return !this.size;
}
```
Make sure you have the standard libraries at either `../lib/std/` or `/lib/std/`.
Testing it out:
Then run
```sh
c3c compile main.c3
```cpp
import stack;
// Define our new types, the first will implicitly create
// a complete copy of the entire Stack module with "Type" set to "int"
def IntStack = Stack<int>;
// The second creates another copy with "Type" set to "double"
def DoubleStack = Stack<double>;
// If we had added "define IntStack2 = Stack<int>"
// no additional copy would have been made (since we already
// have an parameterization of Stack<int>) so it would
// be same as declaring IntStack2 an alias of IntStack
// Importing an external C function is straightforward
// here is an example of importing libc's printf:
extern fn int printf(char* format, ...);
fn void main()
{
IntStack stack;
// Note that C3 uses zero initialization by default
// so the above is equivalent to IntStack stack = {};
stack.push(1);
// The above can also be written IntStack.push(&stack, 1);
stack.push(2);
// Prints pop: 2
printf("pop: %d\n", stack.pop());
// Prints pop: 1
printf("pop: %d\n", stack.pop());
DoubleStack dstack;
dstack.push(2.3);
dstack.push(3.141);
dstack.push(1.1235);
// Prints pop: 1.123500
printf("pop: %f\n", dstack.pop());
}
```
The generated binary will be called `a.out`.
### In what ways do C3 differ from C?
### In what ways does C3 differ from C?
- No mandatory header files
- New semantic macro system
@@ -50,7 +130,6 @@ The generated binary will be called `a.out`.
- Defer
- Value methods
- Associated enum data
- Built-in hooks for convenient string handling
- No preprocessor
- Less undefined behaviour and runtime checks in "safe" mode
- Limited operator overloading to enable userland dynamic arrays
@@ -58,112 +137,60 @@ The generated binary will be called `a.out`.
### Current status
It's possible to try out the current C3 compiler in the browser: https://ide.judge0.com/ this is courtesy of the
developer of Judge0.
The current version of the compiler is alpha release 0.4.
Design work is still being done in the design draft here: https://c3lang.github.io/c3docs/. If you have any suggestions, send a mail to [christoffer@aegik.com](mailto:christoffer@aegik.com), [file an issue](https://github.com/c3lang/c3c/issues) or discuss
C3 on its dedicated Discord: https://discord.gg/qN76R87
Design work on C3 is complete aside from fleshing out details, such as
inline asm. As the standard library work progresses, changes and improvements
to the language will happen continuously.
Follow the issues [here](https://github.com/c3lang/c3c/issues).
The compiler should compile on Linux, Windows (under Mingw or MSYS2) and MacOS,
but needs some install documentation for Windows.
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
or discuss C3 on its dedicated Discord: [https://discord.gg/qN76R87](https://discord.gg/qN76R87).
Due to its ABI compatibility with C, it's possible to mix C and C3 in the same project.
As a demonstration, vkQuake was compiled with a small portion of the code converted
to C3 and compiled with the c3c compiler:
The compiler is currently verified to compile on Linux, Windows and MacOS.
![vkQuake](https://github.com/c3lang/c3c/blob/master/resources/images/vkQuake.png?raw=true)
(The vkFork is at https://github.com/c3lang/vkQuake)
#### Todo / done
- [x] For/while/do
- [x] `if`/ternary
- [x] Structs
- [x] Union
- [x] Enums
- [x] Value methods
- [x] Compound literals
- [x] Designated initalizers
- [x] Slicing syntax
- [x] Arrays and subarrays
- [x] Modules
- [x] `$unreachable`
- [x] Compile time assert with `$assert`
- [x] Compiler guiding `assert`
- [x] C code calling by declaring methods `extern`
- [x] Compile time variables
- [x] Basic macros
- [x] 4cc, 8cc, 2cc
- [x] Enum type inference in switch/assignment
- [x] Integer type inference
- [x] Error type
- [x] Failable error handling
- [x] `try` for conditional execution
- [x] `catch` for error handling
- [x] Implicit unwrap after `catch`
- [x] `sizeof`
- [x] `typeof`
- [x] 2s complement wrapping operators
- [x] Labelled break / continue
- [x] `nextcase` statement
- [x] Expression blocks
- [x] Do-without-while
- [x] Foreach statement
- [x] Templates
- [x] Distinct types
- [x] Built-in linking
- [x] CT only macros evaluating to constants
- [x] range initializers e.g. `{ [1..2] = 2 }`
- [x] Trailing body macros e.g. `@foo(1, 100; int a) { bar(a); };`
- [x] Complex macros
- [x] CT type constants
- [ ] Anonymous structs
- [ ] Complete C ABI conformance *in progress*
- [ ] Debug info *in progress*
- [ ] Virtual type *in progress*
- [ ] Enum associated data support
- [ ] Windows support *in progress*
- [ ] All attributes *in progress*
- [ ] Associative array literals
- [ ] Reflection methods
- [ ] LTO/ThinLTO setup
- [ ] `global` / `shared` for globals
- [ ] Escape macros
- [ ] Implicit capturing macros
- [ ] Subarray initializers
- [ ] Bitstructs
- [ ] `asm` section
- [ ] `$switch`
- [ ] `$for`
- [ ] Pre-post conditions
- [ ] Stdlib inclusion
- [ ] String functions
- [ ] Simd vector types
#### What can you help with?
- If you wish to contribute with ideas, please file issues on the c3docs: https://github.com/c3lang/c3docs instead of the compiler.
- Discuss the language on discord to help iron out syntax.
- If you wish to contribute with ideas, please file issues or discuss on Discord.
- Interested in contributing to the stdlib? Please get in touch on Discord.
- Are you a Windows dev? Please help make the compiler work on Windows!
- Install instructions for other Linux and Unix variants are appreciated.
- Compilation instructions for other Linux and Unix variants are appreciated.
- Would you like to contribute bindings to some library? It would be nice to have support for SDL, Raylib and more.
- Build something with C3 and show it off and give feedback. The language is still open for significant tweaks.
- Start work on the C -> C3 converter which takes C code and does a "best effort" to translate it to C3. The first version only needs to work on C headers.
- Do you have some specific area you have deep knowledge of and could help make C3 even better at doing? File or comment on issues.
#### Installing on Ubuntu 20.10
### Installing
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
2. Install CMake: `sudo apt install cmake`
3. Install LLVM 11: `sudo apt-get install clang-11 zlib1g zlib1g-dev libllvm11 llvm-11 llvm-11-dev llvm-11-runtime liblld-11-dev liblld-11`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
7. Change directory to the build directory `cd build`
8. Set up CMake build for debug: `cmake -DLLVM_DIR=/usr/lib/llvm-11/cmake -DCMAKE_BUILD_TYPE=Debug ..`
9. Build: `cmake --build .`
#### Installing on Windows with precompiled binaries
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-windows-debug.zip))
2. Unzip exe and standard lib.
3. If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
4. Run `c3c.exe`.
You should now have a `c3c` executable.
#### Installing on Debian with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-linux-debug.tar.gz))
2. Unpack executable and standard lib.
3. Run `./c3c`.
You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
#### Installing on Mac with precompiled binaries
1. Make sure you have XCode with command line tools installed.
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-macos-debug.zip))
3. Unzip executable and standard lib.
4. Run `./c3c`.
#### Installing on Arch Linux
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git)
You can use your AUR package manager or clone it manually:
```sh
git clone https://aur.archlinux.org/c3c-git.git
cd c3c-git
makepkg -si
```
#### Building via Docker
@@ -185,10 +212,104 @@ A `c3c` executable will be found under `bin/`.
#### Installing on OS X using Homebrew
2. Install CMake: `brew install cmake`
3. Install LLVM 11: `brew install llvm`
3. Install LLVM 15: `brew install llvm`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
7. Change directory to the build directory `cd build`
8. Set up CMake build for debug: `cmake ..`
9. Build: `cmake --build .`
#### Getting started with a "hello world"
Create a `main.c3` file with:
```c++
module hello_world;
import std::io;
fn void main()
{
io::printn("Hello, world!");
}
```
Make sure you have the standard libraries at either `../lib/std/` or `/lib/std/`.
Then run
```sh
c3c compile main.c3
```
The generated binary will by default be named after the module that contains the main
function. In our case that is `hello_world`, so the resulting binary will be
called `hello_world` or `hello_world.exe`depending on platform.
### Compiling
#### Compiling on Windows
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++" (there is also `c3c/resources/install_win_reqs.bat` to automate this)
2. Install CMake
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`.
5. Set up the CMake build `cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release`
6. Build: `cmake --build build --config Release`
7. You should now have the c3c.exe
You should now have a `c3c` executable.
You can try it out by running some sample code: `c3c.exe compile ../resources/examples/hash.c3`
*Note that if you run into linking issues when building, make sure that you are using the latest version of VS17.*
#### Compiling on Ubuntu 20.10
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
2. Install CMake: `sudo apt install cmake`
3. Install LLVM 15 (or greater: C3C supports LLVM 15-17): `sudo apt-get install clang-15 zlib1g zlib1g-dev libllvm15 llvm-15 llvm-15-dev llvm-15-runtime liblld-15-dev liblld-15`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
7. Change directory to the build directory `cd build`
8. Set up CMake build: `cmake ..`
9. Build: `cmake --build .`
You should now have a `c3c` executable.
You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
#### Compiling on other Linux / Unix variants
1. Install CMake.
2. Install or compile LLVM and LLD *libraries* (version 15+ or higher)
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`.
5. Create a build directory `mkdir build`
6. Change directory to the build directory `cd build`
7. Set up CMake build for debug: `cmake ..`. At this point you may need to manually
provide the link path to the LLVM CMake directories, e.g. `cmake -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm/ ..`
8. Build: `cmake --build .`
*A note on compiling for Linux/Unix/MacOS: to be able to fetch vendor libraries
libcurl is needed. The CMake script should detect it if it is available. Note that
this functionality is non-essential and it is perfectly fine to user the compiler without it.*
#### Licensing
The C3 compiler is licensed under LGPL 3.0, the standard library itself is
MIT licensed.
#### Editor plugins
Editor plugins can be found at https://github.com/c3lang/editor-plugins.
#### Contributing unit tests
1. Write the test, either adding to existing test files in `/test/unit/` or add
a new file. (If testing the standard library, put it in the `/test/unit/stdlib/` subdirectory).
2. Make sure that the test functions have the `@test` attribute.
3. Run tests and see that they pass. (Recommended settings: `c3c compile-test --safe -g1 -O0 test/unit`.
- in this example `test/unit/` is the relative path to the test directory, so adjust as required)
4. Make a pull request for the new tests.

View File

@@ -1,42 +1,44 @@
#!/bin/bash
## build-with-docker.sh
## @author gdm85
## @modified by Kenta
##
## Script to build c3c for either Ubuntu 18 or Ubuntu 20.
## Script to build c3c for Ubuntu 22
##
#
if [ $# -ne 1 -a $# -ne 2 ]; then
echo "Usage: build-with-docker.sh (18|20) [Debug|Release]" 1>&2
exit 1
fi
read -p "Select Build Type: Debug/Release: " config
set -e
if [ -z "$2" ]; then
CMAKE_BUILD_TYPE=Debug
else
CMAKE_BUILD_TYPE="$2"
DOCKER=docker
DOCKER_RUN=""
IMAGE="c3c-builder"
if type podman 2>/dev/null >/dev/null; then
DOCKER=podman
DOCKER_RUN="--userns=keep-id"
IMAGE="localhost/$IMAGE"
fi
TAG="$1"
if [ "$1" = 18 ]; then
UBUNTU_VERSION="18.04"
DEPS="llvm-10-dev liblld-10-dev libclang-10-dev"
elif [ "$1" = 20 ]; then
UBUNTU_VERSION="20.04"
DEPS="llvm-11-dev liblld-11-dev clang-11 libllvm11 llvm-11-runtime"
if [ $config == "Debug" ]; then
echo "debug???"
CMAKE_BUILD_TYPE=Debug
else
echo "ERROR: expected 18 or 20 as Ubuntu version argument" 1>&2
exit 2
CMAKE_BUILD_TYPE="$config"
fi
cd docker && docker build -t c3c-builder:$TAG --build-arg UID=$(id -u) --build-arg GID=$(id -g) \
--build-arg DEPS="$DEPS" --build-arg UBUNTU_VERSION="$UBUNTU_VERSION" .
UBUNTU_VERSION="22.10"
LLVM_VERSION="15"
IMAGE="$IMAGE:22"
cd docker && $DOCKER build -t $IMAGE\
--build-arg DEPS="llvm-$LLVM_VERSION-dev liblld-$LLVM_VERSION-dev clang-$LLVM_VERSION libllvm$LLVM_VERSION llvm-$LLVM_VERSION-runtime" \
--build-arg UBUNTU_VERSION="$UBUNTU_VERSION" .
cd ..
rm -rf build bin lib
rm -rf build bin
mkdir -p build bin
exec docker run -ti --rm -v "$PWD":/home/c3c/source -w /home/c3c/source c3c-builder:$TAG bash -c \
"cd build && cmake -DLLVM_DIR=/usr/lib/llvm-11/cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE .. && cmake --build . && mv c3c ../bin/ && mv lib ../lib"
exec $DOCKER run -ti --rm --tmpfs=/tmp $DOCKER_RUN -v "$PWD":/home/c3c/source -w /home/c3c/source $IMAGE bash -c \
"cd build && cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DC3_LLVM_VERSION=$LLVM_VERSION .. && cmake --build . && mv c3c lib ../bin/"

7833
dependencies/miniz/miniz.c vendored Normal file

File diff suppressed because it is too large Load Diff

1422
dependencies/miniz/miniz.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && export TERM=xterm && apt-get update
ARG GID=1000
ARG UID=1000
RUN groupadd --gid=$GID c3c && useradd --gid=$GID --uid=$GID --create-home --shell /bin/bash c3c
RUN groupadd -o --gid=$GID c3c && useradd --gid=$GID --uid=$GID --create-home --shell /bin/bash c3c
USER c3c

17
install_win_reqs.bat Normal file
View File

@@ -0,0 +1,17 @@
@echo off
set DOWNLOAD_URL=https://aka.ms/vs/17/release
mkdir tmp 2> NUL
if not exist "tmp\vs_buildtools.exe" (
bitsadmin /transfer /download /priority foreground %DOWNLOAD_URL%/vs_buildtools.exe %CD%\tmp\vs_buildtools.exe
)
echo Preparing Build Tools, please wait...
tmp\vs_BuildTools.exe --quiet --wait --layout tmp\ --add Microsoft.VisualStudio.Component.Windows10SDK.19041
echo Installing Build Tools, please wait...
tmp\vs_BuildTools.exe --quiet --wait --noweb --add Microsoft.VisualStudio.Component.Windows10SDK.19041
REM rmdir tmp /s /q

73
lib/std/ascii.c3 Normal file
View File

@@ -0,0 +1,73 @@
module std::ascii;
macro bool in_range_m(c, start, len) => (uint)(c - start) < len;
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
macro bool is_bdigit_m(c) => in_range_m(c, 0x30, 2);
macro bool is_odigit_m(c) => in_range_m(c, 0x30, 8);
macro bool is_xdigit_m(c) => in_range_m(c | 32, 0x61, 6) || is_digit_m(c);
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
macro to_lower_m(c) => is_upper_m(c) ? c + 0x20 : c;
macro to_upper_m(c) => is_lower_m(c) ? c - 0x20 : c;
fn bool in_range(char c, char start, char len) => in_range_m(c, start, len);
fn bool is_lower(char c) => is_lower_m(c);
fn bool is_upper(char c) => is_upper_m(c);
fn bool is_digit(char c) => is_digit_m(c);
fn bool is_bdigit(char c) => is_bdigit_m(c);
fn bool is_odigit(char c) => is_odigit_m(c);
fn bool is_xdigit(char c) => is_xdigit_m(c);
fn bool is_alpha(char c) => is_alpha_m(c);
fn bool is_print(char c) => is_print_m(c);
fn bool is_graph(char c) => is_graph_m(c);
fn bool is_space(char c) => is_space_m(c);
fn bool is_alnum(char c) => is_alnum_m(c);
fn bool is_punct(char c) => is_punct_m(c);
fn bool is_blank(char c) => is_blank_m(c);
fn bool is_cntrl(char c) => is_cntrl_m(c);
fn char to_lower(char c) => (char)to_lower_m(c);
fn char to_upper(char c) => (char)to_upper_m(c);
fn bool char.in_range(char c, char start, char len) => in_range_m(c, start, len);
fn bool char.is_lower(char c) => is_lower_m(c);
fn bool char.is_upper(char c) => is_upper_m(c);
fn bool char.is_digit(char c) => is_digit_m(c);
fn bool char.is_bdigit(char c) => is_bdigit_m(c);
fn bool char.is_odigit(char c) => is_odigit_m(c);
fn bool char.is_xdigit(char c) => is_xdigit_m(c);
fn bool char.is_alpha(char c) => is_alpha_m(c);
fn bool char.is_print(char c) => is_print_m(c);
fn bool char.is_graph(char c) => is_graph_m(c);
fn bool char.is_space(char c) => is_space_m(c);
fn bool char.is_alnum(char c) => is_alnum_m(c);
fn bool char.is_punct(char c) => is_punct_m(c);
fn bool char.is_blank(char c) => is_blank_m(c);
fn bool char.is_cntrl(char c) => is_cntrl_m(c);
fn char char.to_lower(char c) => (char)to_lower_m(c);
fn char char.to_upper(char c) => (char)to_upper_m(c);
fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len);
fn bool uint.is_lower(uint c) => is_lower_m(c);
fn bool uint.is_upper(uint c) => is_upper_m(c);
fn bool uint.is_digit(uint c) => is_digit_m(c);
fn bool uint.is_bdigit(uint c) => is_bdigit_m(c);
fn bool uint.is_odigit(uint c) => is_odigit_m(c);
fn bool uint.is_xdigit(uint c) => is_xdigit_m(c);
fn bool uint.is_alpha(uint c) => is_alpha_m(c);
fn bool uint.is_print(uint c) => is_print_m(c);
fn bool uint.is_graph(uint c) => is_graph_m(c);
fn bool uint.is_space(uint c) => is_space_m(c);
fn bool uint.is_alnum(uint c) => is_alnum_m(c);
fn bool uint.is_punct(uint c) => is_punct_m(c);
fn bool uint.is_blank(uint c) => is_blank_m(c);
fn bool uint.is_cntrl(uint c) => is_cntrl_m(c);
fn uint uint.to_lower(uint c) => (uint)to_lower_m(c);
fn uint uint.to_upper(uint c) => (uint)to_upper_m(c);

5
lib/std/atomic.c3 Normal file
View File

@@ -0,0 +1,5 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::atomic;

173
lib/std/bits.c3 Normal file
View File

@@ -0,0 +1,173 @@
module std::bits;
/**
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
**/
macro reverse(i) => $$bitreverse(i);
/**
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
**/
macro bswap(i) @builtin => $$bswap(i);
macro uint[<*>].popcount(uint[<*>] i) => $$popcount(i);
macro uint[<*>].ctz(uint[<*>] i) => $$ctz(i);
macro uint[<*>].clz(uint[<*>] i) => $$clz(i);
macro uint[<*>] uint[<*>].fshl(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshl(hi, lo, shift);
macro uint[<*>] uint[<*>].fshr(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshr(hi, lo, shift);
macro uint[<*>] uint[<*>].rotl(uint[<*>] i, uint[<*>] shift) => $$fshl(i, i, shift);
macro uint[<*>] uint[<*>].rotr(uint[<*>] i, uint[<*>] shift) => $$fshr(i, i, shift);
macro int[<*>].popcount(int[<*>] i) => $$popcount(i);
macro int[<*>].ctz(int[<*>] i) => $$ctz(i);
macro int[<*>].clz(int[<*>] i) => $$clz(i);
macro int[<*>] int[<*>].fshl(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshl(hi, lo, shift);
macro int[<*>] int[<*>].fshr(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshr(hi, lo, shift);
macro int[<*>] int[<*>].rotl(int[<*>] i, int[<*>] shift) => $$fshl(i, i, shift);
macro int[<*>] int[<*>].rotr(int[<*>] i, int[<*>] shift) => $$fshr(i, i, shift);
macro ushort[<*>].popcount(ushort[<*>] i) => $$popcount(i);
macro ushort[<*>].ctz(ushort[<*>] i) => $$ctz(i);
macro ushort[<*>].clz(ushort[<*>] i) => $$clz(i);
macro ushort[<*>] ushort[<*>].fshl(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshl(hi, lo, shift);
macro ushort[<*>] ushort[<*>].fshr(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshr(hi, lo, shift);
macro ushort[<*>] ushort[<*>].rotl(ushort[<*>] i, ushort[<*>] shift) => $$fshl(i, i, shift);
macro ushort[<*>] ushort[<*>].rotr(ushort[<*>] i, ushort[<*>] shift) => $$fshr(i, i, shift);
macro short[<*>].popcount(short[<*>] i) => $$popcount(i);
macro short[<*>].ctz(short[<*>] i) => $$ctz(i);
macro short[<*>].clz(short[<*>] i) => $$clz(i);
macro short[<*>] short[<*>].fshl(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshl(hi, lo, shift);
macro short[<*>] short[<*>].fshr(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshr(hi, lo, shift);
macro short[<*>] short[<*>].rotl(short[<*>] i, short[<*>] shift) => $$fshl(i, i, shift);
macro short[<*>] short[<*>].rotr(short[<*>] i, short[<*>] shift) => $$fshr(i, i, shift);
macro char[<*>].popcount(char[<*>] i) => $$popcount(i);
macro char[<*>].ctz(char[<*>] i) => $$ctz(i);
macro char[<*>].clz(char[<*>] i) => $$clz(i);
macro char[<*>] char[<*>].fshl(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshl(hi, lo, shift);
macro char[<*>] char[<*>].fshr(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshr(hi, lo, shift);
macro char[<*>] char[<*>].rotl(char[<*>] i, char[<*>] shift) => $$fshl(i, i, shift);
macro char[<*>] char[<*>].rotr(char[<*>] i, char[<*>] shift) => $$fshr(i, i, shift);
macro ichar[<*>].popcount(ichar[<*>] i) => $$popcount(i);
macro ichar[<*>].ctz(ichar[<*>] i) => $$ctz(i);
macro ichar[<*>].clz(ichar[<*>] i) => $$clz(i);
macro ichar[<*>] ichar[<*>].fshl(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshl(hi, lo, shift);
macro ichar[<*>] ichar[<*>].fshr(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshr(hi, lo, shift);
macro ichar[<*>] ichar[<*>].rotl(ichar[<*>] i, ichar[<*>] shift) => $$fshl(i, i, shift);
macro ichar[<*>] ichar[<*>].rotr(ichar[<*>] i, ichar[<*>] shift) => $$fshr(i, i, shift);
macro ulong[<*>].popcount(ulong[<*>] i) => $$popcount(i);
macro ulong[<*>].ctz(ulong[<*>] i) => $$ctz(i);
macro ulong[<*>].clz(ulong[<*>] i) => $$clz(i);
macro ulong[<*>] ulong[<*>].fshl(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshl(hi, lo, shift);
macro ulong[<*>] ulong[<*>].fshr(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshr(hi, lo, shift);
macro ulong[<*>] ulong[<*>].rotl(ulong[<*>] i, ulong[<*>] shift) => $$fshl(i, i, shift);
macro ulong[<*>] ulong[<*>].rotr(ulong[<*>] i, ulong[<*>] shift) => $$fshr(i, i, shift);
macro long[<*>].popcount(long[<*>] i) => $$popcount(i);
macro long[<*>].ctz(long[<*>] i) => $$ctz(i);
macro long[<*>].clz(long[<*>] i) => $$clz(i);
macro long[<*>] long[<*>].fshl(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshl(hi, lo, shift);
macro long[<*>] long[<*>].fshr(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshr(hi, lo, shift);
macro long[<*>] long[<*>].rotl(long[<*>] i, long[<*>] shift) => $$fshl(i, i, shift);
macro long[<*>] long[<*>].rotr(long[<*>] i, long[<*>] shift) => $$fshr(i, i, shift);
macro uint128[<*>].popcount(uint128[<*>] i) => $$popcount(i);
macro uint128[<*>].ctz(uint128[<*>] i) => $$ctz(i);
macro uint128[<*>].clz(uint128[<*>] i) => $$clz(i);
macro uint128[<*>] uint128[<*>].fshl(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshl(hi, lo, shift);
macro uint128[<*>] uint128[<*>].fshr(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshr(hi, lo, shift);
macro uint128[<*>] uint128[<*>].rotl(uint128[<*>] i, uint128[<*>] shift) => $$fshl(i, i, shift);
macro uint128[<*>] uint128[<*>].rotr(uint128[<*>] i, uint128[<*>] shift) => $$fshr(i, i, shift);
macro int128[<*>].popcount(int128[<*>] i) => $$popcount(i);
macro int128[<*>].ctz(int128[<*>] i) => $$ctz(i);
macro int128[<*>].clz(int128[<*>] i) => $$clz(i);
macro int128[<*>] int128[<*>].fshl(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshl(hi, lo, shift);
macro int128[<*>] int128[<*>].fshr(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshr(hi, lo, shift);
macro int128[<*>] int128[<*>].rotl(int128[<*>] i, int128[<*>] shift) => $$fshl(i, i, shift);
macro int128[<*>] int128[<*>].rotr(int128[<*>] i, int128[<*>] shift) => $$fshr(i, i, shift);
macro uint.popcount(uint i) => $$popcount(i);
macro uint.ctz(uint i) => $$ctz(i);
macro uint.clz(uint i) => $$clz(i);
macro uint uint.fshl(uint hi, uint lo, uint shift) => $$fshl(hi, lo, shift);
macro uint uint.fshr(uint hi, uint lo, uint shift) => $$fshr(hi, lo, shift);
macro uint uint.rotl(uint i, uint shift) => $$fshl(i, i, shift);
macro uint uint.rotr(uint i, uint shift) => $$fshr(i, i, shift);
macro int.popcount(int i) => $$popcount(i);
macro int.ctz(int i) => $$ctz(i);
macro int.clz(int i) => $$clz(i);
macro int int.fshl(int hi, int lo, int shift) => $$fshl(hi, lo, shift);
macro int int.fshr(int hi, int lo, int shift) => $$fshr(hi, lo, shift);
macro int int.rotl(int i, int shift) => $$fshl(i, i, shift);
macro int int.rotr(int i, int shift) => $$fshr(i, i, shift);
macro ushort.popcount(ushort i) => $$popcount(i);
macro ushort.ctz(ushort i) => $$ctz(i);
macro ushort.clz(ushort i) => $$clz(i);
macro ushort ushort.fshl(ushort hi, ushort lo, ushort shift) => $$fshl(hi, lo, shift);
macro ushort ushort.fshr(ushort hi, ushort lo, ushort shift) => $$fshr(hi, lo, shift);
macro ushort ushort.rotl(ushort i, ushort shift) => $$fshl(i, i, shift);
macro ushort ushort.rotr(ushort i, ushort shift) => $$fshr(i, i, shift);
macro short.popcount(short i) => $$popcount(i);
macro short.ctz(short i) => $$ctz(i);
macro short.clz(short i) => $$clz(i);
macro short short.fshl(short hi, short lo, short shift) => $$fshl(hi, lo, shift);
macro short short.fshr(short hi, short lo, short shift) => $$fshr(hi, lo, shift);
macro short short.rotl(short i, short shift) => $$fshl(i, i, shift);
macro short short.rotr(short i, short shift) => $$fshr(i, i, shift);
macro char.popcount(char i) => $$popcount(i);
macro char.ctz(char i) => $$ctz(i);
macro char.clz(char i) => $$clz(i);
macro char char.fshl(char hi, char lo, char shift) => $$fshl(hi, lo, shift);
macro char char.fshr(char hi, char lo, char shift) => $$fshr(hi, lo, shift);
macro char char.rotl(char i, char shift) => $$fshl(i, i, shift);
macro char char.rotr(char i, char shift) => $$fshr(i, i, shift);
macro ichar.popcount(ichar i) => $$popcount(i);
macro ichar.ctz(ichar i) => $$ctz(i);
macro ichar.clz(ichar i) => $$clz(i);
macro ichar ichar.fshl(ichar hi, ichar lo, ichar shift) => $$fshl(hi, lo, shift);
macro ichar ichar.fshr(ichar hi, ichar lo, ichar shift) => $$fshr(hi, lo, shift);
macro ichar ichar.rotl(ichar i, ichar shift) => $$fshl(i, i, shift);
macro ichar ichar.rotr(ichar i, ichar shift) => $$fshr(i, i, shift);
macro ulong.popcount(ulong i) => $$popcount(i);
macro ulong.ctz(ulong i) => $$ctz(i);
macro ulong.clz(ulong i) => $$clz(i);
macro ulong ulong.fshl(ulong hi, ulong lo, ulong shift) => $$fshl(hi, lo, shift);
macro ulong ulong.fshr(ulong hi, ulong lo, ulong shift) => $$fshr(hi, lo, shift);
macro ulong ulong.rotl(ulong i, ulong shift) => $$fshl(i, i, shift);
macro ulong ulong.rotr(ulong i, ulong shift) => $$fshr(i, i, shift);
macro long.popcount(long i) => $$popcount(i);
macro long.ctz(long i) => $$ctz(i);
macro long.clz(long i) => $$clz(i);
macro long long.fshl(long hi, long lo, long shift) => $$fshl(hi, lo, shift);
macro long long.fshr(long hi, long lo, long shift) => $$fshr(hi, lo, shift);
macro long long.rotl(long i, long shift) => $$fshl(i, i, shift);
macro long long.rotr(long i, long shift) => $$fshr(i, i, shift);
macro uint128.popcount(uint128 i) => $$popcount(i);
macro uint128.ctz(uint128 i) => $$ctz(i);
macro uint128.clz(uint128 i) => $$clz(i);
macro uint128 uint128.fshl(uint128 hi, uint128 lo, uint128 shift) => $$fshl(hi, lo, shift);
macro uint128 uint128.fshr(uint128 hi, uint128 lo, uint128 shift) => $$fshr(hi, lo, shift);
macro uint128 uint128.rotl(uint128 i, uint128 shift) => $$fshl(i, i, shift);
macro uint128 uint128.rotr(uint128 i, uint128 shift) => $$fshr(i, i, shift);
macro int128.popcount(int128 i) => $$popcount(i);
macro int128.ctz(int128 i) => $$ctz(i);
macro int128.clz(int128 i) => $$clz(i);
macro int128 int128.fshl(int128 hi, int128 lo, int128 shift) => $$fshl(hi, lo, shift);
macro int128 int128.fshr(int128 hi, int128 lo, int128 shift) => $$fshr(hi, lo, shift);
macro int128 int128.rotl(int128 i, int128 shift) => $$fshl(i, i, shift);
macro int128 int128.rotr(int128 i, int128 shift) => $$fshr(i, i, shift);

View File

@@ -0,0 +1,18 @@
module std::collections::enummap<Enum, ValueType>;
struct EnumMap
{
ValueType[Enum.len] values;
}
fn void EnumMap.init(EnumMap* this, ValueType init_value)
{
foreach(&a : this.values)
{
*a = init_value;
}
}
fn uint EnumMap.len(EnumMap* this) @operator(len) => this.values.len;
fn ValueType EnumMap.get(EnumMap* this, Enum key) @operator([]) => this.values[key.ordinal];
fn void EnumMap.set(EnumMap* this, Enum key, ValueType value) @operator([]=) => this.values[key.ordinal] = value;

View File

@@ -0,0 +1,148 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
/**
* @require Enum.kindof == TypeKind.ENUM : "Only enums maybe be used with an enumset"
**/
module std::collections::enumset<Enum>;
const IS_CHAR_ARRAY = Enum.elements > 128;
$switch
$case (Enum.elements > 128):
def EnumSetType @private = char[(Enum.elements + 7) / 8];
$case (Enum.elements > 64):
def EnumSetType @private = uint128;
$case (Enum.elements > 32 || $$C_INT_SIZE > 32):
def EnumSetType @private = ulong;
$case (Enum.elements > 16 || $$C_INT_SIZE > 16):
def EnumSetType @private = uint;
$case (Enum.elements > 8 || $$C_INT_SIZE > 8):
def EnumSetType @private = ushort;
$default:
def EnumSetType @private = char;
$endswitch
def EnumSet = distinct EnumSetType;
fn void EnumSet.add(EnumSet* this, Enum v)
{
$if IS_CHAR_ARRAY:
(*this)[v / 8] |= (char)(1u << (v % 8));
$else
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
$endif
}
fn void EnumSet.clear(EnumSet* this)
{
$if IS_CHAR_ARRAY:
*this = {};
$else
*this = 0;
$endif
}
fn bool EnumSet.remove(EnumSet* this, Enum v)
{
$if IS_CHAR_ARRAY:
if (!this.has(v) @inline) return false;
(*this)[v / 8] &= (char)~(1 << (v % 8));
return true;
$else
EnumSetType old = (EnumSetType)*this;
EnumSetType new = old & ~(1u << (EnumSetType)v);
*this = (EnumSet)new;
return old != new;
$endif
}
fn bool EnumSet.has(EnumSet* this, Enum v)
{
$if IS_CHAR_ARRAY:
return (bool)(((*this)[v / 8] << (v % 8)) & 0x01);
$else
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
$endif
}
fn void EnumSet.add_all(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*this)[i] |= c;
$else
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
$endif
}
fn void EnumSet.retain_all(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*this)[i] &= c;
$else
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
$endif
}
fn void EnumSet.remove_all(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*this)[i] &= ~c;
$else
*this = (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
$endif
}
fn EnumSet EnumSet.and_of(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
copy.retain_all(s);
return copy;
$else
return (EnumSet)((EnumSetType)*this & (EnumSetType)s);
$endif
}
fn EnumSet EnumSet.or_of(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
copy.add_all(s);
return copy;
$else
return (EnumSet)((EnumSetType)*this | (EnumSetType)s);
$endif
}
fn EnumSet EnumSet.diff_of(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
copy.remove_all(s);
return copy;
$else
return (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
$endif
}
fn EnumSet EnumSet.xor_of(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
foreach (i, c : s) copy[i] ^= c;
return copy;
$else
return (EnumSet)((EnumSetType)*this ^ (EnumSetType)s);
$endif
}

View File

@@ -0,0 +1,312 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::linkedlist<Type>;
struct Node @private
{
Node *next;
Node *prev;
Type value;
}
struct LinkedList
{
Allocator *allocator;
usz size;
Node *_first;
Node *_last;
}
fn void LinkedList.push(LinkedList* list, Type value)
{
list.link_first(value);
}
fn void LinkedList.push_last(LinkedList* list, Type value)
{
list.link_last(value);
}
fn void LinkedList.init(LinkedList* list, Allocator* using = mem::heap())
{
*list = { .allocator = using };
}
fn void LinkedList.tinit(LinkedList* list) => list.init(mem::temp()) @inline;
/**
* @require list.allocator
**/
macro void LinkedList.@free_node(LinkedList &list, Node* node) @private
{
list.allocator.free(node)!!;
}
macro Node* LinkedList.@alloc_node(LinkedList &list) @private
{
if (!list.allocator) list.allocator = mem::heap();
return malloc(Node, .using = list.allocator);
}
fn void LinkedList.link_first(LinkedList* list, Type value) @private
{
Node *first = list._first;
Node *new_node = list.@alloc_node();
*new_node = { .next = first, .value = value };
list._first = new_node;
if (!first)
{
list._last = new_node;
}
else
{
first.prev = new_node;
}
list.size++;
}
fn void LinkedList.link_last(LinkedList* list, Type value) @private
{
Node *last = list._last;
Node *new_node = list.@alloc_node();
*new_node = { .prev = last, .value = value };
list._last = new_node;
if (!last)
{
list._first = new_node;
}
else
{
last.next = new_node;
}
list.size++;
}
fn Type! peek(LinkedList* list) => list.first() @inline;
fn Type! peek_last(LinkedList* list) => list.last() @inline;
fn Type! LinkedList.first(LinkedList *list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
return list._first.value;
}
fn Type! LinkedList.last(LinkedList* list)
{
if (!list._last) return IteratorResult.NO_MORE_ELEMENT?;
return list._last.value;
}
fn void LinkedList.free(LinkedList* list) => list.clear() @inline;
fn void LinkedList.clear(LinkedList* list)
{
for (Node* node = list._first; node != null;)
{
Node* next = node.next;
list.@free_node(node);
node = next;
}
list._first = null;
list._last = null;
list.size = 0;
}
fn usz LinkedList.len(LinkedList* list) @inline => list.size;
/**
* @require index < list.size
**/
macro Node* LinkedList.node_at_index(LinkedList* list, usz index)
{
if (index * 2 >= list.size)
{
Node* node = list._last;
index = list.size - index - 1;
while (index--) node = node.prev;
return node;
}
Node* node = list._first;
while (index--) node = node.next;
return node;
}
/**
* @require index < list.size
**/
fn Type LinkedList.get(LinkedList* list, usz index)
{
return list.node_at_index(index).value;
}
/**
* @require index < list.size
**/
fn void LinkedList.set(LinkedList* list, usz index, Type element)
{
list.node_at_index(index).value = element;
}
/**
* @require index < list.size
**/
fn void LinkedList.remove(LinkedList* list, usz index)
{
list.unlink(list.node_at_index(index));
}
/**
* @require index <= list.size
**/
fn void LinkedList.insert(LinkedList* list, usz index, Type element)
{
switch (index)
{
case 0:
list.push(element);
case list.size:
list.push_last(element);
default:
list.link_before(list.node_at_index(index), element);
}
}
/**
* @require succ != null
**/
fn void LinkedList.link_before(LinkedList *list, Node *succ, Type value) @private
{
Node* pred = succ.prev;
Node* new_node = malloc(Node);
*new_node = { .prev = pred, .next = succ, .value = value };
succ.prev = new_node;
if (!pred)
{
list._first = new_node;
}
else
{
pred.next = new_node;
}
list.size++;
}
/**
* @require list && list._first
**/
fn void LinkedList.unlink_first(LinkedList* list) @private
{
Node* f = list._first;
Node* next = f.next;
list.@free_node(f);
list._first = next;
if (!next)
{
list._last = null;
}
else
{
next.prev = null;
}
list.size--;
}
fn bool LinkedList.remove_value(LinkedList* list, Type t)
{
for (Node* node = list._first; node != null; node = node.next)
{
if (node.value == t)
{
list.unlink(node);
return true;
}
}
return false;
}
fn bool LinkedList.remove_last_value(LinkedList* list, Type t)
{
for (Node* node = list._last; node != null; node = node.prev)
{
if (node.value == t)
{
list.unlink(node);
return true;
}
}
return false;
}
/**
* @param [&inout] list
**/
fn Type! LinkedList.pop(LinkedList* list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
defer list.unlink_first();
return list._first.value;
}
/**
* @param [&inout] list
**/
fn void! LinkedList.remove_last(LinkedList* list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
list.unlink_last();
}
/**
* @param [&inout] list
**/
fn void! LinkedList.remove_first(LinkedList* list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
list.unlink_first();
}
/**
* @param [&inout] list
* @require list._last
**/
fn void LinkedList.unlink_last(LinkedList *list) @inline @private
{
Node* l = list._last;
Node* prev = l.prev;
list._last = prev;
list.@free_node(l);
if (!prev)
{
list._first = null;
}
else
{
prev.next = null;
}
list.size--;
}
/**
* @require list != null, x != null
**/
fn void LinkedList.unlink(LinkedList* list, Node* x) @private
{
Node* next = x.next;
Node* prev = x.prev;
if (!prev)
{
list._first = next;
}
else
{
prev.next = next;
}
if (!next)
{
list._last = prev;
}
else
{
next.prev = prev;
}
list.@free_node(x);
list.size--;
}

388
lib/std/collections/list.c3 Normal file
View File

@@ -0,0 +1,388 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::list<Type>;
import std::math;
def ElementPredicate = fn bool(Type *type);
struct List
{
usz size;
usz capacity;
Allocator *allocator;
Type *entries;
}
/**
* @require using != null "A valid allocator must be provided"
**/
fn void List.init(List* list, usz initial_capacity = 16, Allocator* using = mem::heap())
{
list.allocator = using;
list.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
list.entries = malloc_aligned(Type, initial_capacity, .alignment = Type[1].alignof, .using = using)!!;
}
else
{
list.entries = null;
}
list.capacity = initial_capacity;
}
fn void List.tinit(List* list, usz initial_capacity = 16)
{
list.init(initial_capacity, mem::temp()) @inline;
}
fn void List.push(List* list, Type element) @inline
{
list.append(element);
}
fn void List.append(List* list, Type element)
{
list.ensure_capacity();
list.entries[list.size++] = element;
}
/**
* @require list.size > 0
*/
fn Type List.pop(List* list)
{
return list.entries[--list.size];
}
fn void List.clear(List* list)
{
list.size = 0;
}
/**
* @require list.size > 0
*/
fn Type List.pop_first(List* list)
{
Type value = list.entries[0];
list.remove_at(0);
return value;
}
fn void List.remove_at(List* list, usz index)
{
for (usz i = index + 1; i < list.size; i++)
{
list.entries[i - 1] = list.entries[i];
}
list.size--;
}
fn void List.add_all(List* list, List* other_list)
{
if (!other_list.size) return;
list.reserve(other_list.size);
foreach (&value : other_list)
{
list.entries[list.size++] = *value;
}
}
fn Type[] List.to_array(List* list, Allocator* using = mem::heap())
{
if (!list.size) return Type[] {};
Type[] result = malloc(Type, list.size, .using = using);
result[..] = list.entries[:list.size];
return result;
}
/**
* Reverse the elements in a list.
*
* @param [&inout] list "The list to reverse"
**/
fn void List.reverse(List* list)
{
if (list.size < 2) return;
usz half = list.size / 2U;
usz end = list.size - 1;
for (usz i = 0; i < half; i++)
{
@swap(list.entries[i], list.entries[end - i]);
}
}
fn Type[] List.array_view(List* list)
{
return list.entries[:list.size];
}
fn void List.add_array(List* list, Type[] array)
{
if (!array.len) return;
list.reserve(array.len);
foreach (&value : array)
{
list.entries[list.size++] = *value;
}
}
fn void List.push_front(List* list, Type type) @inline
{
list.insert_at(0, type);
}
fn void List.insert_at(List* list, usz index, Type type)
{
list.ensure_capacity();
for (usz i = list.size; i > index; i--)
{
list.entries[i] = list.entries[i - 1];
}
list.size++;
list.entries[index] = type;
}
/**
* @require index < list.size
**/
fn void List.set_at(List* list, usz index, Type type)
{
list.entries[index] = type;
}
fn void List.remove_last(List* list)
{
list.size--;
}
fn void List.remove_first(List* list)
{
list.remove_at(0);
}
fn Type* List.first(List* list)
{
return list.size ? &list.entries[0] : null;
}
fn Type* List.last(List* list)
{
return list.size ? &list.entries[list.size - 1] : null;
}
fn bool List.is_empty(List* list)
{
return !list.size;
}
fn usz List.len(List* list) @operator(len)
{
return list.size;
}
fn Type List.get(List* list, usz index)
{
return list.entries[index];
}
fn void List.free(List* list)
{
if (!list.allocator) return;
free_aligned(list.entries, .using = list.allocator);
list.capacity = 0;
list.size = 0;
list.entries = null;
}
fn void List.swap(List* list, usz i, usz j)
{
@swap(list.entries[i], list.entries[j]);
}
/**
* @param [&inout] list "The list to remove elements from"
* @param filter "The function to determine if it should be removed or not"
* @return "the number of deleted elements"
**/
fn usz List.remove_if(List* list, ElementPredicate filter)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (filter(&list.entries[i - 1])) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
/**
* @param [&inout] list "The list to remove elements from"
* @param selection "The function to determine if it should be kept or not"
* @return "the number of deleted elements"
**/
fn usz List.retain_if(List* list, ElementPredicate selection)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (!selection(&list.entries[i - 1])) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
/**
* Reserve at least min_capacity
**/
fn void List.reserve(List* list, usz min_capacity)
{
if (!min_capacity) return;
if (list.capacity >= min_capacity) return;
if (!list.allocator) list.allocator = mem::heap();
min_capacity = math::next_power_of_2(min_capacity);
list.entries = realloc_aligned(list.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof, .using = list.allocator) ?? null;
list.capacity = min_capacity;
}
macro Type List.@item_at(List &list, usz index) @operator([])
{
return list.entries[index];
}
fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
{
return &list.entries[index];
}
fn void List.ensure_capacity(List* list, usz added = 1) @inline @private
{
usz new_size = list.size + added;
if (list.capacity > new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = list.capacity ? 2U * list.capacity : 16U;
while (new_size >= new_capacity) new_capacity *= 2U;
list.reserve(new_capacity);
}
// Functions for equatable types
$if types::is_equatable_type(Type):
fn usz! List.index_of(List* list, Type type)
{
foreach (i, v : list)
{
if (v == type) return i;
}
return SearchResult.MISSING?;
}
fn usz! List.rindex_of(List* list, Type type)
{
foreach_r (i, v : list)
{
if (v == type) return i;
}
return SearchResult.MISSING?;
}
fn bool List.equals(List* list, List other_list)
{
if (list.size != other_list.size) return false;
foreach (i, v : list)
{
if (v != other_list.entries[i]) return false;
}
return true;
}
/**
* Check for presence of a value in a list.
*
* @param [&in] list "the list to find elements in"
* @param value "The value to search for"
* @return "True if the value is found, false otherwise"
**/
fn bool List.contains(List* list, Type value)
{
foreach (i, v : list)
{
if (v == value) return true;
}
return false;
}
/**
* @param [&inout] list "The list to remove elements from"
* @param value "The value to remove"
* @return "the number of deleted elements."
**/
fn usz List.remove(List* list, Type value)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (list.entries[i - 1] != value) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
fn void List.remove_all(List* list, List* other_list)
{
if (!other_list.size) return;
foreach (v : other_list) list.remove(v);
}
$endif
$if Type.kindof == POINTER:
/**
* @param [&in] list
* @return "The number non-null values in the list"
**/
fn usz List.compact_count(List* list)
{
usz vals = 0;
foreach (v : list) if (v) vals++;
return vals;
}
fn usz List.compact(List* list)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (list.entries[i - 1]) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
$endif

368
lib/std/collections/map.c3 Normal file
View File

@@ -0,0 +1,368 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::map<Key, Value>;
import std::math;
const uint DEFAULT_INITIAL_CAPACITY = 16;
const uint MAXIMUM_CAPACITY = 1u << 31;
const float DEFAULT_LOAD_FACTOR = 0.75;
struct HashMap
{
Entry*[] table;
Allocator* allocator;
uint count; // Number of elements
uint threshold; // Resize limit
float load_factor;
}
/**
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
* @require using != null "The allocator must be non-null"
**/
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* using = mem::heap())
{
capacity = math::next_power_of_2(capacity);
map.allocator = using;
map.load_factor = load_factor;
map.threshold = (uint)(capacity * load_factor);
map.table = calloc(Entry*, capacity, .using = using);
}
/**
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn void HashMap.tinit(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
map.init(capacity, load_factor, mem::temp());
}
/**
* Has this hash map been initialized yet?
*
* @param [&in] map "The hash map we are testing"
* @return "Returns true if it has been initialized, false otherwise"
**/
fn bool HashMap.is_initialized(HashMap* map)
{
return map.allocator != null;
}
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* using = mem::heap())
{
map.init(other_map.table.len, other_map.load_factor, using);
map.put_all_for_create(other_map);
}
fn void HashMap.tinit_from_map(HashMap* map, HashMap* other_map)
{
map.init_from_map(other_map, mem::temp()) @inline;
}
fn bool HashMap.is_empty(HashMap* map) @inline
{
return !map.count;
}
fn Value*! HashMap.get_ref(HashMap* map, Key key)
{
if (!map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return SearchResult.MISSING?;
}
fn Entry*! HashMap.get_entry(HashMap* map, Key key)
{
if (!map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return SearchResult.MISSING?;
}
/**
* Get the value or update and
**/
macro Value HashMap.@get_or_set(HashMap* map, Key key, Value #expr)
{
if (!map.count)
{
Value val = #expr;
map.set(key, val);
return val;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e.value;
}
Value val = #expr;
map.add_entry(hash, key, val, index);
return val;
}
fn Value! HashMap.get(HashMap* map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
fn bool HashMap.has_key(HashMap* map, Key key)
{
return @ok(map.get_ref(key));
}
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!map.allocator)
{
map.init();
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return true;
}
}
map.add_entry(hash, key, value, index);
return false;
}
fn void! HashMap.remove(HashMap* map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
}
fn void HashMap.clear(HashMap* map)
{
if (!map.count) return;
foreach (Entry** &entry_ref : map.table)
{
Entry* entry = *entry_ref;
if (!entry) continue;
map.free_internal(entry);
*entry_ref = null;
}
map.count = 0;
}
fn void HashMap.free(HashMap* map)
{
if (!map.allocator) return;
map.clear();
map.free_internal(map.table.ptr);
map.table = Entry*[] {};
}
fn Key[] HashMap.key_tlist(HashMap* map)
{
return map.key_list(mem::temp()) @inline;
}
fn Key[] HashMap.key_list(HashMap* map, Allocator* using = mem::heap())
{
if (!map.count) return Key[] {};
Key[] list = calloc(Key, map.count, .using = using);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.key;
entry = entry.next;
}
}
return list;
}
fn Value[] HashMap.value_tlist(HashMap* map)
{
return map.value_list(mem::temp()) @inline;
}
fn Value[] HashMap.value_list(HashMap* map, Allocator* using = mem::heap())
{
if (!map.count) return Value[] {};
Value[] list = calloc(Value, map.count, .using = using);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.value;
entry = entry.next;
}
}
return list;
}
$if types::is_equatable(Value):
fn bool HashMap.has_value(HashMap* map, Value v)
{
if (!map.count) return false;
foreach (Entry* entry : map.table)
{
while (entry)
{
if (equals(v, entry.value)) return true;
entry = entry.next;
}
}
return false;
}
$endif
// --- private methods
fn void HashMap.add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
{
Entry* entry = malloc(Entry, .using = map.allocator);
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
}
}
fn void HashMap.resize(HashMap* map, uint new_capacity) @private
{
Entry*[] old_table = map.table;
uint old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
map.threshold = uint.max;
return;
}
Entry*[] new_table = calloc(Entry*, new_capacity, .using = map.allocator);
map.transfer(new_table);
map.table = new_table;
map.free_internal(old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
fn uint rehash(uint hash) @inline @private
{
hash ^= (hash >> 20) ^ (hash >> 12);
return hash ^ ((hash >> 7) ^ (hash >> 4));
}
macro uint index_for(uint hash, uint capacity) @private
{
return hash & (capacity - 1);
}
fn void HashMap.transfer(HashMap* map, Entry*[] new_table) @private
{
Entry*[] src = map.table;
uint new_capacity = new_table.len;
foreach (uint j, Entry *e : src)
{
if (!e) continue;
do
{
Entry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void HashMap.put_all_for_create(HashMap* map, HashMap* other_map) @private
{
if (!other_map.count) return;
foreach (Entry *e : other_map.table)
{
if (!e) continue;
map.put_for_create(e.key, e.value);
}
}
fn void HashMap.put_for_create(HashMap* map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
for (Entry *e = map.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return;
}
}
map.create_entry(hash, key, value, i);
}
fn void HashMap.free_internal(HashMap* map, void* ptr) @inline @private
{
map.allocator.free(ptr)!!;
}
fn bool HashMap.remove_entry_for_key(HashMap* map, Key key) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
Entry* prev = map.table[i];
Entry* e = prev;
while (e)
{
Entry *next = e.next;
if (e.hash == hash && equals(key, e.key))
{
map.count--;
if (prev == e)
{
map.table[i] = next;
}
else
{
prev.next = next;
}
map.free_internal(e);
return true;
}
prev = e;
e = next;
}
return false;
}
fn void HashMap.create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
Entry* entry = malloc(Entry, .using = map.allocator);
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
map.table[bucket_index] = entry;
map.count++;
}
struct Entry
{
uint hash;
Key key;
Value value;
Entry* next;
}

View File

@@ -0,0 +1,474 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::object;
import std::collections::map;
import std::collections::list;
import std::io;
const Object TRUE_OBJECT = { .b = true, .type = bool.typeid };
const Object FALSE_OBJECT = { .b = false, .type = bool.typeid };
const Object NULL_OBJECT = { .type = void*.typeid };
struct Object
{
typeid type;
Allocator* allocator;
union
{
uint128 i;
double f;
bool b;
String s;
void* other;
ObjectInternalList array;
ObjectInternalMap map;
}
}
fn void! Object.to_format(Object* o, Formatter* formatter) @dynamic
{
switch (o.type)
{
case void:
formatter.printf("{}")!;
case void*:
formatter.printf("null")!;
case String:
formatter.printf(`"%s"`, o.s)!;
case bool:
formatter.printf(o.b ? "true" : "false")!;
case ObjectInternalList:
formatter.printf("[")!;
foreach (i, ol : o.array)
{
formatter.printf(i == 0 ? " " : ", ")!;
ol.to_format(formatter)!;
}
formatter.printf(" ]")!;
case ObjectInternalMap:
formatter.printf("{")!;
@pool()
{
foreach (i, key : o.map.key_tlist())
{
formatter.printf(i == 0 ? " " : ", ")!;
formatter.printf(`"%s": `, key)!;
o.map.get(key).to_format(formatter)!;
}
};
formatter.printf(" }")!;
default:
switch (o.type.kindof)
{
case SIGNED_INT:
formatter.printf("%d", o.i)!;
case UNSIGNED_INT:
formatter.printf("%d", (uint128)o.i)!;
case FLOAT:
formatter.printf("%d", o.f)!;
case ENUM:
formatter.printf("%d", o.i)!;
default:
formatter.printf("<>")!;
}
}
}
fn Object* new_obj(Allocator* using = mem::heap())
{
Object* o = malloc(Object, .using = using);
*o = { .allocator = using, .type = void.typeid };
return o;
}
fn Object* new_null()
{
return &NULL_OBJECT;
}
fn Object* new_int(int128 i, Allocator* using = mem::heap())
{
Object* o = malloc(Object, .using = using);
*o = { .i = i, .allocator = using, .type = int128.typeid };
return o;
}
macro Object* new_enum(e, Allocator* using = mem::heap())
{
Object* o = malloc(Object, .using = using);
*o = { .i = (int128)e, .allocator = using, .type = $typeof(e).typeid };
return o;
}
fn Object* new_float(double f, Allocator* using = mem::current_allocator())
{
Object* o = malloc(Object, .using = using);
*o = { .f = f, .allocator = using, .type = double.typeid };
return o;
}
fn Object* new_string(String s, Allocator* using = mem::heap())
{
Object* o = malloc(Object, .using = using);
*o = { .s = s.copy(using), .allocator = using, .type = String.typeid };
return o;
}
fn Object* new_bool(bool b)
{
return b ? &TRUE_OBJECT : &FALSE_OBJECT;
}
/**
* @param [&inout] o
**/
fn void Object.free(Object* o)
{
switch (o.type)
{
case void:
break;
case String:
free(o.s, .using = o.allocator);
case ObjectInternalList:
foreach (ol : o.array)
{
ol.free();
}
o.array.free();
case ObjectInternalMap:
@pool()
{
foreach (key : o.map.key_tlist())
{
o.map.get(key).free();
free(key, .using = o.allocator);
}
o.map.free();
};
default:
break;
}
if (o.allocator) free(o, .using = o.allocator);
}
fn bool Object.is_null(Object* this) @inline => this == &NULL_OBJECT;
fn bool Object.is_empty(Object* this) @inline => this.type == void.typeid;
fn bool Object.is_map(Object* this) @inline => this.type == ObjectInternalMap.typeid;
fn bool Object.is_array(Object* this) @inline => this.type == ObjectInternalList.typeid;
fn bool Object.is_bool(Object* this) @inline => this.type == bool.typeid;
fn bool Object.is_string(Object* this) @inline => this.type == String.typeid;
fn bool Object.is_float(Object* this) @inline => this.type == double.typeid;
fn bool Object.is_int(Object* this) @inline => this.type == int128.typeid;
fn bool Object.is_keyable(Object* this) => this.is_empty() || this.is_map();
fn bool Object.is_indexable(Object* this) => this.is_empty() || this.is_array();
/**
* @require o.is_keyable()
**/
fn void Object.init_map_if_needed(Object* o) @private
{
if (o.is_empty())
{
o.type = ObjectInternalMap.typeid;
o.map.init(.using = o.allocator);
}
}
/**
* @require o.is_indexable()
**/
fn void Object.init_array_if_needed(Object* o) @private
{
if (o.is_empty())
{
o.type = ObjectInternalList.typeid;
o.array.init(.using = o.allocator);
}
}
/**
* @require o.is_keyable()
**/
fn void Object.set_object(Object* o, String key, Object* new_object) @private
{
o.init_map_if_needed();
ObjectInternalMapEntry*! entry = o.map.get_entry(key);
defer
{
(void)free(entry.key, .using = o.allocator);
entry.value.free();
}
o.map.set(key.copy(o.map.allocator), new_object);
}
macro Object* object_from_value(value) @private
{
var $Type = $typeof(value);
$switch
$case types::is_int($Type):
return new_int(value);
$case types::is_float($Type):
return new_float(value);
$case $Type.typeid == String.typeid:
return new_string(value);
$case $Type.typeid == bool.typeid:
return new_bool(value);
$case $Type.typeid == Object*.typeid:
return value;
$case $Type.typeid == void*.typeid:
assert(value == null);
return &NULL_OBJECT;
$case $checks(String s = value):
return new_string(value);
$default:
$error "Unsupported object type.";
$endswitch
}
macro Object* Object.set(Object* o, String key, value)
{
Object* val = object_from_value(value);
o.set_object(key, val);
return val;
}
/**
* @require o.is_indexable()
**/
macro Object* Object.set_at(Object* o, usz index, String key, value)
{
Object* val = object_from_value(value);
o.set_object_at(key, index, val);
return val;
}
/**
* @require o.is_indexable()
* @ensure return != null
**/
macro Object* Object.append(Object* o, value)
{
Object* val = object_from_value(value);
o.append_object(val);
return val;
}
/**
* @require o.is_keyable()
**/
fn Object*! Object.get(Object* o, String key) => o.is_empty() ? SearchResult.MISSING? : o.map.get(key);
fn bool Object.has_key(Object* o, String key) => o.is_map() && o.map.has_key(key);
/**
* @require o.is_indexable()
**/
fn Object* Object.get_at(Object* o, usz index)
{
return o.array.get(index);
}
/**
* @require o.is_indexable()
**/
fn void Object.append_object(Object* o, Object* to_append)
{
o.init_array_if_needed();
o.array.append(to_append);
}
/**
* @require o.is_indexable()
**/
fn void Object.set_object_at(Object* o, usz index, Object* to_set)
{
o.init_array_if_needed();
while (o.array.len() < index)
{
o.array.append(&NULL_OBJECT);
}
if (o.array.len() == index)
{
o.array.append(to_set);
return;
}
o.array.get(index).free();
o.array.set_at(index, to_set);
}
macro get_integer_value(Object* value, $Type)
{
if (value.is_float())
{
return ($Type)value.f;
}
if (value.is_string())
{
$if $Type.kindof == TypeKind.SIGNED_INT:
return ($Type)value.s.to_int128();
$else
return ($Type)value.s.to_uint128();
$endif
}
if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER?;
return ($Type)value.i;
}
/**
* @require o.is_indexable()
**/
macro Object.get_integer_at(Object* o, $Type, usz index) @private
{
return get_integer_value(o.get_at(index), $Type);
}
/**
* @require o.is_keyable()
**/
macro Object.get_integer(Object* o, $Type, String key) @private
{
return get_integer_value(o.get(key), $Type);
}
fn ichar! Object.get_ichar(Object* o, String key) => o.get_integer(ichar, key);
fn short! Object.get_short(Object* o, String key) => o.get_integer(short, key);
fn int! Object.get_int(Object* o, String key) => o.get_integer(int, key);
fn long! Object.get_long(Object* o, String key) => o.get_integer(long, key);
fn int128! Object.get_int128(Object* o, String key) => o.get_integer(int128, key);
fn ichar! Object.get_ichar_at(Object* o, usz index) => o.get_integer_at(ichar, index);
fn short! Object.get_short_at(Object* o, usz index) => o.get_integer_at(short, index);
fn int! Object.get_int_at(Object* o, usz index) => o.get_integer_at(int, index);
fn long! Object.get_long_at(Object* o, usz index) => o.get_integer_at(long, index);
fn int128! Object.get_int128_at(Object* o, usz index) => o.get_integer_at(int128, index);
fn char! Object.get_char(Object* o, String key) => o.get_integer(ichar, key);
fn short! Object.get_ushort(Object* o, String key) => o.get_integer(ushort, key);
fn uint! Object.get_uint(Object* o, String key) => o.get_integer(uint, key);
fn ulong! Object.get_ulong(Object* o, String key) => o.get_integer(ulong, key);
fn uint128! Object.get_uint128(Object* o, String key) => o.get_integer(uint128, key);
fn char! Object.get_char_at(Object* o, usz index) => o.get_integer_at(char, index);
fn ushort! Object.get_ushort_at(Object* o, usz index) => o.get_integer_at(ushort, index);
fn uint! Object.get_uint_at(Object* o, usz index) => o.get_integer_at(uint, index);
fn ulong! Object.get_ulong_at(Object* o, usz index) => o.get_integer_at(ulong, index);
fn uint128! Object.get_uint128_at(Object* o, usz index) => o.get_integer_at(uint128, index);
/**
* @require o.is_keyable()
**/
fn String! Object.get_string(Object* o, String key)
{
Object* value = o.get(key)!;
assert(value.is_string());
return value.s;
}
/**
* @require o.is_indexable()
**/
fn String Object.get_string_at(Object* o, usz index)
{
Object* value = o.get_at(index);
assert(value.is_string());
return value.s;
}
/**
* @require o.is_keyable()
**/
macro String! Object.get_enum(Object* o, $EnumType, String key)
{
Object value = o.get(key)!;
assert($EnumType.typeid == value.type);
return ($EnumType)value.i;
}
/**
* @require o.is_indexable()
**/
macro String Object.get_enum_at(Object* o, $EnumType, usz index)
{
Object value = o.get_at(index);
assert($EnumType.typeid == value.type);
return ($EnumType)value.i;
}
/**
* @require o.is_keyable()
**/
fn bool! Object.get_bool(Object* o, String key)
{
Object* value = o.get(key)!;
assert(value.is_bool());
return value.b;
}
/**
* @require o.is_indexable()
**/
fn bool Object.get_bool_at(Object* o, usz index)
{
Object* value = o.get_at(index);
assert(value.is_bool());
return value.b;
}
/**
* @require o.is_keyable()
**/
fn double! Object.get_float(Object* o, String key)
{
Object* value = o.get(key)!;
switch (value.type.kindof)
{
case SIGNED_INT:
return (double)value.i;
case UNSIGNED_INT:
return (double)(uint128)value.i;
case FLOAT:
return value.f;
default:
unreachable();
}
}
/**
* @require o.is_indexable()
**/
fn double Object.get_float_at(Object* o, usz index)
{
Object* value = o.get_at(index);
switch (value.type.kindof)
{
case SIGNED_INT:
return (double)value.i;
case UNSIGNED_INT:
return (double)(uint128)value.i;
case FLOAT:
return value.f;
default:
unreachable();
}
}
fn Object* Object.get_or_create_obj(Object* o, String key)
{
if (try obj = o.get(key) && !obj.is_null()) return obj;
Object* container = new_obj();
o.set(key, container);
return container;
}
def ObjectInternalMap @private = HashMap<String, Object*>;
def ObjectInternalList @private = List<Object*>;
def ObjectInternalMapEntry @private = Entry<String, Object*>;

View File

@@ -0,0 +1,113 @@
// priorityqueue.c3
// A priority queue using a classic binary heap for C3.
//
// Copyright (c) 2022 David Kopec
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in 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:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// 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
// AUTHORS 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 IN THE
// SOFTWARE.
module std::collections::priorityqueue<Type>;
import std::collections::list;
def Heap = List<Type>;
struct PriorityQueue
{
Heap heap;
bool max; // true if max-heap, false if min-heap
}
fn void PriorityQueue.push(PriorityQueue* pq, Type element)
{
pq.heap.push(element);
usz i = pq.heap.len() - 1;
while (i > 0)
{
usz parent = (i - 1) / 2;
if ((pq.max && greater(pq.heap.get(i), pq.heap.get(parent))) || (!pq.max && less(pq.heap.get(i), pq.heap.get(parent))))
{
pq.heap.swap(i, parent);
i = parent;
continue;
}
break;
}
}
/**
* @require pq != null
*/
fn Type! PriorityQueue.pop(PriorityQueue* pq)
{
usz i = 0;
usz len = pq.heap.len() @inline;
if (!len) return IteratorResult.NO_MORE_ELEMENT?;
usz newCount = len - 1;
pq.heap.swap(0, newCount);
while ((2 * i + 1) < newCount)
{
usz j = 2 * i + 1;
if (((j + 1) < newCount) &&
((pq.max && greater(pq.heap.get(j + 1), pq.heap[j]))
|| (!pq.max && less(pq.heap.get(j + 1), pq.heap.get(j)))))
{
j++;
}
if ((pq.max && less(pq.heap.get(i), pq.heap.get(j))) || (!pq.max && greater(pq.heap.get(i), pq.heap.get(j))))
{
pq.heap.swap(i, j);
i = j;
continue;
}
break;
}
return pq.heap.pop();
}
/**
* @require pq != null
*/
fn Type! PriorityQueue.peek(PriorityQueue* pq)
{
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT?;
return pq.heap.get(0);
}
/**
* @require pq != null
*/
fn void PriorityQueue.free(PriorityQueue* pq)
{
pq.heap.free();
}
/**
* @require pq != null
*/
fn usz PriorityQueue.len(PriorityQueue* pq) @operator(len)
{
return pq.heap.len();
}
/**
* @require pq != null, index < pq.len()
*/
fn Type PriorityQueue.peek_at(PriorityQueue* pq, usz index) @operator([])
{
return pq.heap[index];
}

View File

@@ -0,0 +1,149 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
struct ArenaAllocator
{
inline Allocator allocator;
char[] data;
usz used;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void ArenaAllocator.init(ArenaAllocator* this, char[] data)
{
this.function = &arena_allocator_function;
this.data = data;
this.used = 0;
}
/**
* @require this != null
**/
fn void ArenaAllocator.reset(ArenaAllocator* this)
{
this.used = 0;
}
struct ArenaAllocatorHeader
{
usz size;
char[*] data;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
ArenaAllocator* arena = (ArenaAllocator*)data;
bool clear = false;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
clear = true;
nextcase;
case ALLOC:
case ALIGNED_ALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
alignment = alignment_for_allocation(alignment);
void* mem = arena._alloc(size, alignment, offset)!;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
case ALIGNED_REALLOC:
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
alignment = alignment_for_allocation(alignment);
return arena._realloc(old_pointer, size, alignment, offset)!;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
assert((uptr)old_pointer >= (uptr)arena.data.ptr, "Pointer originates from a different allocator.");
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
// Reclaim memory if it's the last element.
if (old_pointer + header.size == &arena.data[arena.used])
{
arena.used -= header.size + ArenaAllocatorHeader.sizeof;
}
return null;
case MARK:
return (void*)(uptr)arena.used;
case RESET:
arena.used = size;
return null;
}
unreachable();
}
/**
* @require alignment > 0 `alignment must be non zero`
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require this != null
**/
fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usz size, usz alignment, usz offset) @private
{
usz total_len = this.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
void* start_mem = this.data.ptr;
void* unaligned_pointer_to_offset = start_mem + this.used + ArenaAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - this.data.ptr) + size - offset;
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
this.used = end;
void* mem = aligned_pointer_to_offset - offset;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size;
return mem;
}
/**
* @require alignment > 0 `alignment must be non zero`
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require this != null
**/
fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz size, usz alignment, usz offset) @private
{
assert(old_pointer >= this.data.ptr, "Pointer originates from a different allocator.");
usz total_len = this.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
usz old_size = header.size;
// Do last allocation and alignment match?
if (&this.data[this.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer + offset, alignment))
{
if (old_size >= size)
{
this.used -= old_size - size;
}
else
{
usz new_used = this.used + size - old_size;
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY?;
this.used = new_used;
}
header.size = size;
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = this._alloc(size, alignment, offset)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -0,0 +1,247 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
struct DynamicArenaAllocator
{
inline Allocator allocator;
Allocator* backing_allocator;
DynamicArenaPage* page;
DynamicArenaPage* unused_page;
usz page_size;
}
/**
* @require page_size >= 128
* @require this != null
**/
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usz page_size, Allocator* using = mem::heap())
{
this.function = &dynamic_arena_allocator_function;
this.page = null;
this.unused_page = null;
this.page_size = page_size;
this.backing_allocator = using;
}
/**
* @require this != null
**/
fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this)
{
DynamicArenaPage* page = this.page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
free(page, .using = this.backing_allocator);
page = next_page;
}
page = this.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
free(page, .using = this.backing_allocator);
page = next_page;
}
this.page = null;
this.unused_page = null;
}
struct DynamicArenaPage
{
void* memory;
void* prev_arena;
usz total;
usz used;
void* last_ptr;
}
struct DynamicArenaChunk @local
{
usz size;
}
/**
* @require ptr && this
* @require this.page `tried to free pointer on invalid allocator`
*/
fn void DynamicArenaAllocator.free_ptr(DynamicArenaAllocator* this, void* ptr) @private
{
DynamicArenaPage* current_page = this.page;
if (ptr == current_page.last_ptr)
{
current_page.used = (usz)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory);
}
current_page.last_ptr = null;
}
/**
* @require old_pointer && size > 0
* @require this.page `tried to realloc pointer on invalid allocator`
*/
fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, void* old_pointer, usz size, usz alignment, usz offset) @local
{
DynamicArenaPage* current_page = this.page;
alignment = alignment_for_allocation(alignment);
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
usz old_size = *old_size_ptr;
// We have the old pointer and it's correctly aligned.
if (old_size >= size && mem::ptr_is_aligned(old_pointer, alignment))
{
*old_size_ptr = size;
if (current_page.last_ptr == old_pointer)
{
current_page.used = (usz)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory);
}
return old_pointer;
}
if REUSE: (current_page.last_ptr == old_pointer && mem::ptr_is_aligned(old_pointer, alignment))
{
assert(size > old_size);
usz add_size = size - old_size;
if (add_size + current_page.used > current_page.total) break REUSE;
*old_size_ptr = size;
current_page.used += add_size;
return old_pointer;
}
void* new_mem = this._alloc(size, alignment, offset)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this) @private
{
DynamicArenaPage* page = this.page;
DynamicArenaPage** unused_page_ptr = &this.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
page.used = 0;
DynamicArenaPage* prev_unused = *unused_page_ptr;
*unused_page_ptr = page;
page.prev_arena = prev_unused;
page = next_page;
}
this.page = page;
}
/**
* @require math::is_power_of_2(alignment)
* @require size > 0
*/
fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this, usz size, usz alignment, usz offset) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(this.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
// Grab the page without alignment (we do it ourselves)
void* mem = this.backing_allocator.alloc(page_size)!;
DynamicArenaPage*! page = malloc(DynamicArenaPage, .using = this.backing_allocator);
if (catch err = page)
{
free(mem, .using = this.backing_allocator);
return err?;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
assert(mem_start + size < mem + page_size);
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
chunk.size = size;
page.prev_arena = this.page;
page.total = page_size;
page.used = mem_start + size - page.memory;
this.page = page;
page.last_ptr = mem_start;
return mem_start;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require size > 0
* @require this
*/
fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usz size, usz alignment, usz offset) @local
{
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = this.page;
if (!page && this.unused_page)
{
this.page = page = this.unused_page;
this.unused_page = page.prev_arena;
page.prev_arena = null;
}
if (!page) return this._alloc_new(size, alignment, offset);
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
usz new_used = start - page.memory + size;
if ALLOCATE_NEW: (new_used > page.total)
{
if ((page = this.unused_page))
{
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
new_used = start + size - page.memory;
if (page.total >= new_used)
{
this.unused_page = page.prev_arena;
page.prev_arena = this.page;
this.page = page;
break ALLOCATE_NEW;
}
}
return this._alloc_new(size, alignment, offset);
}
page.used = new_used;
assert(start + size == page.memory + page.used);
void* mem = start;
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem - 1;
chunk.size = size;
return mem;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
DynamicArenaAllocator* allocator = (DynamicArenaAllocator*)data;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
assert(!old_pointer, "Unexpected no old pointer for calloc.");
if (!size) return null;
void* mem = allocator._alloc(size, alignment, offset)!;
mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
case ALLOC:
case ALIGNED_ALLOC:
assert(!old_pointer, "Unexpected no old pointer for alloc.");
if (!size) return null;
return allocator._alloc(size, alignment, offset);
case REALLOC:
case ALIGNED_REALLOC:
if (!size)
{
if (!old_pointer) return null;
allocator.free_ptr(old_pointer);
return null;
}
if (!old_pointer) return allocator._alloc(size, alignment, offset);
void* mem = allocator._realloc(old_pointer, size, alignment, offset)!;
return mem;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
allocator.free_ptr(old_pointer);
return null;
case MARK:
unreachable("Tried to mark a dynamic arena");
case RESET:
allocator.reset();
return null;
}
unreachable();
}

View File

@@ -0,0 +1,223 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
def MemoryAllocFn = fn char[]!(usz);
struct SimpleHeapAllocator
{
inline Allocator allocator;
MemoryAllocFn alloc_fn;
Header* free_list;
}
/**
* @require this "Unexpectedly missing the allocator"
* @require allocator "An underlying memory provider must be given"
* @require !this.free_list "The allocator may not be already initialized"
**/
fn void SimpleHeapAllocator.init(SimpleHeapAllocator* this, MemoryAllocFn allocator)
{
this.alloc_fn = allocator;
this.allocator = { &simple_heap_allocator_function };
this.free_list = null;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require this `unexpectedly missing the allocator`
*/
fn void*! simple_heap_allocator_function(Allocator* this, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
SimpleHeapAllocator* heap = (SimpleHeapAllocator*)this;
switch (kind)
{
case ALIGNED_ALLOC:
return @aligned_alloc(heap._alloc, size, alignment, offset);
case ALLOC:
return heap._alloc(size);
case ALIGNED_CALLOC:
return @aligned_calloc(heap._calloc, size, alignment, offset);
case CALLOC:
return heap._calloc(size);
case ALIGNED_REALLOC:
if (!size) nextcase ALIGNED_FREE;
if (!old_pointer) nextcase ALIGNED_CALLOC;
return @aligned_realloc(heap._calloc, heap._free, old_pointer, size, alignment, offset);
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase CALLOC;
return heap._realloc(old_pointer, size);
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION?;
case ALIGNED_FREE:
@aligned_free(heap._free, old_pointer)!;
return null;
case FREE:
heap._free(old_pointer);
return null;
default:
unreachable();
}
}
/**
* @require this && old_pointer && bytes > 0
**/
fn void*! SimpleHeapAllocator._realloc(SimpleHeapAllocator* this, void* old_pointer, usz bytes)
{
// Find the block header.
Header* block = (Header*)old_pointer - 1;
if (block.size >= bytes) return old_pointer;
void* new = this._alloc(bytes)!;
usz max_to_copy = math::min(block.size, bytes);
mem::copy(new, old_pointer, max_to_copy);
this._free(old_pointer);
return new;
}
fn void*! SimpleHeapAllocator._calloc(SimpleHeapAllocator* this, usz bytes) @local
{
void* data = this._alloc(bytes)!;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
fn void*! SimpleHeapAllocator._alloc(SimpleHeapAllocator* this, usz bytes) @local
{
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
if (!this.free_list)
{
this.add_block(aligned_bytes)!;
}
Header* current = this.free_list;
Header* previous = current;
while (current)
{
switch
{
case current.size >= aligned_bytes && current.size <= aligned_bytes + Header.sizeof + 64:
if (current == previous)
{
this.free_list = current.next;
}
else
{
previous.next = current.next;
}
current.next = null;
return current + 1;
case current.size > aligned_bytes:
Header* unallocated = (Header*)((char*)current + aligned_bytes + Header.sizeof);
unallocated.size = current.size - aligned_bytes;
unallocated.next = current.next;
if (current == this.free_list)
{
this.free_list = unallocated;
}
else
{
previous.next = unallocated;
}
current.size = aligned_bytes;
current.next = null;
return current + 1;
default:
previous = current;
current = current.next;
}
}
this.add_block(aligned_bytes)!;
return this.alloc(aligned_bytes);
}
fn void! SimpleHeapAllocator.add_block(SimpleHeapAllocator* this, usz aligned_bytes) @local
{
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
char[] result = this.alloc_fn(aligned_bytes + Header.sizeof)!;
Header* new_block = (Header*)result.ptr;
new_block.size = result.len - Header.sizeof;
new_block.next = null;
this._free(new_block + 1);
}
fn void SimpleHeapAllocator._free(SimpleHeapAllocator* this, void* ptr) @local
{
// Empty ptr -> do nothing.
if (!ptr) return;
// Find the block header.
Header* block = (Header*)ptr - 1;
// No free list? Then just return this.
if (!this.free_list)
{
this.free_list = block;
return;
}
// Find where in the list it should be inserted.
Header* current = this.free_list;
Header* prev = current;
while (current)
{
if (block < current)
{
// Between prev and current
if (block > prev) break;
// Before current
if (current == prev) break;
}
prev = current;
current = prev.next;
}
if (current)
{
// Insert after the current block.
// Are the blocks adjacent?
if (current == (Header*)((char*)(block + 1) + block.size))
{
// Merge
block.size += current.size + Header.sizeof;
block.next = current.next;
}
else
{
// Chain to current
block.next = current;
}
}
if (prev == current)
{
// Swap new start of free list
this.free_list = block;
}
else
{
// Prev adjacent?
if (block == (Header*)((char*)(prev + 1) + prev.size))
{
prev.size += block.size + Header.sizeof;
prev.next = block.next;
}
else
{
// Link prev to block
prev.next = block;
}
}
}
union Header @private
{
struct
{
Header* next;
usz size;
}
usz align;
}

View File

@@ -0,0 +1,136 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
import libc;
const Allocator _NULL_ALLOCATOR @private = { &null_allocator_fn };
const Allocator _SYSTEM_ALLOCATOR @private = { &libc_allocator_fn };
fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
switch (kind)
{
case ALLOC:
case CALLOC:
case REALLOC:
case ALIGNED_ALLOC:
case ALIGNED_REALLOC:
case ALIGNED_CALLOC:
return AllocationFailure.OUT_OF_MEMORY?;
default:
return null;
}
}
struct AlignedBlock
{
usz len;
void* start;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
$if $checks(#alloc_fn(bytes)!):
void* data = #alloc_fn(header + bytes)!;
$else
void* data = #alloc_fn(header + bytes);
$endif
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
assert(mem > data);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
*desc = { bytes, data };
return mem;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_calloc(#calloc_fn, usz bytes, usz alignment, usz offset)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
$if $checks(#calloc_fn(bytes)!):
void* data = #calloc_fn(header + bytes)!;
$else
void* data = #calloc_fn(header + bytes);
$endif
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
*desc = { bytes, data };
return mem;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment, usz offset)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)!;
mem::copy(new_data, old_pointer, desc.len > bytes ? desc.len : bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
$if $checks(#free_fn(data_start)!):
#free_fn(data_start)!;
$else
#free_fn(data_start);
$endif
return new_data;
}
macro void! @aligned_free(#free_fn, void* old_pointer)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
$if $checks(#free_fn(desc.start)!):
#free_fn(desc.start)!;
$else
#free_fn(desc.start);
$endif
}
fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @inline
{
if (!alignment) alignment = mem::DEFAULT_MEM_ALIGNMENT;
assert(math::is_power_of_2(alignment), "Alignment was not a power of 2");
void* data;
switch (kind)
{
case ALIGNED_ALLOC:
data = @aligned_alloc(libc::malloc, bytes, alignment, offset)!!;
case ALLOC:
data = libc::malloc(bytes);
case ALIGNED_CALLOC:
data = @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!!;
case CALLOC:
data = libc::calloc(bytes, 1);
case ALIGNED_REALLOC:
if (!bytes) nextcase ALIGNED_FREE;
if (!old_pointer) nextcase ALIGNED_CALLOC;
data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_pointer, bytes, alignment, offset)!!;
case REALLOC:
if (!bytes) nextcase FREE;
if (!old_pointer) nextcase CALLOC;
data = libc::realloc(old_pointer, bytes);
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION?;
case ALIGNED_FREE:
@aligned_free(libc::free, old_pointer)!!;
return null;
case FREE:
libc::free(old_pointer);
return null;
default:
unreachable();
}
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
return data;
}

View File

@@ -0,0 +1,235 @@
module std::core::mem::allocator;
struct OnStackAllocator
{
inline Allocator allocator;
Allocator* backing_allocator;
char[] data;
usz used;
OnStackAllocatorExtraChunk* chunk;
}
macro void @stack_mem(usz $size; @body(Allocator* mem)) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, mem::heap());
defer allocator.free();
@body(&allocator);
}
macro void @stack_pool(usz $size; @body) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, mem::heap());
defer allocator.free();
mem::@scoped(&allocator)
{
@body();
};
}
struct OnStackAllocatorExtraChunk @local
{
bool is_aligned;
OnStackAllocatorExtraChunk* prev;
void* data;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void OnStackAllocator.init(OnStackAllocator* this, char[] data, Allocator* using = mem::heap())
{
this.function = &on_stack_allocator_function;
this.data = data;
this.backing_allocator = using;
this.used = 0;
}
/**
* @require this != null
**/
fn void OnStackAllocator.free(OnStackAllocator* this)
{
OnStackAllocatorExtraChunk* chunk = this.chunk;
while (chunk)
{
if (chunk.is_aligned)
{
this.backing_allocator.free_aligned(chunk.data)!!;
}
else
{
this.backing_allocator.free(chunk.data)!!;
}
void* old = chunk;
chunk = chunk.prev;
this.backing_allocator.free(old)!!;
}
this.chunk = null;
this.used = 0;
}
struct OnStackAllocatorHeader
{
usz size;
char[*] data;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! on_stack_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
OnStackAllocator* allocator = (OnStackAllocator*)data;
bool clear = false;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
clear = true;
nextcase;
case ALLOC:
case ALIGNED_ALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
return on_stack_allocator_alloc(allocator, size, alignment, offset, clear, kind == AllocationKind.ALIGNED_ALLOC || kind == AllocationKind.ALIGNED_CALLOC);
case ALIGNED_REALLOC:
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
return on_stack_allocator_realloc(allocator, old_pointer, size, alignment, offset, kind == AllocationKind.ALIGNED_REALLOC);
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
if (allocation_in_stack_mem(allocator, old_pointer)) return null;
on_stack_allocator_remove_chunk(allocator, old_pointer);
if (kind == AllocationKind.ALIGNED_FREE)
{
allocator.backing_allocator.free_aligned(old_pointer)!;
}
else
{
allocator.backing_allocator.free(old_pointer)!;
}
return null;
case MARK:
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION?;
}
unreachable();
}
fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local
{
return ptr >= a.data.ptr && ptr <= &a.data[^1];
}
fn void on_stack_allocator_remove_chunk(OnStackAllocator* a, void* ptr) @local
{
OnStackAllocatorExtraChunk* chunk = a.chunk;
OnStackAllocatorExtraChunk** addr = &a.chunk;
while (chunk)
{
if (chunk.data == ptr)
{
*addr = chunk.prev;
a.backing_allocator.free(chunk)!!;
return;
}
addr = &chunk.prev;
chunk = *addr;
}
unreachable("Missing chunk");
}
fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a, void* ptr) @local
{
OnStackAllocatorExtraChunk* chunk = a.chunk;
while (chunk)
{
if (chunk.data == ptr) return chunk;
chunk = chunk.prev;
}
return null;
}
/**
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require a != null
**/
fn void*! on_stack_allocator_realloc(OnStackAllocator* a, void* old_pointer, usz size, usz alignment, usz offset, bool aligned) @local @inline
{
if (!allocation_in_stack_mem(a, old_pointer))
{
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(a, old_pointer);
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
if (aligned)
{
return chunk.data = a.backing_allocator.realloc_aligned(old_pointer, size, alignment, offset)!;
}
return chunk.data = a.backing_allocator.realloc(old_pointer, size)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = on_stack_allocator_alloc(a, size, alignment, offset, true, aligned)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
import std::io;
/**
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require a != null
**/
fn void*! on_stack_allocator_alloc(OnStackAllocator* a, usz size, usz alignment, usz offset, bool clear, bool aligned) @local @inline
{
alignment = alignment_for_allocation(alignment);
usz total_len = a.data.len;
void* start_mem = a.data.ptr;
void* unaligned_pointer_to_offset = start_mem + a.used + OnStackAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - a.data.ptr) + size - offset;
Allocator* backing_allocator = a.backing_allocator;
if (end > total_len)
{
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc(OnStackAllocatorExtraChunk.sizeof)!;
defer catch backing_allocator.free(chunk)!!;
defer try a.chunk = chunk;
*chunk = { .prev = a.chunk, .is_aligned = aligned };
void* data @noinit;
switch
{
case !aligned && !clear:
data = backing_allocator.alloc(size)!;
case aligned && !clear:
data = backing_allocator.alloc_aligned(size, alignment, offset)!;
case !aligned && clear:
data = backing_allocator.calloc(size)!;
case aligned && clear:
data = backing_allocator.calloc_aligned(size, alignment, offset)!;
}
return chunk.data = data;
}
a.used = end;
void *mem = aligned_pointer_to_offset - offset;
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
header.size = size;
return mem;
}

View File

@@ -0,0 +1,254 @@
module std::core::mem::allocator;
import std::io;
struct TempAllocatorChunk @local
{
usz size;
char[*] data;
}
struct TempAllocator
{
inline Allocator allocator;
Allocator* backing_allocator;
TempAllocatorPage* last_page;
usz used;
usz capacity;
char[*] data;
}
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
struct TempAllocatorPage
{
TempAllocatorPage* prev_page;
void* start;
usz mark;
usz size;
usz ident;
char[*] data;
}
macro usz TempAllocatorPage.pagesize(TempAllocatorPage* page) => page.size & ~PAGE_IS_ALIGNED;
macro bool TempAllocatorPage.is_aligned(TempAllocatorPage* page) => page.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED;
/**
* @require size >= 16
**/
fn TempAllocator*! new_temp(usz size, Allocator* using)
{
TempAllocator* allocator = malloc_checked(TempAllocator, .using = using, .end_padding = size)!;
allocator.last_page = null;
allocator.function = &temp_allocator_function;
allocator.backing_allocator = using;
allocator.used = 0;
allocator.capacity = size;
return allocator;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! temp_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
TempAllocator* arena = (TempAllocator*)data;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
return arena._alloc(size, alignment_for_allocation(alignment), offset, true);
case ALLOC:
case ALIGNED_ALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
return arena._alloc(size, alignment_for_allocation(alignment), offset, false);
case ALIGNED_REALLOC:
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
return arena._realloc(old_pointer, size, alignment_for_allocation(alignment), offset);
case FREE:
case ALIGNED_FREE:
if (!old_pointer) return null;
arena._free(old_pointer)!;
return null;
case MARK:
return (void*)(uptr)arena.used;
case RESET:
arena._reset(size)!;
return null;
}
unreachable();
}
fn void! TempAllocator._free(TempAllocator* this, void* old_pointer) @local
{
// TODO fix free
assert((uptr)old_pointer >= (uptr)&this.data, "Pointer originates from a different allocator.");
usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX);
if (old_pointer + old_size == &this.data[this.used])
{
this.used -= old_size;
}
}
fn void! TempAllocator._reset(TempAllocator* this, usz mark) @local
{
TempAllocatorPage *last_page = this.last_page;
while (last_page && last_page.mark > mark)
{
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
this._free_page(to_free)!;
}
this.last_page = last_page;
this.used = mark;
}
fn void! TempAllocator._free_page(TempAllocator* this, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
if (page.is_aligned()) return this.backing_allocator.free_aligned(mem);
return this.backing_allocator.free(mem);
}
fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
// Walk backwards to find the pointer to this page.
TempAllocatorPage **pointer_to_prev = &this.last_page;
// Remove the page from the list
while (*pointer_to_prev != page)
{
pointer_to_prev = &((*pointer_to_prev).prev_page);
}
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = this._alloc(size, alignment, offset, false)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
if (page.is_aligned())
{
this.backing_allocator.free_aligned(real_pointer)!;
}
else
{
this.backing_allocator.free(real_pointer)!;
}
return data;
}
fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz size, usz alignment, usz offset) @inline @local
{
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1)
{
assert(this.last_page, "Realloc of non temp pointer");
// First grab the page
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
return this._realloc_page(page, size, alignment, offset);
}
// TODO optimize last allocation
TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
/**
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require this != null
**/
fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz offset, bool clear) @local
{
void* start_mem = &this.data;
void* starting_ptr = start_mem + this.used;
void* aligned_header_start = mem::aligned_pointer(starting_ptr, TempAllocatorChunk.alignof);
void* mem = aligned_header_start + TempAllocatorChunk.sizeof;
if (alignment > TempAllocatorChunk.alignof)
{
mem = mem::aligned_pointer(mem + offset, alignment) - offset;
}
usz new_usage = (usz)(mem - start_mem) + size;
// Arena alignment, simple!
if (new_usage <= this.capacity)
{
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
chunk_start.size = size;
this.used = new_usage;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
// Fallback to backing allocator
TempAllocatorPage* page;
// We have something we need to align.
if (alignment > mem::DEFAULT_MEM_ALIGNMENT || offset)
{
// This is actually simpler, since it will create the offset for us.
usz total_alloc_size = TempAllocatorPage.sizeof + size;
if (clear)
{
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
}
else
{
page = this.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
}
page.start = page;
page.size = size | PAGE_IS_ALIGNED;
}
else
{
// Here we might need to pad
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
usz total_alloc_size = padded_header_size + size;
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))!;
// Find the page.
page = alloc + padded_header_size - TempAllocatorPage.sizeof;
assert(mem::ptr_is_aligned(page, TempAllocator.alignof));
assert(mem::ptr_is_aligned(&page.data[0], mem::DEFAULT_MEM_ALIGNMENT));
page.start = alloc;
page.size = size;
}
// Mark it as a page
page.ident = ~(usz)0;
// Store when it was created
page.mark = ++this.used;
// Hook up the page.
page.prev_page = this.last_page;
this.last_page = page;
return &page.data[0];
}
fn void TempAllocator.print_pages(TempAllocator* this, File f)
{
TempAllocatorPage *last_page = this.last_page;
if (!last_page)
{
f.printf("No pages.\n");
return;
}
f.printf("---Pages----\n");
uint index = 0;
while (last_page)
{
bool is_not_aligned = !(last_page.size & (1u64 << 63));
f.printf("%d. Alloc: %d %d at %p%s\n", ++index,
last_page.size & ~(1u64 << 63), last_page.mark, &last_page.data[0], is_not_aligned ? "" : " [aligned]");
last_page = last_page.prev_page;
}
}

View File

@@ -0,0 +1,105 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
import std::collections::map;
def PtrMap = HashMap<uptr, usz>;
// A simple tracking allocator.
// It tracks allocations using a hash map but
// is not compatible with allocators that uses mark()
struct TrackingAllocator
{
inline Allocator allocator;
Allocator* inner_allocator;
PtrMap map;
usz mem_total;
usz allocs_total;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void TrackingAllocator.init(TrackingAllocator* this, Allocator* using)
{
*this = { .inner_allocator = using, .allocator.function = &tracking_allocator_fn };
this.map.init(.using = using);
}
fn void TrackingAllocator.free(TrackingAllocator* this)
{
this.map.free();
*this = {};
}
/**
* @param [inout] data
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! tracking_allocator_fn(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
TrackingAllocator* this = (TrackingAllocator*)data;
void* result = this.inner_allocator.function(this.inner_allocator, size, alignment, offset, old_pointer, kind)!;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
case ALLOC:
case ALIGNED_ALLOC:
this.map.set((uptr)result, size);
this.mem_total += size;
this.allocs_total++;
return result;
case REALLOC:
case ALIGNED_REALLOC:
this.map.remove((uptr)old_pointer);
this.map.set((uptr)result, size);
this.mem_total += size;
if (size > 0) this.allocs_total++;
return result;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
this.map.remove((uptr)old_pointer);
return null;
case MARK:
// Unsupported
return null;
case RESET:
this.map.clear();
return null;
}
unreachable();
}
fn usz TrackingAllocator.allocated(TrackingAllocator* this)
{
usz allocated = 0;
@pool()
{
foreach (usz allocation : this.map.value_tlist())
{
allocated += allocation;
}
};
return allocated;
}
fn usz TrackingAllocator.total_allocated(TrackingAllocator* this)
{
return this.mem_total;
}
fn usz TrackingAllocator.total_allocation_count(TrackingAllocator* this)
{
return this.allocs_total;
}
fn usz TrackingAllocator.allocation_count(TrackingAllocator* this)
{
return this.map.count;
}

70
lib/std/core/array.c3 Normal file
View File

@@ -0,0 +1,70 @@
module std::core::array;
/**
* @param [in] array
* @param [in] element
* @return "the first index of the element"
* @return! SearchResult.MISSING
**/
macro index_of(array, element)
{
foreach (i, &e : array)
{
if (*e == element) return i;
}
return SearchResult.MISSING?;
}
/**
* @param [in] array
* @param [in] element
* @return "the last index of the element"
* @return! SearchResult.MISSING
**/
macro rindex_of(array, element)
{
foreach_r (i, &e : array)
{
if (*e == element) return i;
}
return SearchResult.MISSING?;
}
/**
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them.
*
* @param [in] arr1
* @param [in] arr2
* @param [&inout] using "The allocator to use, default is the heap allocator"
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
* @require @typekind(arr2) == SUBARRAY || @typekind(arr2) == ARRAY
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
* @ensure result.len == arr1.len + arr2.len
**/
macro concat(arr1, arr2, Allocator* using = mem::heap())
{
var $Type = $typeof(arr1[0]);
$Type[] result = malloc($Type, arr1.len + arr2.len, .using = using);
if (arr1.len > 0)
{
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
}
if (arr2.len > 0)
{
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof);
}
return result;
}
/**
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them,
* allocated using the temp allocator.
*
* @param [in] arr1
* @param [in] arr2
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
* @require @typekind(arr2) == SUBARRAY || @typekind(arr2) == ARRAY
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
* @ensure result.len == arr1.len + arr2.len
**/
macro tconcat(arr1, arr2) => concat(arr1, arr2, mem::temp());

89
lib/std/core/bitorder.c3 Normal file
View File

@@ -0,0 +1,89 @@
// Copyright (c) 2023 Christoffer Lerno and contributors. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::bitorder;
// This module contains types of different endianness.
// *BE types represent big-endian types
// *LE types represent little-endian types.
bitstruct ShortBE : short @bigendian
{
short val : 0..15;
}
bitstruct UShortBE : ushort @bigendian
{
ushort val : 0..15;
}
bitstruct IntBE : int @bigendian
{
int val : 0..31;
}
bitstruct UIntBE : int @bigendian
{
uint val : 0..31;
}
bitstruct LongBE : long @bigendian
{
long val : 0..63;
}
bitstruct ULongBE : ulong @bigendian
{
ulong val : 0..63;
}
bitstruct Int128BE : int128 @bigendian
{
int128 val : 0..127;
}
bitstruct UInt128BE : uint128 @bigendian
{
uint128 val : 0..127;
}
bitstruct ShortLE : short @littleendian
{
short val : 0..15;
}
bitstruct UShortLE : ushort @littleendian
{
ushort val : 0..15;
}
bitstruct IntLE : int @littleendian
{
int val : 0..31;
}
bitstruct UIntLE : int @littleendian
{
uint val : 0..31;
}
bitstruct LongLE : long @littleendian
{
long val : 0..63;
}
bitstruct ULongLE : ulong @littleendian
{
ulong val : 0..63;
}
bitstruct Int128LE : int128 @littleendian
{
int128 val : 0..127;
}
bitstruct UInt128LE : uint128 @littleendian
{
uint128 val : 0..127;
}

290
lib/std/core/builtin.c3 Normal file
View File

@@ -0,0 +1,290 @@
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::builtin;
import libc;
import std::hash;
/**
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
**/
fault IteratorResult
{
NO_MORE_ELEMENT
}
/**
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
**/
fault SearchResult
{
MISSING
}
/**
* Use `CastResult` when an attempt at conversion fails.
**/
fault CastResult
{
TYPE_MISMATCH
}
/**
* Stores a variable on the stack, then restores it at the end of the
* macro scope.
*
* @param variable `the variable to store and restore`
**/
macro void @scope(&variable; @body) @builtin
{
var temp = variable;
defer variable = temp;
@body();
}
/**
* Swap two variables
**/
macro void @swap(&a, &b) @builtin
{
var temp = a;
a = b;
b = temp;
}
/**
* Convert an `any` type to a type, returning an failure if there is a type mismatch.
*
* @param v `the any to convert to the given type.`
* @param $Type `the type to convert to`
* @return `The any.ptr converted to its type.`
* @ensure @typeis(return, $Type*)
* @return! CastResult.TYPE_MISMATCH
**/
macro anycast(any v, $Type) @builtin
{
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
return ($Type*)v.ptr;
}
struct CallstackElement
{
CallstackElement* prev;
String function;
String file;
uint line;
}
fn void default_panic(String message, String file, String function, uint line)
{
CallstackElement* stack = $$stacktrace();
$if $defined(io::stderr) && $defined(File.printf):
if (stack) stack = stack.prev;
if (stack)
{
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
(void)io::stderr().printn("'");
}
else
{
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
(void)io::stderr().printfn("', in function %s (%s:%d)", function, file, line);
}
while (stack)
{
(void)io::stderr().printfn(" in function %s (%s:%d)", stack.function, stack.file, stack.line);
if (stack == stack.prev) break;
stack = stack.prev;
}
$endif
$$trap();
}
def PanicFn = fn void(String message, String file, String function, uint line);
PanicFn panic = &default_panic;
fn void panicf(String fmt, String file, String function, uint line, args...)
{
@stack_mem(512; Allocator* mem)
{
DString s;
s.init(.using = mem);
s.printf(fmt, ...args);
panic(s.str(), file, function, line);
};
}
/**
* Marks the path as unreachable. This will panic in safe mode, and in fast will simply be assumed
* never happens.
* @param [in] string "The panic message or format string"
**/
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
$$unreachable();
}
/**
* Marks the path as unsupported, this is similar to unreachable.
* @param [in] string "The error message"
**/
macro void unsupported(String string = "Unsupported function invoked") @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
$$unreachable();
}
/**
* @param expr "the expression to cast"
* @param $Type "the type to cast to"
*
* @require $sizeof(expr) == $Type.sizeof "Cannot bitcast between types of different size."
* @ensure @typeis(result, $Type)
**/
macro bitcast(expr, $Type) @builtin
{
usz $size = $sizeof(expr);
$Type x @noinit;
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
return x;
}
/**
* @param $Type `The type of the enum`
* @param [in] enum_name `The name of the enum to search for`
* @require $Type.kindof == ENUM `Only enums may be used`
* @ensure @typeis(return, $Type)
* @return! SearchResult.MISSING
**/
macro enum_by_name($Type, String enum_name) @builtin
{
typeid x = $Type.typeid;
foreach (i, name : x.names)
{
if (name == enum_name) return ($Type)i;
}
return SearchResult.MISSING?;
}
/**
* Mark an expression as likely to be true
*
* @param #value "expression to be marked likely"
* @param $probability "in the range 0 - 1"
* @require $probability >= 0 && $probability <= 1.0
**/
macro bool @likely(bool #value, $probability = 1.0) @builtin
{
$if $probability == 1.0:
return $$expect(#value, true);
$else
return $$expect_with_probability(#value, true, $probability);
$endif
}
/**
* Mark an expression as unlikely to be true
*
* @param #value "expression to be marked unlikely"
* @param $probability "in the range 0 - 1"
* @require $probability >= 0 && $probability <= 1.0
**/
macro bool @unlikely(bool #value, $probability = 1.0) @builtin
{
$if $probability == 1.0:
return $$expect(#value, false);
$else
return $$expect_with_probability(#value, false, $probability);
$endif
}
/**
* @require values::@is_int(#value) || values::@is_bool(#value)
* @checked $typeof(#value) a = expected
* @require $probability >= 0 && $probability <= 1.0
**/
macro @expect(#value, expected, $probability = 1.0) @builtin
{
$if $probability == 1.0:
return $$expect(#value, ($typeof(#value))expected);
$else
return $$expect_with_probability(#value, expected, $probability);
$endif
}
/**
* Locality for prefetch, levels 0 - 3, corresponding
* to "extremely local" to "no locality"
**/
enum PrefetchLocality
{
NO_LOCALITY,
FAR,
NEAR,
VERY_NEAR,
}
/**
* Prefetch a pointer.
* @param [in] ptr `Pointer to prefetch`
* @param $locality `Locality ranging from none to extremely local`
* @param $write `Prefetch for write, otherwise prefetch for read.`
**/
macro prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write = false) @builtin
{
$$prefetch(ptr, $write ? 1 : 0, $locality.ordinal);
}
macro swizzle(v, ...) @builtin
{
return $$swizzle(v, $vasplat());
}
macro swizzle2(v, v2, ...) @builtin
{
return $$swizzle2(v, v2, $vasplat());
}
macro bool @castable(#expr, $To) @builtin
{
return $checks(($To)#expr);
}
macro bool @convertible(#expr, $To) @builtin
{
return $checks($To x = #expr);
}
macro anyfault @catchof(#expr) @builtin
{
if (catch f = #expr) return f;
return anyfault {};
}
macro bool @ok(#expr) @builtin
{
if (catch #expr) return false;
return true;
}
macro uint int.hash(int i) => i;
macro uint uint.hash(uint i) => i;
macro uint short.hash(short s) => s;
macro uint ushort.hash(ushort s) => s;
macro uint char.hash(char c) => c;
macro uint ichar.hash(ichar c) => c;
macro uint long.hash(long i) => (uint)((i >> 32) ^ i);
macro uint ulong.hash(ulong i) => (uint)((i >> 32) ^ i);
macro uint int128.hash(int128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
macro uint uint128.hash(uint128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
macro uint bool.hash(bool b) => (uint)b;
macro uint typeid.hash(typeid t) => ((ulong)(uptr)t).hash();
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);

View File

@@ -0,0 +1,114 @@
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::builtin;
/**
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
**/
macro less(a, b) @builtin
{
$switch
$case $defined(a.less):
return a.less(b);
$case $defined(a.compare_to):
return a.compare_to(b) < 0;
$default:
return a < b;
$endswitch
}
/**
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
**/
macro less_eq(a, b) @builtin
{
$switch
$case $defined(a.less):
return !b.less(a);
$case $defined(a.compare_to):
return a.compare_to(b) <= 0;
$default:
return a <= b;
$endswitch
}
/**
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
**/
macro greater(a, b) @builtin
{
$switch
$case $defined(a.less):
return b.less(a);
$case $defined(a.compare_to):
return a.compare_to(b) > 0;
$default:
return a > b;
$endswitch
}
/**
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
**/
macro greater_eq(a, b) @builtin
{
$switch
$case $defined(a.less):
return !a.less(b);
$case $defined(a.compare_to):
return a.compare_to(b) >= 0;
$default:
return a >= b;
$endswitch
}
/**
* @require types::is_equatable_value(a) && types::is_equatable_value(b) `values must be equatable`
**/
macro bool equals(a, b) @builtin
{
$switch
$case $defined(a.equals):
return a.equals(b);
$case $defined(a.compare_to):
return a.compare_to(b) == 0;
$case $defined(a.less):
return !a.less(b) && !b.less(a);
$default:
return a == b;
$endswitch
}
macro min(x, ...) @builtin
{
$if $vacount == 1:
return less(x, $vaarg(0)) ? x : $vaarg(0);
$else
var result = x;
$for (var $i = 0; $i < $vacount; $i++)
if (less($vaarg($i), result))
{
result = $vaarg($i);
}
$endfor
return result;
$endif
}
macro max(x, ...) @builtin
{
$if $vacount == 1:
return greater(x, $vaarg(0)) ? x : $vaarg(0);
$else
var result = x;
$for (var $i = 0; $i < $vacount; $i++)
if (greater($vaarg($i), result))
{
result = $vaarg($i);
}
$endfor
return result;
$endif
}

87
lib/std/core/cinterop.c3 Normal file
View File

@@ -0,0 +1,87 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::cinterop;
const C_INT_SIZE = $$C_INT_SIZE;
const C_LONG_SIZE = $$C_LONG_SIZE;
const C_SHORT_SIZE = $$C_SHORT_SIZE;
const C_LONG_LONG_SIZE = $$C_LONG_LONG_SIZE;
$assert C_SHORT_SIZE < 32;
$assert C_INT_SIZE < 128;
$assert C_LONG_SIZE < 128;
$assert C_LONG_LONG_SIZE <= 128;
$assert C_SHORT_SIZE <= C_INT_SIZE;
$assert C_INT_SIZE <= C_LONG_SIZE;
$assert C_LONG_SIZE <= C_LONG_LONG_SIZE;
$switch ($$C_INT_SIZE)
$case 64:
def CInt = long;
def CUInt = ulong;
$case 32:
def CInt = int;
def CUInt = uint;
$case 16:
def CInt = short;
def CUInt = ushort;
$default:
$error "Invalid C int size";
$endswitch
$switch ($$C_LONG_SIZE)
$case 64:
def CLong = long;
def CULong = ulong;
$case 32:
def CLong = int;
def CULong = uint;
$case 16:
def CLong = short;
def CULong = ushort;
$default:
$error "Invalid C long size";
$endswitch
$switch ($$C_SHORT_SIZE)
$case 32:
def CShort = int;
def CUShort = uint;
$case 16:
def CShort = short;
def CUShort = ushort;
$case 8:
def CShort = ichar;
def CUShort = char;
$default:
$error "Invalid C short size";
$endswitch
$switch ($$C_LONG_LONG_SIZE)
$case 128:
def CLongLong = int128;
def CULongLong = uint128;
$case 64:
def CLongLong = long;
def CULongLong = ulong;
$case 32:
def CLongLong = int;
def CULongLong = uint;
$case 16:
def CLongLong = short;
def CULongLong = ushort;
$default:
$error "Invalid C long long size";
$endswitch
def CSChar = ichar;
def CUChar = char;
$if $$C_CHAR_IS_SIGNED:
def CChar = ichar;
$else
def CChar = char;
$endif

415
lib/std/core/conv.c3 Normal file
View File

@@ -0,0 +1,415 @@
module std::core::string::conv;
const uint UTF16_SURROGATE_OFFSET @private = 0x10000;
const uint UTF16_SURROGATE_GENERIC_MASK @private = 0xF800;
const uint UTF16_SURROGATE_GENERIC_VALUE @private = 0xD800;
const uint UTF16_SURROGATE_MASK @private = 0xFC00;
const uint UTF16_SURROGATE_CODEPOINT_MASK @private = 0x03FF;
const uint UTF16_SURROGATE_BITS @private = 10;
const uint UTF16_SURROGATE_LOW_VALUE @private = 0xDC00;
const uint UTF16_SURROGATE_HIGH_VALUE @private = 0xD800;
/**
* @param c `The utf32 codepoint to convert`
* @param [out] output `the resulting buffer`
* @param available `the size available`
**/
fn usz! char32_to_utf8(Char32 c, char* output, usz available)
{
if (!available) return UnicodeResult.CONVERSION_FAILED?;
switch (true)
{
case c <= 0x7f:
output[0] = (char)c;
return 1;
case c <= 0x7ff:
if (available < 2) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xC0 | c >> 6);
output[1] = (char)(0x80 | (c & 0x3F));
return 2;
case c <= 0xffff:
if (available < 3) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xE0 | c >> 12);
output[1] = (char)(0x80 | (c >> 6 & 0x3F));
output[2] = (char)(0x80 | (c & 0x3F));
return 3;
case c <= 0x10ffff:
if (available < 4) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xF0 | c >> 18);
output[1] = (char)(0x80 | (c >> 12 & 0x3F));
output[2] = (char)(0x80 | (c >> 6 & 0x3F));
output[3] = (char)(0x80 | (c & 0x3F));
return 4;
default:
// 0x10FFFF and above is not defined.
return UnicodeResult.CONVERSION_FAILED?;
}
}
/**
* Convert a code pointer into 1-2 UTF16 characters.
*
* @param c `The character to convert.`
* @param [inout] output `the resulting UTF16 buffer to write to.`
**/
fn void char32_to_utf16_unsafe(Char32 c, Char16** output)
{
if (c < UTF16_SURROGATE_OFFSET)
{
(*output)++[0] = (Char16)c;
return;
}
c -= UTF16_SURROGATE_OFFSET;
Char16 low = (Char16)(UTF16_SURROGATE_LOW_VALUE | (c & UTF16_SURROGATE_CODEPOINT_MASK));
c >>= UTF16_SURROGATE_BITS;
Char16 high = (Char16)(UTF16_SURROGATE_HIGH_VALUE | (c & UTF16_SURROGATE_CODEPOINT_MASK));
(*output)++[0] = (Char16)high;
(*output)++[0] = (Char16)low;
}
/**
* Convert 1-2 UTF16 data points into UTF8.
*
* @param [in] ptr `The UTF16 data to convert.`
* @param [inout] available `amount of UTF16 data available.`
* @param [inout] output `the resulting utf8 buffer to write to.`
**/
fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
{
Char16 high = *ptr;
if (high & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
{
char32_to_utf8_unsafe(high, output);
*available = 1;
return;
}
// Low surrogate first is an error
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16?;
// Unmatched high surrogate is an error
if (*available == 1) return UnicodeResult.INVALID_UTF16?;
Char16 low = ptr[1];
// Unmatched high surrogate, invalid
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16?;
// The high bits of the codepoint are the value bits of the high surrogate
// The low bits of the codepoint are the value bits of the low surrogate
Char32 uc = (high & UTF16_SURROGATE_CODEPOINT_MASK) << UTF16_SURROGATE_BITS
| (low & UTF16_SURROGATE_CODEPOINT_MASK) + UTF16_SURROGATE_OFFSET;
char32_to_utf8_unsafe(uc, output);
*available = 2;
}
/**
* @param c `The utf32 codepoint to convert`
* @param [inout] output `the resulting buffer`
**/
fn void char32_to_utf8_unsafe(Char32 c, char** output)
{
switch (true)
{
case c < 0x7f:
(*output)++[0] = (char)c;
case c < 0x7ff:
(*output)++[0] = (char)(0xC0 | c >> 6);
(*output)++[0] = (char)(0x80 | (c & 0x3F));
case c < 0xffff:
(*output)++[0] = (char)(0xE0 | c >> 12);
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
(*output)++[0] = (char)(0x80 | (c & 0x3F));
default:
(*output)++[0] = (char)(0xF0 | c >> 18);
(*output)++[0] = (char)(0x80 | (c >> 12 & 0x3F));
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
(*output)++[0] = (char)(0x80 | (c & 0x3F));
}
}
/**
* @param [in] ptr `pointer to the first character to parse`
* @param [inout] size `Set to max characters to read, set to characters read`
* @return `the parsed 32 bit codepoint`
**/
fn Char32! utf8_to_char32(char* ptr, usz* size)
{
usz max_size = *size;
if (max_size < 1) return UnicodeResult.INVALID_UTF8?;
char c = (ptr++)[0];
if ((c & 0x80) == 0)
{
*size = 1;
return c;
}
if ((c & 0xE0) == 0xC0)
{
if (max_size < 2) return UnicodeResult.INVALID_UTF8?;
*size = 2;
Char32 uc = (c & 0x1F) << 6;
c = *ptr;
// Overlong sequence or invalid second.
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
return uc + c & 0x3F;
}
if ((c & 0xF0) == 0xE0)
{
if (max_size < 3) return UnicodeResult.INVALID_UTF8?;
*size = 3;
Char32 uc = (c & 0x0F) << 12;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
uc += (c & 0x3F) << 6;
c = ptr++[0];
// Overlong sequence or invalid last
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
return uc + c & 0x3F;
}
if (max_size < 4) return UnicodeResult.INVALID_UTF8?;
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8?;
*size = 4;
Char32 uc = (c & 0x07) << 18;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
uc += (c & 0x3F) << 12;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
uc += (c & 0x3F) << 6;
c = ptr++[0];
// Overlong sequence or invalid last
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
return uc + c & 0x3F;
}
/**
* @param utf8 `An UTF-8 encoded slice of bytes`
* @return `the number of encoded code points`
**/
fn usz utf8_codepoints(String utf8)
{
usz len = 0;
foreach (char c : utf8)
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
/**
* Calculate the UTF8 length required to encode an UTF32 array.
* @param [in] utf32 `the utf32 data to calculate from`
* @return `the length of the resulting UTF8 array`
**/
fn usz utf8len_for_utf32(Char32[] utf32)
{
usz len = 0;
foreach (Char32 uc : utf32)
{
switch (true)
{
case uc < 0x7f:
len++;
case uc < 0x7ff:
len += 2;
case uc < 0xffff:
len += 3;
default:
len += 4;
}
}
return len;
}
/**
* Calculate the UTF8 length required to encode an UTF16 array.
* @param [in] utf16 `the utf16 data to calculate from`
* @return `the length of the resulting UTF8 array`
**/
fn usz utf8len_for_utf16(Char16[] utf16)
{
usz len = 0;
usz len16 = utf16.len;
for (usz i = 0; i < len16; i++)
{
Char16 c = utf16[i];
if (c & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
{
if (c < 0x7f)
{
len++;
continue;
}
if (c < 0x7ff)
{
len += 2;
continue;
}
len += 3;
continue;
}
len += 4;
}
return len;
}
/**
* Calculate the UTF16 length required to encode a UTF8 array.
* @param utf8 `the utf8 data to calculate from`
* @return `the length of the resulting UTF16 array`
**/
fn usz utf16len_for_utf8(String utf8)
{
usz len = utf8.len;
usz len16 = 0;
for (usz i = 0; i < len; i++)
{
len16++;
char c = utf8[i];
if (c & 0x80 == 0) continue;
i++;
if (c & 0xE0 == 0xC0) continue;
i++;
if (c & 0xF0 == 0xE0) continue;
i++;
len16++;
}
return len16;
}
/**
* @param [in] utf32 `the UTF32 array to check the length for`
* @return `the required length of an UTF16 array to hold the UTF32 data.`
**/
fn usz utf16len_for_utf32(Char32[] utf32)
{
usz len = utf32.len;
foreach (Char32 uc : utf32)
{
if (uc >= UTF16_SURROGATE_OFFSET) len++;
}
return len;
}
/**
* Convert an UTF32 array to an UTF8 array.
*
* @param [in] utf32
* @param [out] utf8_buffer
* @return `the number of bytes written.`
**/
fn usz! utf32to8(Char32[] utf32, String utf8_buffer)
{
usz len = utf8_buffer.len;
char* ptr = utf8_buffer.ptr;
foreach (Char32 uc : utf32)
{
usz used = char32_to_utf8(uc, ptr, len) @inline!;
len -= used;
ptr += used;
}
// Zero terminate if there is space.
if (len > 0) ptr[0] = 0;
return utf8_buffer.len - len;
}
/**
* Convert an UTF8 array to an UTF32 array.
*
* @param [in] utf8
* @param [out] utf32_buffer
* @return `the number of Char32s written.`
**/
fn usz! utf8to32(String utf8, Char32[] utf32_buffer)
{
usz len = utf8.len;
Char32* ptr = utf32_buffer.ptr;
usz len32 = 0;
usz buf_len = utf32_buffer.len;
for (usz i = 0; i < len;)
{
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED?;
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
i += width;
ptr[len32++] = uc;
}
// Zero terminate if possible
if (len32 + 1 < buf_len) ptr[len32] = 0;
return len32;
}
/**
* Copy an array of UTF16 data into an UTF8 buffer without bounds
* checking. This will assume the buffer is sufficiently large to hold
* the converted data.
*
* @param [in] utf16 `The UTF16 array containing the data to convert.`
* @param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF16 data.`
**/
fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
{
usz len16 = utf16.len;
for (usz i = 0; i < len16;)
{
usz available = len16 - i;
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline!;
i += available;
}
}
/**
* Copy an array of UTF8 data into an UTF32 buffer without bounds
* checking. This will assume the buffer is sufficiently large to hold
* the converted data.
*
* @param [in] utf8 `The UTF8 buffer containing the data to convert.`
* @param [out] utf32_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
**/
fn void! utf8to32_unsafe(String utf8, Char32* utf32_buffer)
{
usz len = utf8.len;
for (usz i = 0; i < len;)
{
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
i += width;
(utf32_buffer++)[0] = uc;
}
}
/**
* Copy an array of UTF8 data into an UTF16 buffer without bounds
* checking. This will assume the buffer is sufficiently large to hold
* the converted data.
*
* @param [in] utf8 `The UTF8 buffer containing the data to convert.`
* @param [out] utf16_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
**/
fn void! utf8to16_unsafe(String utf8, Char16* utf16_buffer)
{
usz len = utf8.len;
for (usz i = 0; i < len;)
{
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
char32_to_utf16_unsafe(uc, &utf16_buffer) @inline;
i += width;
}
}
/**
* Copy an array of UTF32 code points into an UTF8 buffer without bounds
* checking. This will assume the buffer is sufficiently large to hold
* the converted data.
*
* @param [in] utf32 `The UTF32 buffer containing the data to convert.`
* @param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
**/
fn void utf32to8_unsafe(Char32[] utf32, char* utf8_buffer)
{
char* start = utf8_buffer;
foreach (Char32 uc : utf32)
{
char32_to_utf8_unsafe(uc, &utf8_buffer) @inline;
}
}

414
lib/std/core/dstring.c3 Normal file
View File

@@ -0,0 +1,414 @@
module std::core::dstring;
def DString = distinct void*;
const usz MIN_CAPACITY @private = 16;
/**
* @require !str.data() "String already initialized"
**/
fn void DString.init(DString *str, usz capacity = MIN_CAPACITY, Allocator* using = mem::heap())
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = malloc(StringData, 1, .using = using, .end_padding = capacity);
data.allocator = using;
data.len = 0;
data.capacity = capacity;
*str = (DString)data;
}
/**
* @require !str.data() "String already initialized"
**/
fn void DString.tinit(DString *str, usz capacity = MIN_CAPACITY) => str.init(capacity, mem::temp()) @inline;
fn DString new_with_capacity(usz capacity, Allocator* using = mem::heap())
{
DString dstr;
dstr.init(capacity, using);
return dstr;
}
fn DString tnew_with_capacity(usz capacity) => new_with_capacity(capacity, mem::temp()) @inline;
fn DString new(String c = "", Allocator* using = mem::heap())
{
usz len = c.len;
StringData* data = (StringData*)new_with_capacity(len, using);
if (len)
{
data.len = len;
mem::copy(&data.chars, c.ptr, len);
}
return (DString)data;
}
fn DString tnew(String s = "") => new(s, mem::temp()) @inline;
fn DString DString.new_concat(DString a, DString b, Allocator* using = mem::heap())
{
DString string;
string.init(a.len() + b.len(), using);
string.append(a);
string.append(b);
return string;
}
fn DString DString.new_tconcat(DString a, DString b) => a.new_concat(b, mem::temp());
fn ZString DString.zstr(DString str)
{
StringData* data = str.data();
if (!data) return "";
if (data.capacity == data.len)
{
str.reserve(1);
data.chars[data.len] = 0;
}
else if (data.chars[data.len] != 0)
{
data.chars[data.len] = 0;
}
return (ZString)&data.chars[0];
}
fn usz DString.capacity(DString this)
{
if (!this) return 0;
return this.data().capacity;
}
fn usz DString.len(DString this)
{
if (!this) return 0;
return this.data().len;
}
/**
* @require new_size <= this.len()
*/
fn void DString.chop(DString this, usz new_size)
{
if (!this) return;
this.data().len = new_size;
}
fn String DString.str(DString str)
{
StringData* data = (StringData*)str;
if (!data) return "";
return (String)data.chars[:data.len];
}
fn void DString.append_utf32(DString* str, Char32[] chars)
{
str.reserve(chars.len);
foreach (Char32 c : chars)
{
str.append_char32(c);
}
}
/**
* @require index < str.len()
**/
fn void DString.set(DString str, usz index, char c)
{
str.data().chars[index] = c;
}
fn void DString.append_repeat(DString* str, char c, usz times)
{
if (times == 0) return;
str.reserve(times);
StringData* data = str.data();
for (usz i = 0; i < times; i++)
{
data.chars[data.len++] = c;
}
}
/**
* @require c <= 0x10ffff
*/
fn void DString.append_char32(DString* str, Char32 c)
{
if (c < 0x7f)
{
str.reserve(1);
StringData* data = str.data();
data.chars[data.len++] = (char)c;
return;
}
if (c < 0x7ff)
{
str.reserve(2);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xC0 | c >> 6);
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
return;
}
if (c < 0xffff)
{
str.reserve(3);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xE0 | c >> 12);
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
return;
}
str.reserve(4);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xF0 | c >> 18);
data.chars[data.len++] = (char)(0x80 | (c >> 12 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
}
fn DString DString.tcopy(DString* str) => str.copy(mem::temp());
fn DString DString.copy(DString* str, Allocator* using = null)
{
if (!str)
{
if (using) return new_with_capacity(0, using);
return (DString)null;
}
if (!using) using = mem::heap();
StringData* data = str.data();
DString new_string = new_with_capacity(data.capacity, using);
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
return new_string;
}
fn ZString DString.copy_zstr(DString* str, Allocator* using = mem::heap())
{
usz str_len = str.len();
if (!str_len)
{
return (ZString)calloc(1, .using = using);
}
char* zstr = malloc(str_len + 1, .using = using);
StringData* data = str.data();
mem::copy(zstr, &data.chars, str_len);
zstr[str_len] = 0;
return (ZString)zstr;
}
fn String DString.copy_str(DString* str, Allocator* using = mem::heap())
{
return (String)str.copy_zstr(using)[:str.len()];
}
fn String DString.tcopy_str(DString* str) => str.copy_str(mem::temp()) @inline;
fn bool DString.equals(DString str, DString other_string)
{
StringData *str1 = str.data();
StringData *str2 = other_string.data();
if (str1 == str2) return true;
if (!str1) return str2.len == 0;
if (!str2) return str1.len == 0;
usz str1_len = str1.len;
if (str1_len != str2.len) return false;
for (int i = 0; i < str1_len; i++)
{
if (str1.chars[i] != str2.chars[i]) return false;
}
return true;
}
fn void DString.free(DString* str)
{
if (!*str) return;
StringData* data = str.data();
if (!data) return;
free(data, .using = data.allocator);
*str = (DString)null;
}
fn bool DString.less(DString str, DString other_string)
{
StringData* str1 = str.data();
StringData* str2 = other_string.data();
if (str1 == str2) return false;
if (!str1) return str2.len != 0;
if (!str2) return str1.len == 0;
usz str1_len = str1.len;
usz str2_len = str2.len;
if (str1_len != str2_len) return str1_len < str2_len;
for (int i = 0; i < str1_len; i++)
{
if (str1.chars[i] >= str2.chars[i]) return false;
}
return true;
}
fn void DString.append_chars(DString* this, String str)
{
usz other_len = str.len;
if (!other_len) return;
if (!*this)
{
*this = new(str);
return;
}
this.reserve(other_len);
StringData* data = (StringData*)*this;
mem::copy(&data.chars[data.len], str.ptr, other_len);
data.len += other_len;
}
fn Char32[] DString.copy_utf32(DString* this, Allocator* using = mem::heap())
{
return this.str().to_utf32(using) @inline!!;
}
fn void DString.append_string(DString* this, DString str)
{
StringData* other = (StringData*)str;
if (!other) return;
this.append(str.str());
}
fn void DString.clear(DString* str)
{
str.data().len = 0;
}
fn void DString.append_char(DString* str, char c)
{
if (!*str)
{
*str = new_with_capacity(MIN_CAPACITY);
}
str.reserve(1);
StringData* data = (StringData*)*str;
data.chars[data.len++] = c;
}
macro void DString.append(DString* str, value)
{
var $Type = $typeof(value);
$switch ($Type)
$case char:
$case ichar:
str.append_char(value);
$case DString:
str.append_string(value);
$case String:
str.append_chars(value);
$case Char32:
str.append_char32(value);
$default:
$switch
$case @convertible(value, Char32):
str.append_char32(value);
$case @convertible(value, String):
str.append_chars(value);
$default:
$error "Unsupported type for append use printf instead.";
$endswitch
$endswitch
}
fn usz! DString.printf(DString* str, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
return formatter.vprintf(format, args);
}
fn usz! DString.printfn(DString* str, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
usz len = formatter.vprintf(format, args)!;
str.append('\n');
return len + 1;
}
fn DString new_join(String[] s, String joiner, Allocator* using = mem::heap())
{
if (!s.len) return (DString)null;
usz total_size = joiner.len * s.len;
foreach (String* &str : s)
{
total_size += str.len;
}
DString res = new_with_capacity(total_size, using);
res.append(s[0]);
foreach (String* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res;
}
fn void! out_string_append_fn(char c, void* data) @private
{
DString* s = data;
s.append_char(c);
}
fn StringData* DString.data(DString str) @inline @private
{
return (StringData*)str;
}
fn void DString.reserve(DString* str, usz addition)
{
StringData* data = str.data();
if (!data)
{
*str = dstring::new_with_capacity(addition);
return;
}
usz len = data.len + addition;
if (data.capacity >= len) return;
usz new_capacity = data.capacity *= 2;
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
*str = (DString)realloc(data, StringData.sizeof + new_capacity, .using = data.allocator);
}
fn usz! DString.read_from_stream(DString* string, Stream* reader)
{
if (reader.supports_available())
{
usz total_read = 0;
while (usz available = reader.available()!)
{
string.reserve(available);
StringData* data = string.data();
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!;
total_read += len;
data.len += len;
}
return total_read;
}
usz total_read = 0;
while (true)
{
// Reserve at least 16 bytes
string.reserve(16);
StringData* data = string.data();
// Read into the rest of the buffer
usz read = reader.read(data.chars[data.len..(data.capacity - 1)])!;
data.len += read;
// Ok, we reached the end.
if (read < 16) return total_read;
// Otherwise go another round
}
}
struct StringData @private
{
Allocator* allocator;
usz len;
usz capacity;
char[*] chars;
}

226
lib/std/core/env.c3 Normal file
View File

@@ -0,0 +1,226 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::env;
import libc;
enum CompilerOptLevel
{
O0,
O1,
O2,
O3
}
enum MemoryEnvironment
{
NORMAL,
SMALL,
TINY,
NONE
}
enum OsType
{
UNKNOWN,
NONE,
ANANAS,
CLOUD_ABI,
DRAGON_FLY,
FREEBSD,
FUCHSIA,
IOS,
KFREEBSD,
LINUX,
PS3,
MACOS,
NETBSD,
OPENBSD,
SOLARIS,
WIN32,
HAIKU,
MINIX,
RTEMS,
NACL, // Native Client
CNK, // BG/P Compute-Node Kernel
AIX,
CUDA,
NVOPENCL,
AMDHSA,
PS4,
ELFIAMCU,
TVOS,
WATCHOS,
MESA3D,
CONTIKI,
AMDPAL,
HERMITCORE,
HURD,
WASI,
EMSCRIPTEN,
}
enum ArchType
{
UNKNOWN,
ARM, // ARM (little endian): arm, armv.*, xscale
ARMB, // ARM (big endian): armeb
AARCH64, // AArch64 (little endian): aarch64
AARCH64_BE, // AArch64 (big endian): aarch64_be
AARCH64_32, // AArch64 (little endian) ILP32: aarch64_32
ARC, // ARC: Synopsys ARC
AVR, // AVR: Atmel AVR microcontroller
BPFEL, // eBPF or extended BPF or 64-bit BPF (little endian)
BPFEB, // eBPF or extended BPF or 64-bit BPF (big endian)
HEXAGON, // Hexagon: hexagon
MIPS, // MIPS: mips, mipsallegrex, mipsr6
MIPSEL, // MIPSEL: mipsel, mipsallegrexe, mipsr6el
MIPS64, // MIPS64: mips64, mips64r6, mipsn32, mipsn32r6
MIPS64EL, // MIPS64EL: mips64el, mips64r6el, mipsn32el, mipsn32r6el
MSP430, // MSP430: msp430
PPC, // PPC: powerpc
PPC64, // PPC64: powerpc64, ppu
PPC64LE, // PPC64LE: powerpc64le
R600, // R600: AMD GPUs HD2XXX - HD6XXX
AMDGCN, // AMDGCN: AMD GCN GPUs
RISCV32, // RISC-V (32-bit): riscv32
RISCV64, // RISC-V (64-bit): riscv64
SPARC, // Sparc: sparc
SPARCV9, // Sparcv9: Sparcv9
SPARCEL, // Sparc: (endianness = little). NB: 'Sparcle' is a CPU variant
SYSTEMZ, // SystemZ: s390x
TCE, // TCE (http://tce.cs.tut.fi/): tce
TCELE, // TCE little endian (http://tce.cs.tut.fi/): tcele
THUMB, // Thumb (little endian): thumb, thumbv.*
THUMBEB, // Thumb (big endian): thumbeb
X86, // X86: i[3-9]86
X86_64, // X86-64: amd64, x86_64
XCORE, // XCore: xcore
NVPTX, // NVPTX: 32-bit
NVPTX64, // NVPTX: 64-bit
LE32, // le32: generic little-endian 32-bit CPU (PNaCl)
LE64, // le64: generic little-endian 64-bit CPU (PNaCl)
AMDIL, // AMDIL
AMDIL64, // AMDIL with 64-bit pointers
HSAIL, // AMD HSAIL
HSAIL64, // AMD HSAIL with 64-bit pointers
SPIR, // SPIR: standard portable IR for OpenCL 32-bit version
SPIR64, // SPIR: standard portable IR for OpenCL 64-bit version
KALIMBA, // Kalimba: generic kalimba
SHAVE, // SHAVE: Movidius vector VLIW processors
LANAI, // Lanai: Lanai 32-bit
WASM32, // WebAssembly with 32-bit pointers
WASM64, // WebAssembly with 64-bit pointers
RSCRIPT32, // 32-bit RenderScript
RSCRIPT64, // 64-bit RenderScript
}
const OsType OS_TYPE = (OsType)$$OS_TYPE;
const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE;
const bool COMPILER_LIBC_AVAILABLE = $$COMPILER_LIBC_AVAILABLE;
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)$$COMPILER_OPT_LEVEL;
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
const usz LLVM_VERSION = $$LLVM_VERSION;
const bool BENCHMARKING = $$BENCHMARKING;
const bool TESTING = $$TESTING;
const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT;
macro bool os_is_win32()
{
return OS_TYPE == WIN32;
}
macro bool os_is_darwin()
{
$switch (OS_TYPE)
$case IOS:
$case MACOS:
$case TVOS:
$case WATCHOS:
return true;
$default:
return false;
$endswitch
}
macro bool os_is_posix()
{
$switch (OS_TYPE)
$case IOS:
$case MACOS:
$case NETBSD:
$case LINUX:
$case KFREEBSD:
$case FREEBSD:
$case OPENBSD:
$case SOLARIS:
$case TVOS:
$case WATCHOS:
return true;
$case WIN32:
$case WASI:
$case EMSCRIPTEN:
return false;
$default:
$echo("Assuming non-Posix environment");
return false;
$endswitch
}
/**
* @param [&in] name
* @require name.len > 0
**/
fn String! get_var(String name)
{
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
@pool()
{
ZString val = libc::getenv(name.zstr_tcopy());
return val ? val.as_str() : SearchResult.MISSING?;
};
$else
return "";
$endif
}
/**
* @param [&in] name
* @param [&in] value
* @require name.len > 0
**/
fn void set_var(String name, String value, bool overwrite = true)
{
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
@pool()
{
if (libc::setenv(name.zstr_tcopy(), value.zstr_copy(), (int)overwrite))
{
unreachable();
}
};
$endif
}
/**
* @param [&in] name
* @require name.len > 0
**/
fn void clear_var(String name)
{
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
@pool()
{
if (libc::unsetenv(name.zstr_tcopy()))
{
unreachable();
}
};
$endif
}

431
lib/std/core/mem.c3 Normal file
View File

@@ -0,0 +1,431 @@
// Copyright (c) 2021-2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem;
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
macro @volatile_load(&x) @builtin
{
return $$volatile_load(&x);
}
macro @volatile_store(&x, y) @builtin
{
return $$volatile_store(&x, ($typeof(x))y);
}
enum AtomicOrdering : int
{
NOT_ATOMIC, // Not atomic
UNORDERED, // No lock
MONOTONIC, // Consistent ordering
ACQUIRE, // Barrier locking load/store
RELEASE, // Barrier releasing load/store
ACQUIRE_RELEASE, // Barrier fence to load/store
SEQ_CONSISTENT, // Acquire semantics, ordered with other seq_consistent
}
/**
* @param [in] x "the variable or dereferenced pointer to load."
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
* @param $volatile "whether the load should be volatile, defaults to 'false'"
* @return "returns the value of x"
*
* @require $ordering != AtomicOrdering.RELEASE "Release ordering is not valid for load."
* @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for load."
* @require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
**/
macro @atomic_load(&x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
return $$atomic_load(&x, $volatile, (int)$ordering);
}
/**
* @param [out] x "the variable or dereferenced pointer to store to."
* @param value "the value to store."
* @param $ordering "the atomic ordering of the store, defaults to SEQ_CONSISTENT"
* @param $volatile "whether the store should be volatile, defaults to 'false'"
*
* @require $ordering != AtomicOrdering.ACQUIRE "Acquire ordering is not valid for store."
* @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for store."
* @require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
**/
macro void @atomic_store(&x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
$$atomic_store(&x, value, $volatile, (int)$ordering);
}
macro compare_exchange(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT, bool $volatile = true, bool $weak = false, usz $alignment = 0)
{
return $$compare_exchange(ptr, compare, value, $volatile, $weak, $success.ordinal, $failure.ordinal, $alignment);
}
macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT)
{
return compare_exchange(ptr, compare, value, $success, $failure, true);
}
/**
* @require math::is_power_of_2(alignment)
**/
fn usz aligned_offset(usz offset, usz alignment)
{
return alignment * ((offset + alignment - 1) / alignment);
}
macro void* aligned_pointer(void* ptr, usz alignment)
{
return (void*)(uptr)aligned_offset((uptr)ptr, alignment);
}
/**
* @require math::is_power_of_2(alignment)
**/
fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
{
return (uptr)ptr & ((uptr)alignment - 1) == 0;
}
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if $inlined:
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
$else
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
$endif
}
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if $inlined:
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
$else
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
$endif
}
macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
{
$$memmove(dst, src, len, $is_volatile, $dst_align, $src_align);
}
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if $inlined:
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
$else
$$memset(dst, val, len, $is_volatile, $dst_align);
$endif
}
/**
* @require values::@inner_kind(a) == TypeKind.SUBARRAY || values::@inner_kind(a) == TypeKind.POINTER
* @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER
* @require values::@inner_kind(a) != TypeKind.SUBARRAY || len == -1
* @require values::@inner_kind(a) != TypeKind.POINTER || len > -1
* @checked (a = b), (b = a)
**/
macro bool equals(a, b, isz len = -1, usz $align = 0)
{
$if !$align:
$align = $typeof(a[0]).alignof;
$endif
void* x @noinit;
void* y @noinit;
$if values::@inner_kind(a) == TypeKind.SUBARRAY:
len = a.len;
if (len != b.len) return false;
x = a.ptr;
y = b.ptr;
$else
x = a;
y = b;
assert(len >= 0, "A zero or positive length must be given when comparing pointers.");
$endif
if (!len) return true;
var $Type;
$switch ($align)
$case 1:
$Type = char;
$case 2:
$Type = ushort;
$case 4:
$Type = uint;
$case 8:
$default:
$Type = ulong;
$endswitch
var $step = $Type.sizeof;
usz end = len / $step;
for (usz i = 0; i < end; i++)
{
if ((($Type*)x)[i] != (($Type*)y)[i]) return false;
}
usz last = len % $align;
for (usz i = len - last; i < len; i++)
{
if (((char*)x)[i] != ((char*)y)[i]) return false;
}
return true;
}
macro @clone(&value) @builtin
{
$typeof(value)* x = malloc($typeof(value));
*x = value;
return x;
}
macro @tclone(&value) @builtin
{
$typeof(value)* x = talloc($typeof(value));
*x = value;
return x;
}
macro type_alloc_must_be_aligned($Type)
{
return $Type.alignof > DEFAULT_MEM_ALIGNMENT;
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro malloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
return malloc_checked($vasplat(), .using = using, .end_padding = end_padding)!!;
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro malloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with malloc_aligned";
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.alloc($Type.sizeof * size + end_padding))[:size];
$else
return ($Type*)using.alloc($Type.sizeof + end_padding);
$endif
$else
return using.alloc($vaarg(0) + end_padding);
$endif
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
* @require alignment && math::is_power_of_2(alignment)
**/
macro malloc_aligned(..., usz alignment = 0, usz end_padding = 0, Allocator* using = mem::heap()) @builtin
{
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size];
$else
return ($Type*)using.alloc_aligned($Type.sizeof + end_padding, alignment);
$endif
$else
return using.alloc_aligned($vaarg(0) + end_padding, alignment);
$endif
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro calloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
return calloc_checked($vasplat(), .using = using, .end_padding = end_padding)!!;
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro calloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with calloc_aligned";
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.calloc($Type.sizeof * size + end_padding))[:size];
$else
return ($Type*)using.calloc($Type.sizeof + end_padding);
$endif
$else
return using.calloc($vaarg(0) + end_padding);
$endif
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
* @require alignment && math::is_power_of_2(alignment)
**/
macro calloc_aligned(..., usz alignment = 0, Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size];
$else
return ($Type*)using.calloc_aligned($Type.sizeof + end_padding, alignment);
$endif
$else
return using.calloc_aligned($vaarg(0) + end_padding, alignment);
$endif
}
fn void* realloc(void *ptr, usz new_size, Allocator* using = mem::heap()) @builtin @inline
{
return using.realloc(ptr, new_size)!!;
}
fn void*! realloc_checked(void *ptr, usz new_size, Allocator* using = mem::heap()) @builtin @inline
{
return using.realloc(ptr, new_size);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! realloc_aligned(void *ptr, usz new_size, usz alignment, Allocator* using = mem::heap()) @builtin @inline
{
return using.realloc_aligned(ptr, new_size, alignment);
}
macro void free(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr)!!;
macro void! free_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr);
macro void free_aligned(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr)!!;
macro void! free_aligned_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr);
/**
* Run with a specific allocator inside of the macro body.
**/
macro void @scoped(Allocator* using; @body())
{
Allocator* old_allocator = thread_allocator;
thread_allocator = using;
defer thread_allocator = old_allocator;
@body();
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @builtin
{
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)temp().alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
$else
return ($Type*)temp().alloc_aligned($Type.sizeof + end_padding, alignment)!!;
$endif
$else
return temp().alloc_aligned($vaarg(0) + end_padding, alignment)!!;
$endif
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro tcalloc(..., usz end_padding = 0, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin
{
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)temp().calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
$else
return ($Type*)temp().calloc_aligned($Type.sizeof + end_padding, alignment)!!;
$endif
$else
return temp().calloc_aligned($vaarg(0) + end_padding, alignment)!!;
$endif
}
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline
{
return temp().realloc_aligned(ptr, size, alignment)!!;
}
macro void @pool(;@body) @builtin
{
TempAllocator* allocator = temp();
usz mark = allocator.used;
defer allocator.reset(mark);
@body();
}
macro void @allocating_pool(Allocator* using; @body(bool is_temp)) @builtin
{
TempAllocator* allocator = temp();
usz mark = allocator.used;
bool is_temp = allocator == using;
defer if (!is_temp) allocator.reset(mark);
@body(is_temp);
}
tlocal Allocator* thread_allocator @private = allocator::LIBC_ALLOCATOR;
tlocal TempAllocator* thread_temp_allocator @private = null;
macro TempAllocator* temp_allocator() => temp();
macro TempAllocator* temp()
{
if (!thread_temp_allocator)
{
$switch (env::MEMORY_ENV)
$case NORMAL:
thread_temp_allocator = allocator::new_temp(1024 * 256, thread_allocator)!!;
$case SMALL:
thread_temp_allocator = allocator::new_temp(1024 * 16, thread_allocator)!!;
$case TINY:
thread_temp_allocator = allocator::new_temp(1024 * 2, thread_allocator)!!;
$case NONE:
unreachable("Temp allocator must explicitly created when memory-env is set to 'none'.");
$endswitch
}
return thread_temp_allocator;
}
macro Allocator* current_allocator() => thread_allocator;
macro Allocator* heap() => thread_allocator;
$if !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64:
SimpleHeapAllocator wasm_allocator @private;
extern int __heap_base;
static initialize @priority(1)
{
allocator::wasm_memory.allocate_block(mem::DEFAULT_MEM_ALIGNMENT)!!; // Give us a valid null.
// Check if we need to move the heap.
uptr start = (uptr)&__heap_base;
if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start;
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
thread_allocator = &wasm_allocator;
}
$endif

View File

@@ -0,0 +1,109 @@
module std::core::mem::allocator;
const DEFAULT_SIZE_PREFIX = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR;
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR;
def AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
struct Allocator
{
AllocatorFunction function;
}
enum AllocationKind
{
ALLOC,
CALLOC,
REALLOC,
FREE,
ALIGNED_ALLOC,
ALIGNED_CALLOC,
ALIGNED_REALLOC,
ALIGNED_FREE,
RESET,
MARK,
}
fault AllocationFailure
{
OUT_OF_MEMORY,
UNSUPPORTED_OPERATION,
CHUNK_TOO_LARGE,
}
fn void*! Allocator.alloc(Allocator* allocator, usz size) @inline
{
return allocator.function(allocator, size, 0, 0, null, ALLOC);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! Allocator.alloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
{
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_ALLOC);
}
fn void*! Allocator.realloc(Allocator* allocator, void* old_pointer, usz size) @inline
{
return allocator.function(allocator, size, 0, 0, old_pointer, REALLOC);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! Allocator.realloc_aligned(Allocator* allocator, void* old_pointer, usz size, usz alignment, usz offset = 0) @inline
{
return allocator.function(allocator, size, alignment, offset, old_pointer, ALIGNED_REALLOC);
}
fn usz! Allocator.mark(Allocator* allocator) @inline
{
return (usz)(uptr)allocator.function(allocator, 0, 0, 0, null, MARK);
}
fn void*! Allocator.calloc(Allocator* allocator, usz size) @inline
{
return allocator.function(allocator, size, 0, 0, null, CALLOC);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! Allocator.calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
{
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_CALLOC);
}
fn void! Allocator.free(Allocator* allocator, void* old_pointer) @inline
{
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)!;
}
fn void! Allocator.free_aligned(Allocator* allocator, void* old_pointer) @inline
{
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)!;
}
fn void Allocator.reset(Allocator* allocator, usz mark = 0)
{
allocator.function(allocator, mark, 0, 0, null, RESET)!!;
}
fn usz alignment_for_allocation(usz alignment) @inline @private
{
if (alignment < mem::DEFAULT_MEM_ALIGNMENT)
{
alignment = mem::DEFAULT_MEM_ALIGNMENT;
}
return alignment;
}

View File

@@ -0,0 +1,32 @@
module std::core::mem::allocator;
const usz WASM_BLOCK_SIZE = 65536;
WasmMemory wasm_memory;
struct WasmMemory
{
usz allocation;
uptr use;
}
fn char[]! WasmMemory.allocate_block(WasmMemory* this, usz bytes)
{
if (!this.allocation)
{
this.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
}
isz bytes_required = bytes + this.use - this.allocation;
if (bytes_required <= 0)
{
defer this.use += bytes;
return ((char*)this.use)[:bytes];
}
usz blocks_required = (bytes_required + WASM_BLOCK_SIZE + 1) / WASM_BLOCK_SIZE;
if ($$wasm_memory_grow(0, blocks_required) == -1) return AllocationFailure.OUT_OF_MEMORY?;
this.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
defer this.use += bytes;
return ((char*)this.use)[:bytes];
}

View File

@@ -0,0 +1,168 @@
module std::core::main_stub;
macro usz _strlen(ptr) @private
{
usz len = 0;
while (ptr[len]) len++;
return len;
}
macro int @main_to_err_main(#m, int, char**)
{
if (catch #m()) return 1;
return 0;
}
macro int @main_to_int_main(#m, int, char**) => #m();
macro int @main_to_void_main(#m, int, char**)
{
#m();
return 0;
}
macro String[] args_to_strings(int argc, char** argv) @private
{
String[] list = malloc(String, argc);
for (int i = 0; i < argc; i++)
{
char* arg = argv[i];
usz len = 0;
list[i] = (String)arg[:_strlen(arg)];
}
return list;
}
macro int @main_to_err_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
if (catch #m(list)) return 1;
return 0;
}
macro int @main_to_int_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
return #m(list);
}
macro int @main_to_void_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
#m(list);
return 0;
}
$if env::os_is_win32():
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @extern("CommandLineToArgvW");
macro String[] win_command_line_to_strings(ushort* cmd_line) @private
{
int argc;
Char16** argv = _win_command_line_to_argv_w(cmd_line, &argc);
return wargs_strings(argc, argv);
}
macro String[] wargs_strings(int argc, Char16** argv) @private
{
String[] list = malloc(String, argc);
for (int i = 0; i < argc; i++)
{
Char16* arg = argv[i];
Char16[] argstring = arg[:_strlen(arg)];
list[i] = string::from_utf16(argstring) ?? "?".copy();
}
return list[:argc];
}
macro void release_wargs(String[] list) @private
{
foreach (s : list) free(s.ptr);
free(list.ptr);
}
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
{
if (catch #m()) return 1;
return 0;
}
macro int @win_to_int_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => #m();
macro int @win_to_void_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
{
#m();
return 0;
}
macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
if (catch #m(args)) return 1;
return 0;
}
macro int @win_to_int_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(args);
}
macro int @win_to_void_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(args);
return 0;
}
macro int @win_to_err_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
if (catch #m(handle, args, show_cmd)) return 1;
return 0;
}
macro int @win_to_int_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(handle, args, show_cmd);
}
macro int @win_to_void_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(handle, args, show_cmd);
return 0;
}
macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
if (catch #m(args)) return 1;
return 1;
}
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
return #m(args);
}
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
#m(args);
return 0;
}
$endif

99
lib/std/core/runtime.c3 Normal file
View File

@@ -0,0 +1,99 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::runtime;
struct VirtualAny
{
void* ptr;
typeid type_id;
}
struct SubArrayContainer
{
void* ptr;
usz len;
}
def TestFn = fn void!();
struct TestRunner
{
String[] test_names;
TestFn[] test_fns;
JmpBuf buf;
}
fn TestRunner test_runner_create()
{
return TestRunner {
.test_fns = $$TEST_FNS,
.test_names = $$TEST_NAMES,
};
}
import libc;
TestRunner* current_runner @private;
fn void test_panic(String message, String file, String function, uint line)
{
io::printn("[error]");
io::print("\n Error: ");
io::print(message);
io::printn();
io::printfn(" - in %s %s:%s.\n", function, file, line);
libc::longjmp(&current_runner.buf, 1);
}
fn bool TestRunner.run(TestRunner* runner)
{
current_runner = runner;
PanicFn old_panic = builtin::panic;
defer builtin::panic = old_panic;
builtin::panic = &test_panic;
int tests_passed = 0;
int tests = runner.test_names.len;
io::printn("----- TESTS -----");
foreach(i, String name : runner.test_names)
{
io::printf("Testing %s ... ", name);
if (libc::setjmp(&runner.buf) == 0)
{
if (catch err = runner.test_fns[i]())
{
io::printn("[failed]");
continue;
}
io::printn("[ok]");
tests_passed++;
}
}
io::printfn("\n%d test(s) run.\n", tests);
io::print("Test Result: ");
if (tests_passed < tests)
{
io::print("FAILED");
}
else
{
io::print("ok");
}
io::printfn(". %d passed, %d failed.", tests_passed, tests - tests_passed);
return tests == tests_passed;
}
fn bool __run_default_test_runner()
{
return test_runner_create().run();
}
$if !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64:
extern fn void __wasm_call_ctors();
fn void wasm_initialize() @extern("_initialize") @wasm
{
// The linker synthesizes this to call constructors.
__wasm_call_ctors();
}
$endif

514
lib/std/core/string.c3 Normal file
View File

@@ -0,0 +1,514 @@
module std::core::string;
import std::ascii;
def ZString = distinct inline char*;
def Char32 = uint;
def Char16 = ushort;
fault UnicodeResult
{
INVALID_UTF8,
INVALID_UTF16,
CONVERSION_FAILED,
}
const uint SURROGATE_OFFSET @private = 0x10000;
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
const uint SURROGATE_MASK @private = 0xFC00;
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
const uint SURROGATE_BITS @private = 10;
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
fault NumberConversion
{
EMPTY_STRING,
NEGATIVE_VALUE,
MALFORMED_INTEGER,
INTEGER_OVERFLOW,
MALFORMED_FLOAT,
FLOAT_OUT_OF_RANGE,
}
macro String printf(String fmt, ..., Allocator* using = mem::heap())
{
@stack_mem(256; Allocator* mem)
{
DString str;
str.init(.using = mem);
str.printf(fmt, $vasplat());
return str.copy_str(using);
};
}
macro String tprintf(String fmt, ...)
{
DString str;
str.tinit();
str.printf(fmt, $vasplat());
return str.str();
}
macro bool char_in_set(char c, String set)
{
foreach (ch : set) if (ch == c) return true;
return false;
}
fn String join(String[] s, String joiner, Allocator* using = mem::heap())
{
if (!s)
{
return (String)(calloc(char, 2, .using = using)[:0]);
}
usz total_size = joiner.len * s.len;
foreach (String* &str : s)
{
total_size += str.len;
}
@stack_mem(256; Allocator* mem)
{
DString res = dstring::new_with_capacity(total_size, .using = mem);
res.append(s[0]);
foreach (String* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res.copy_str(using);
};
}
/**
* @param [in] string
* @param [in] to_trim
**/
fn String String.trim(String string, String to_trim = "\t\n\r ")
{
usz start = 0;
usz len = string.len;
while (start < len && char_in_set(string[start], to_trim)) start++;
if (start == len) return string[:0];
usz end = len - 1;
while (end > start && char_in_set(string[end], to_trim)) end--;
return string[start..end];
}
/**
* @param [in] string
* @param [in] needle
**/
fn bool String.starts_with(String string, String needle)
{
if (needle.len > string.len) return false;
if (!needle.len) return true;
return string[:needle.len] == needle;
}
/**
* @param [in] string
* @param [in] needle
**/
fn bool String.ends_with(String string, String needle)
{
if (needle.len > string.len) return false;
if (!needle.len) return true;
return string[^needle.len..] == needle;
}
/**
* Strip the front of the string if the prefix exists.
*
* @param [in] string
* @param [in] needle
**/
fn String String.strip(String string, String needle)
{
if (!needle.len || !string.starts_with(needle)) return string;
return string[needle.len..];
}
/**
* Strip the end of the string if the suffix exists.
*
* @param [in] string
* @param [in] needle
**/
fn String String.strip_end(String string, String needle)
{
if (!needle.len || !string.ends_with(needle)) return string;
// Note that this is the safe way if we want to support zero length.
return string[:(string.len - needle.len)];
}
/**
* Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
*
* @param [in] s
* @param [in] needle
* @param [&inout] using "The allocator, defaults to the heap allocator"
* @param max "Max number of elements, 0 means no limit, defaults to 0"
* @require needle.len > 0 "The needle must be at least 1 character long"
* @ensure return.len > 0
**/
fn String[] String.split(String s, String needle, usz max = 0, Allocator* using = mem::heap())
{
usz capacity = 16;
usz i = 0;
String* holder = malloc(String, capacity, .using = using);
bool no_more = false;
while (!no_more)
{
usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle);
String res @noinit;
if (try index)
{
res = s[:index];
s = s[index + needle.len..];
}
else
{
res = s;
no_more = true;
}
if (i == capacity)
{
capacity *= 2;
holder = realloc(holder, String.sizeof * capacity, .using = using);
}
holder[i++] = res;
}
return holder[:i];
}
/**
* This function is identical to String.split, but implicitly uses the
* temporary allocator.
*
* @param [in] s
* @param [in] needle
* @param max "Max number of elements, 0 means no limit, defaults to 0"
**/
fn String[] String.tsplit(String s, String needle, usz max = 0)
{
return s.split(needle, max, mem::temp()) @inline;
}
fn bool String.contains(String s, String needle)
{
return @ok(s.index_of(needle));
}
/**
* Find the index of the first incidence of a string.
*
* @param [in] s
* @param [in] needle
* @pure
* @ensure return < s.len
* @require needle.len > 0 : "The needle must be len 1 or more"
* @return "the index of the needle"
* @return! SearchResult.MISSING "if the needle cannot be found"
**/
fn usz! String.index_of(String s, String needle)
{
usz match = 0;
usz needed = needle.len;
usz index_start = 0;
char search = needle[0];
foreach (usz i, char c : s)
{
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start;
search = needle[match];
continue;
}
if (match)
{
match = 0;
search = needle[0];
}
}
return SearchResult.MISSING?;
}
/**
* Find the index of the last incidence of a string.
*
* @param [in] s
* @param [in] needle
* @pure
* @ensure return < s.len
* @require needle.len > 0 "The needle must be len 1 or more"
* @return "the index of the needle"
* @return! SearchResult.MISSING "if the needle cannot be found"
**/
fn usz! String.rindex_of(String s, String needle)
{
usz match = 0;
usz needed = needle.len;
usz index_start = 0;
char search = needle[^1];
foreach_r (usz i, char c : s)
{
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start - needle.len + 1;
search = needle[^(match + 1)];
continue;
}
if (match)
{
match = 0;
search = needle[^1];
}
}
return SearchResult.MISSING?;
}
fn String ZString.as_str(ZString str)
{
return (String)((char*)str)[:str.len()];
}
fn usz ZString.char_len(ZString str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0])
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
fn usz ZString.len(ZString str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0]) len++;
return len;
}
fn ZString String.zstr_copy(String s, Allocator* using = mem::heap())
{
usz len = s.len;
char* str = malloc(len + 1, .using = using);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn String String.concat(String s1, String s2, Allocator* using = mem::heap())
{
usz full_len = s1.len + s2.len;
char* str = malloc(full_len + 1, .using = using);
usz s1_len = s1.len;
mem::copy(str, s1.ptr, s1_len);
mem::copy(str + s1_len, s2.ptr, s2.len);
str[full_len] = 0;
return (String)str[:full_len];
}
fn String String.tconcat(String s1, String s2) => s1.concat(s2, mem::temp());
fn ZString String.zstr_tcopy(String s) => s.zstr_copy(mem::temp()) @inline;
fn String String.copy(String s, Allocator* using = mem::heap())
{
usz len = s.len;
char* str = malloc(len + 1, .using = using);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (String)str[:len];
}
fn String String.tcopy(String s) => s.copy(mem::temp()) @inline;
fn String ZString.copy(ZString z, Allocator* using = mem::heap()) => z.as_str().copy(using) @inline;
fn String ZString.tcopy(ZString z) => z.as_str().copy(mem::temp()) @inline;
/**
* Convert an UTF-8 string to UTF-16
* @return "The UTF-16 string as a slice, allocated using the given allocator"
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
* @return! AllocationFailure "If allocation of the string fails"
**/
fn Char16[]! String.to_utf16(String s, Allocator* using = mem::heap())
{
usz len16 = conv::utf16len_for_utf8(s);
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)!;
conv::utf8to16_unsafe(s, data)!;
data[len16] = 0;
return data[:len16];
}
fn Char32[]! String.to_utf32(String s, Allocator* using = mem::heap())
{
usz codepoints = conv::utf8_codepoints(s);
Char32* data = malloc_checked(Char32, codepoints + 1, .using = using)!;
conv::utf8to32_unsafe(s, data)!;
data[codepoints] = 0;
return data[:codepoints];
}
fn void String.convert_ascii_to_lower(String s)
{
foreach (&c : s) if (*c >= 'A' && *c <= 'Z') *c += 'a' - 'A';
}
fn String String.ascii_to_lower(String s, Allocator* using = mem::heap())
{
String copy = s.copy(using);
copy.convert_ascii_to_lower();
return copy;
}
fn void String.convert_ascii_to_upper(String s)
{
foreach (&c : s) if (*c >= 'a' && *c <= 'z') *c -= 'a' - 'A';
}
fn String String.ascii_to_upper(String s, Allocator* using = mem::heap())
{
String copy = s.copy(using);
copy.convert_ascii_to_upper();
return copy;
}
fn String! from_utf32(Char32[] utf32, Allocator* using = mem::heap())
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = malloc_checked(len + 1, .using = using)!;
defer catch free(data, .using = using);
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return (String)data[:len];
}
fn String! from_utf16(Char16[] utf16, Allocator* using = mem::heap())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = malloc_checked(len + 1, .using = using)!;
defer catch free(data, .using = using);
conv::utf16to8_unsafe(utf16, data)!;
data[len] = 0;
return (String)data[:len];
}
fn String! from_zutf16(Char16* utf16_pointer, Allocator* using = mem::heap())
{
usz utf16_len;
while (utf16_pointer[utf16_len] != 0) utf16_len++;
Char16[] utf16 = utf16_pointer[:utf16_len];
return from_utf16(utf16, using);
}
fn usz String.utf8_codepoints(String s)
{
usz len = 0;
foreach (char c : s)
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
macro String.to_integer(String string, $Type)
{
usz len = string.len;
usz index = 0;
char* ptr = string.ptr;
while (index < len && ascii::is_blank_m(ptr[index])) index++;
if (len == index) return NumberConversion.EMPTY_STRING?;
bool is_negative;
switch (string[index])
{
case '-':
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE?;
is_negative = true;
index++;
case '+':
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
$Type base = 10;
if (string[index] == '0')
{
index++;
if (index == len) return ($Type)0;
switch (string[index])
{
case 'x':
case 'X':
base = 16;
index++;
case 'b':
case 'B':
base = 2;
index++;
case 'o':
case 'O':
base = 8;
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
}
$Type value = 0;
while (index != len)
{
char c = {|
char ch = string[index++];
if (base != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A');
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?;
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?;
return (char)(ch - 'a');
|}!;
if (c >= base) return NumberConversion.MALFORMED_INTEGER?;
value = {|
if (is_negative)
{
$Type new_value = value * base - c;
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
}
$Type new_value = value * base + c;
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
|}!;
}
return value;
}
fn Char16[]! String.to_temp_utf16(String s) => s.to_utf16(mem::temp());
fn int128! String.to_int128(String s) => s.to_integer(int128);
fn long! String.to_long(String s) => s.to_integer(long);
fn int! String.to_int(String s) => s.to_integer(int);
fn short! String.to_short(String s) => s.to_integer(short);
fn ichar! String.to_ichar(String s) => s.to_integer(ichar);
fn uint128! String.to_uint128(String s) => s.to_integer(uint128);
fn ulong! String.to_ulong(String s) => s.to_integer(ulong);
fn uint! String.to_uint(String s) => s.to_integer(uint);
fn ushort! String.to_ushort(String s) => s.to_integer(ushort);
fn char! String.to_uchar(String s) => s.to_integer(char);
fn double! String.to_double(String s) => s.to_real(double);
fn float! String.to_float(String s) => s.to_real(float);

View File

@@ -0,0 +1,23 @@
module std::core::string::iterator;
struct StringIterator
{
String utf8;
usz current;
}
fn void StringIterator.reset(StringIterator* this)
{
this.current = 0;
}
fn Char32! StringIterator.next(StringIterator* this)
{
usz len = this.utf8.len;
usz current = this.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
usz read = (len - current < 4 ? len - current : 4);
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)!;
this.current += read;
return res;
}

View File

@@ -0,0 +1,487 @@
module std::core::string;
import std::math;
// Float parsing based on code in Musl floatscan.c by Rich Felker.
// Musl uses the MIT license, copied below:
// ----------------------------------------------------------------------
// Copyright © 2005-2014 Rich Felker, et al.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in 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:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// 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 AUTHORS 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 IN THE SOFTWARE.
// ----------------------------------------------------------------------
const KMAX = 128;
const MASK = KMAX - 1;
const B1B_DIG = 2;
const uint[2] B1B_MAX = { 9007199, 254740991 };
/**
* @require chars.len > 0
**/
macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
{
uint[KMAX] x;
const uint[2] TH = B1B_MAX;
int emax = - $emin - $bits + 3;
const int[*] P10S = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
usz index;
bool got_digit = chars[0] == '0';
bool got_rad;
long lrp, dc;
int k, j, lnz;
usz len = chars.len;
usz last_char = len - 1;
assert(len);
char c @noinit;
// Skip past first characters
while ((c = chars[index]) == '0')
{
if (index == last_char) return sign * 0.0;
index++;
}
if (c == '.')
{
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
{
lrp--;
got_digit = true;
while (last_char != index && (c = chars[++index]) == '0')
{
lrp--;
}
}
}
while (c - '0' < 10u || c == '.')
{
switch
{
case c == '.':
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
got_rad = true;
lrp = dc;
case k < KMAX - 3:
dc++;
if (c != '0') lnz = (int)dc;
if (j)
{
x[k] = x[k] * 10 + c - '0';
}
else
{
x[k] = c - '0';
}
if (++j == 9)
{
k++;
j = 0;
}
got_digit = true;
default:
dc++;
if (c != '0') x[KMAX - 4] |= 1;
}
if (index == last_char) break;
assert(index < last_char);
c = chars[++index];
}
if (!got_rad) lrp = dc;
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if ((c | 32) == 'e')
{
if (last_char == index) return NumberConversion.MALFORMED_FLOAT?;
long e10 = String.to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT?!;
lrp += e10;
}
else if (index != last_char)
{
return NumberConversion.MALFORMED_FLOAT?;
}
// Handle zero specially to avoid nasty special cases later
if (!x[0]) return sign * 0.0;
// Optimize small integers (w/no exponent) and over/under-flow
if (lrp == dc && dc < 10 && ($bits > 30 || (ulong)x[0] >> $bits == 0)) return sign * (double)x[0];
if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
// Align incomplete final B1B digit
if (j)
{
for (; j < 9; j++) x[k] *= 10;
k++;
j = 0;
}
int a;
int z = k;
int e2;
long rp = lrp;
// Optimize small to mid-size integers (even in exp. notation)
if (lnz < 9 && lnz <= rp && rp < 18)
{
if (rp == 9) return sign * (double)x[0];
if (rp < 9) return sign * (double)x[0] / P10S[8 - rp];
int bitlim = $bits - 3 * (int)(rp - 9);
if (bitlim > 30 || x[0] >> bitlim == 0) return sign * (double)x[0] * P10S[rp - 10];
}
// Align radix point to B1B digit boundary
if (rp % 9)
{
long rpm9 = rp >= 0 ? rp % 9 : rp % 9 + 9;
int p10 = P10S[8 - rpm9];
uint carry = 0;
for (k = a; k != z; k++)
{
uint tmp = x[k] % p10;
x[k] = x[k] / p10 + carry;
carry = 1000000000 / p10 * tmp;
if (k == a && !x[k])
{
a = (a + 1) & MASK;
rp -= 9;
}
}
if (carry) x[z++] = carry;
rp += 9 - rpm9;
}
// Upscale until desired number of bits are left of radix point
while (rp < 9 * B1B_DIG || (rp == 9 * B1B_DIG && x[a] < TH[0]))
{
uint carry = 0;
e2 -= 29;
for (k = (z - 1) & MASK; ; k = (k - 1) & MASK)
{
ulong tmp = (ulong)x[k] << 29 + carry;
if (tmp > 1000000000)
{
carry = (uint)(tmp / 1000000000);
x[k] = (uint)(tmp % 1000000000);
}
else
{
carry = 0;
x[k] = (uint)tmp;
}
if (k == (z - 1) & MASK && k != a && !x[k]) z = k;
if (k == a) break;
}
if (carry)
{
rp += 9;
a = (a - 1) & MASK;
if (a == z)
{
z = (z - 1) & MASK;
x[(z - 1) & MASK] |= x[z];
}
x[a] = carry;
}
}
// Downscale until exactly number of bits are left of radix point
while (true)
{
uint carry = 0;
int sh = 1;
int i;
for (i = 0; i < B1B_DIG; i++)
{
k = (a + i) & MASK;
if (k == z || x[k] < TH[i])
{
i = B1B_DIG;
break;
}
if (x[(a + i) & MASK] > TH[i]) break;
}
if (i == B1B_DIG && rp == 9 * B1B_DIG) break;
if (rp > 9 + 9 * B1B_DIG) sh = 9;
e2 += sh;
for (k = a; k != z; k = (k+1) & MASK)
{
uint tmp = x[k] & (1 << sh - 1);
x[k] = x[k] >> sh + carry;
carry = (1000000000 >> sh) * tmp;
if (k == a && !x[k])
{
a = (a + 1) & MASK;
i--;
rp -= 9;
}
}
if (carry)
{
if ((z + 1) & MASK != a)
{
x[z] = carry;
z = (z + 1) & MASK;
}
else
{
x[(z - 1) & MASK] |= 1;
}
}
}
// Assemble desired bits into floating point variable
double y;
int i;
for (i = 0; i < B1B_DIG; i++)
{
if ((a + i) & MASK == z) x[(z = (z + 1) & MASK) - 1] = 0;
y = 1000000000.0 * y + x[(a + i) & MASK];
}
y *= sign;
bool denormal;
/* Limit precision for denormal results */
uint bits = $bits;
if (bits > math::DOUBLE_MANT_DIG + e2 - $emin)
{
bits = math::DOUBLE_MANT_DIG + e2 - $emin;
if (bits < 0) bits = 0;
denormal = true;
}
// Calculate bias term to force rounding, move out lower bits
double bias;
double frac;
if (bits < math::DOUBLE_MANT_DIG)
{
bias = math::copysign(math::scalbn(1, 2 * math::DOUBLE_MANT_DIG - bits - 1), y);
frac = y % math::scalbn(1, math::DOUBLE_MANT_DIG - bits);
y -= frac;
y += bias;
}
// Process tail of decimal input so it can affect rounding
if ((a + i) & MASK != z)
{
uint t = x[(a + i) & MASK];
switch
{
case t < 500000000 && (t || (a + i + 1) & MASK != z):
frac += 0.25 * sign;
case t > 500000000:
frac += 0.75 * sign;
case t == 500000000:
if ((a + i + 1) & MASK == z)
{
frac += 0.5 * sign;
}
else
{
frac += 0.75 * sign;
}
}
if (math::DOUBLE_MANT_DIG - bits >= 2 && !(frac % 1)) frac++;
}
y += frac;
y -= bias;
if (((e2 + math::DOUBLE_MANT_DIG) & int.max) > emax - 5)
{
if (math::abs(y) >= 0x1p53)
{
if (denormal && bits == math::DOUBLE_MANT_DIG + e2 - $emin) denormal = false;
y *= 0.5;
e2++;
}
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT?;
}
return math::scalbn(y, e2);
}
macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
{
double scale = 1;
uint x;
long rp;
long dc;
char c @noinit;
bool got_rad;
bool got_digit;
bool got_tail;
usz len = chars.len;
usz last_char = len - 1;
usz index;
double y;
// Skip past first characters
while ((c = chars[index]) == '0')
{
if (index == last_char) return 0.0;
index++;
}
if (c == '.')
{
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
{
rp--;
got_digit = true;
while (last_char != index && (c = chars[++index]) == '0')
{
rp--;
}
}
}
while ((c - '0') < 10u || ((c | 32) - 'a') < 6u || c == '.')
{
if (c == '.')
{
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
got_rad = true;
rp = dc;
}
else
{
got_digit = true;
int d = {|
if (c > '9') return (c | 32) + 10 - 'a';
return c - '0';
|};
switch
{
case dc < 8:
x = x * 16 + d;
case dc < math::DOUBLE_MANT_DIG / 4 + 1:
y += d * (scale /= 16);
got_tail = true;
case d && !got_tail:
y += 0.5 * scale;
got_tail = true;
}
dc++;
}
if (index == last_char) break;
c = chars[++index];
}
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if (!got_rad) rp = dc;
for (; dc < 8; dc++) x *= 16;
long e2;
if ((c | 32) == 'p')
{
long e2val = String.to_long((String)chars[index + 1..]) ?? (NumberConversion.MALFORMED_FLOAT?)!;
e2 = e2val;
}
e2 += 4 * rp - 32;
if (!x) return sign * 0.0;
if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
while (x < 0x80000000)
{
if (y >= 0.5)
{
x += x + 1;
y += y - 1;
}
else
{
x += x;
y += y;
}
e2--;
}
int bits = $bits;
if ($bits > 32 + e2 - $emin)
{
bits = (int)(32 + e2 - $emin);
if (bits < 0) bits = 0;
}
double bias;
if (bits < math::DOUBLE_MANT_DIG)
{
bias = math::copysign(math::scalbn(1, 32 + math::DOUBLE_MANT_DIG - bits - 1), (double)sign);
}
if (bits < 32 && y && !(x & 1))
{
x++;
y = 0;
}
y = bias + sign * (double)x + sign * y;
y -= bias;
if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE?;
return math::scalbn(y, (int)e2);
}
macro String.to_real(String chars, $Type) @private
{
int sign = 1;
$switch ($Type)
$case float:
const int BITS = math::FLOAT_MANT_DIG;
const int EMIN = math::FLOAT_MIN_EXP - BITS;
$case double:
const int BITS = math::DOUBLE_MANT_DIG;
const int EMIN = math::DOUBLE_MIN_EXP - BITS;
$case float128:
$error "Not yet supported";
$default:
$error "Unexpected type";
$endswitch
while (chars.len && chars[0] == ' ') chars = chars[1..];
if (!chars.len) return NumberConversion.MALFORMED_FLOAT?;
switch (chars[0])
{
case '-':
sign = -1;
nextcase;
case '+':
chars = chars[1..];
}
if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf;
if (chars == "NAN" || chars == "nan") return $Type.nan;
if (chars.len > 2 && chars[0] == '0' && (chars[1] | 32) == 'x')
{
return ($Type)hexfloat((char[])chars[2..], BITS, EMIN, sign);
}
return ($Type)decfloat((char[])chars, BITS, EMIN, sign);
}

275
lib/std/core/types.c3 Normal file
View File

@@ -0,0 +1,275 @@
module std::core::types;
import libc;
fault ConversionResult
{
VALUE_OUT_OF_RANGE,
VALUE_OUT_OF_UNSIGNED_RANGE,
}
/**
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
**/
macro any_to_int(any v, $Type)
{
typeid any_type = v.type;
TypeKind kind = any_type.kindof;
if (kind == TypeKind.ENUM)
{
any_type = any_type.inner;
kind = any_type.kindof;
}
bool is_mixed_signed = $Type.kindof != any_type.kindof;
$Type max = $Type.max;
$Type min = $Type.min;
switch (any_type)
{
case ichar:
ichar c = *(char*)v.ptr;
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
return ($Type)c;
case short:
short s = *(short*)v.ptr;
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)s;
case int:
int i = *(int*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case long:
long l = *(long*)v.ptr;
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)l;
case int128:
int128 i = *(int128*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case char:
char c = *(char*)v.ptr;
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)c;
case ushort:
ushort s = *(ushort*)v.ptr;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)s;
case uint:
uint i = *(uint*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case ulong:
ulong l = *(ulong*)v.ptr;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)l;
case uint128:
uint128 i = *(uint128*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
default:
unreachable();
}
}
macro bool is_numerical($Type)
{
var $kind = $Type.kindof;
$if $kind == TypeKind.DISTINCT:
return is_numerical($Type.inner);
$else
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|| $kind == TypeKind.VECTOR;
$endif
}
fn bool TypeKind.is_int(TypeKind kind) @inline
{
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
}
macro bool is_indexable($Type)
{
return $checks($Type t, int i, t[i]);
}
macro bool is_comparable($Type)
{
var $kind = $Type.kindof;
$if $kind == TypeKind.DISTINCT:
return is_comparable($Type.inner);
$else
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|| $kind == TypeKind.VECTOR || $kind == TypeKind.BOOL || $kind == TypeKind.POINTER
|| $kind == TypeKind.ENUM;
$endif
}
macro bool is_equatable($Type)
{
return $checks($Type a, a == a);
}
macro bool is_subarray_convertable($Type)
{
$switch ($Type.kindof)
$case SUBARRAY:
return true;
$case POINTER:
return $Type.inner.kindof == TypeKind.ARRAY;
$default:
return false;
$endswitch
}
macro bool is_bool($Type) => $Type.kindof == TypeKind.BOOL;
macro bool is_int($Type) => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
macro bool is_intlike($Type)
{
$switch ($Type.kindof)
$case SIGNED_INT:
$case UNSIGNED_INT:
return true;
$case VECTOR:
return $Type.inner.kindof == TypeKind.SIGNED_INT || $Type.inner.kindof == TypeKind.UNSIGNED_INT;
$default:
return false;
$endswitch
}
macro bool is_float($Type) => $Type.kindof == TypeKind.FLOAT;
macro bool is_floatlike($Type)
{
$switch ($Type.kindof)
$case FLOAT:
return true;
$case VECTOR:
return $Type.inner.kindof == TypeKind.FLOAT;
$default:
return false;
$endswitch
}
macro bool is_vector($Type)
{
return $Type.kindof == TypeKind.VECTOR;
}
macro TypeKind inner_kind($Type)
{
$if $Type.kindof == TypeKind.DISTINCT:
return inner_kind($typefrom($Type.inner));
$else
return $Type.kindof;
$endif
}
macro bool @convertable(#a, $TypeB) @builtin
{
return $checks($TypeB x = #a);
}
macro bool is_same($TypeA, $TypeB)
{
return $TypeA.typeid == $TypeB.typeid;
}
macro bool @has_same(#a, #b, ...)
{
var $type_a = $typeof(#a).typeid;
$if $type_a != $typeof(#b).typeid:
return false;
$endif
$for (var $i = 0; $i < $vacount; $i++)
$if $typeof($vaexpr($i)).typeid != $type_a:
return false;
$endif
$endfor
return true;
}
macro bool may_load_atomic($Type)
{
$switch ($Type.kindof)
$case SIGNED_INT:
$case UNSIGNED_INT:
$case POINTER:
$case FLOAT:
return true;
$case DISTINCT:
return may_load_atomic($Type.inner);
$default:
return false;
$endswitch
}
macro bool is_promotable_to_floatlike($Type) => types::is_floatlike($Type) || types::is_int($Type);
macro bool is_promotable_to_float($Type) => types::is_float($Type) || types::is_int($Type);
macro bool is_same_vector_type($Type1, $Type2)
{
$if $Type1.kindof != TypeKind.VECTOR:
return $Type2.kindof != TypeKind.VECTOR;
$else
return $Type1.inner == $Type2.inner && $Type1.len == $Type2.len;
$endif
}
macro bool is_equatable_type($Type)
{
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
return true;
$else
return is_equatable($Type);
$endif
}
macro bool is_equatable_value(value)
{
return is_equatable_type($typeof(value));
}
macro bool is_comparable_value(value)
{
$if $defined(value.less) || $defined(value.compare_to):
return true;
$else
return is_comparable($typeof(value));
$endif
}
enum TypeKind : char
{
VOID,
BOOL,
SIGNED_INT,
UNSIGNED_INT,
FLOAT,
TYPEID,
ANYFAULT,
ANY,
ENUM,
FAULT,
STRUCT,
UNION,
BITSTRUCT,
FUNC,
OPTIONAL,
ARRAY,
SUBARRAY,
VECTOR,
DISTINCT,
POINTER,
}
struct TypeEnum
{
TypeKind type;
usz elements;
}

23
lib/std/core/values.c3 Normal file
View File

@@ -0,0 +1,23 @@
module std::core::values;
macro TypeKind @typekind(#value) @builtin => $typeof(#value).kindof;
macro bool @typeis(#value, $Type) @builtin => $typeof(#value).typeid == $Type.typeid;
macro bool @is_bool(#value) => types::is_bool($typeof(#value));
macro bool @is_int(#value) => types::is_int($typeof(#value));
macro bool @convertable_to(#a, #b) => $checks($typeof(#b) x = #a);
macro bool @is_floatlike(#value) => types::is_floatlike($typeof(#value));
macro bool @is_float(#value) => types::is_float($typeof(#value));
macro bool @is_promotable_to_floatlike(#value) => types::is_promotable_to_floatlike($typeof(#value));
macro bool @is_promotable_to_float(#value) => types::is_promotable_to_float($typeof(#value));
macro bool @is_same_vector_type(#value1, #value2) => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
macro promote_int(x)
{
$if @is_int(x):
return (double)x;
$else
return x;
$endif
}
macro TypeKind @inner_kind(#value) => types::inner_kind($typeof(#value));

2
lib/std/crypto/crypto.c3 Normal file
View File

@@ -0,0 +1,2 @@
module std::crypto;

65
lib/std/crypto/rc4.c3 Normal file
View File

@@ -0,0 +1,65 @@
module std::crypto::rc4;
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
struct Rc4
{
uint i, j;
char[256] state;
}
/**
* Initialize the RC4 state.
*
* @param [inout] this "The RC4 state"
* @param [in] key "The key to use"
* @require key.len > 0 "The key must be at least 1 byte long"
**/
fn void Rc4.init(Rc4* this, char[] key)
{
// Init the state matrix
foreach (char i, &c : this.state) *c = i;
for (int i = 0, int j = 0; i < 256; i++)
{
j = (j + this.state[i] + key[i % key.len]) & 0xFF;
@swap(this.state[i], this.state[j]);
}
this.i = 0;
this.j = 0;
}
/**
* Encrypt or decrypt a sequence of bytes.
*
* @param [inout] this "The RC4 State"
* @param [in] in "The input"
* @param [out] out "The output"
* @require in.len <= out.len "Output would overflow"
**/
fn void Rc4.crypt(Rc4* this, char[] in, char[] out)
{
uint i = this.i;
uint j = this.j;
char* state = &this.state;
isz len = in.len;
foreach (idx, c : in)
{
i = (i + 1) & 0xFF;
j = (j + state[i]) & 0xFF;
@swap(state[i], state[j]);
out[idx] = in[idx] ^ state[(state[i] + state[j]) & 0xFF];
}
this.i = i;
this.j = j;
}
/**
* Clear the rc4 state.
*
* @param [out] this "The RC4 State"
**/
fn void Rc4.destroy(Rc4* this)
{
*this = {};
}

353
lib/std/encoding/json.c3 Normal file
View File

@@ -0,0 +1,353 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::encoding::json;
import std::io;
import std::ascii;
import std::collections::object;
enum JsonTokenType
{
NO_TOKEN,
LBRACE,
LBRACKET,
COMMA,
COLON,
RBRACE,
RBRACKET,
STRING,
NUMBER,
TRUE,
FALSE,
NULL,
EOF,
}
struct JsonParser
{
uint line;
Stream stream;
Allocator* allocator;
JsonTokenType token;
DString last_string;
double last_number;
char current;
anyfault current_err;
bool skip_comments;
bool reached_end;
}
fault JsonParsingError
{
EOF,
UNEXPECTED_CHARACTER,
INVALID_ESCAPE_SEQUENCE,
DUPLICATE_MEMBERS,
INVALID_NUMBER,
}
fn void JsonParser.init(JsonParser* parser, Stream s, Allocator* using = mem::heap())
{
*parser = { .last_string = dstring::new_with_capacity(64, using), .stream = s, .allocator = using };
}
fn Object*! JsonParser.parse_from_token(JsonParser* this, JsonTokenType token)
{
switch (token)
{
case NO_TOKEN: unreachable();
case LBRACE: return this.parse_map();
case LBRACKET: return this.parse_array();
case COMMA:
case RBRACE:
case RBRACKET:
case COLON: return JsonParsingError.UNEXPECTED_CHARACTER?;
case STRING: return object::new_string(this.last_string.str(), this.allocator);
case NUMBER: return object::new_float(this.last_number, this.allocator);
case TRUE: return object::new_bool(true);
case FALSE: return object::new_bool(false);
case NULL: return object::new_null();
case EOF: return JsonParsingError.EOF?;
}
unreachable();
}
fn Object*! JsonParser.parse_any(JsonParser* this)
{
return this.parse_from_token(this.advance());
}
fn JsonTokenType! JsonParser.lex_number(JsonParser* this, char c)
{
@pool()
{
DString t = dstring::tnew_with_capacity(32);
bool negate = c == '-';
if (negate)
{
t.append(c);
c = this.read_next()!;
}
while (c >= '0' && c <= '9')
{
t.append(c);
c = this.read_next()!;
}
if (c == '.')
{
t.append(c);
while (c = this.read_next()!, c >= '0' && c <= '9')
{
t.append(c);
}
}
if ((c | 32) == 'e')
{
t.append(c);
c = this.read_next()!;
switch (c)
{
case '-':
case '+':
t.append(c);
c = this.read_next()!;
}
if (c < '0' || c > '9') return JsonParsingError.INVALID_NUMBER?;
while (c >= '0' && c <= '9')
{
t.append(c);
c = this.read_next()!;
}
}
this.pushback();
double! d = t.str().to_double() ?? JsonParsingError.INVALID_NUMBER?;
this.last_number = d!;
return NUMBER;
};
}
fn Object*! JsonParser.parse_map(JsonParser* this)
{
Object* map = object::new_obj(this.allocator);
JsonTokenType token = this.advance()!;
defer catch map.free();
DString temp_key = dstring::new_with_capacity(32, this.allocator);
defer temp_key.free();
while (token != JsonTokenType.RBRACE)
{
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
DString string = this.last_string;
if (map.has_key(string.str())) return JsonParsingError.DUPLICATE_MEMBERS?;
// Copy the key to our temp holder. We do this to work around the issue
// if the temp allocator should be used as the default allocator.
temp_key.clear();
temp_key.append(string);
this.parse_expected(COLON)!;
Object* element = this.parse_any()!;
map.set(temp_key.str(), element);
token = this.advance()!;
if (token == JsonTokenType.COMMA)
{
token = this.advance()!;
continue;
}
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
return map;
}
fn Object*! JsonParser.parse_array(JsonParser* this)
{
Object* list = object::new_obj(this.allocator);
defer catch list.free();
JsonTokenType token = this.advance()!;
while (token != JsonTokenType.RBRACKET)
{
Object* element = this.parse_from_token(token)!;
list.append(element);
token = this.advance()!;
if (token == JsonTokenType.COMMA)
{
token = this.advance()!;
continue;
}
if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
return list;
}
fn void JsonParser.pushback(JsonParser* this)
{
if (!this.reached_end) this.stream.pushback_byte()!!;
}
fn char! JsonParser.read_next(JsonParser* this)
{
if (this.reached_end) return '\0';
char! c = this.stream.read_byte();
if (catch err = c)
{
case IoError.EOF:
this.reached_end = true;
return '\0';
default:
return err?;
}
if (c == 0)
{
this.reached_end = true;
}
return c;
}
fn JsonTokenType! JsonParser.advance(JsonParser* this)
{
char c;
// Skip whitespace
while WS: (c = this.read_next()!)
{
switch (c)
{
case '\n':
this.line++;
nextcase;
case ' ':
case '\t':
case '\r':
case '\v':
continue;
case '/':
if (!this.skip_comments) break;
c = this.read_next()!;
if (c != '*')
{
this.pushback();
break WS;
}
while COMMENT: (1)
{
// Skip to */
while (c = this.read_next()!)
{
if (c == '\n') this.line++;
if (c != '*') continue;
// Skip through all the '*'
while (c = this.read_next()!)
{
if (c == '\n') this.line++;
if (c != '*') break;
}
if (c == '/') break COMMENT;
}
}
continue;
default:
break WS;
}
}
switch (c)
{
case '\0':
return IoError.EOF?;
case '{':
return LBRACE;
case '}':
return RBRACE;
case '[':
return LBRACKET;
case ']':
return RBRACKET;
case ':':
return COLON;
case ',':
return COMMA;
case '"':
return this.lex_string();
case '-':
case '0'..'9':
return this.lex_number(c);
case 't':
this.match("rue")!;
return TRUE;
case 'f':
this.match("alse")!;
return FALSE;
case 'n':
this.match("ull")!;
return NULL;
default:
return JsonParsingError.UNEXPECTED_CHARACTER?;
}
}
fn void! JsonParser.match(JsonParser* this, String str)
{
foreach (c : str)
{
char l = this.read_next()!;
if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
}
fn void! JsonParser.parse_expected(JsonParser* this, JsonTokenType token) @local
{
if (this.advance()! != token) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
{
this.last_string.clear();
while LOOP: (1)
{
char c = this.read_next()!;
switch (c)
{
case '\0':
return JsonParsingError.EOF?;
case 1..31:
return JsonParsingError.UNEXPECTED_CHARACTER?;
case '"':
break LOOP;
case '\\':
break;
default:
this.last_string.append(c);
continue;
}
c = this.read_next()!;
switch (c)
{
case '\0':
return JsonParsingError.EOF?;
case 1..31:
return JsonParsingError.UNEXPECTED_CHARACTER?;
case '"':
case '\\':
case '/':
break;
case 'b':
c = '\b';
case 'f':
c = '\f';
case 'n':
c = '\n';
case 'r':
c = '\r';
case 't':
c = '\t';
case 'u':
uint val;
for (int i = 0; i < 4; i++)
{
c = this.read_next()!;
if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
val = val << 4 + (c > '9' ? (c | 32) - 'a' + 10 : c - '0');
}
this.last_string.append_char32(val);
continue;
default:
return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
}
}
return STRING;
}

53
lib/std/hash/adler32.c3 Normal file
View File

@@ -0,0 +1,53 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::hash::adler32;
const uint ADLER_CONST @private = 65521;
struct Adler32
{
uint a;
uint b;
}
fn void Adler32.init(Adler32 *this)
{
*this = { 1, 0 };
}
fn void Adler32.updatec(Adler32* this, char c)
{
this.a = (this.a + c) % ADLER_CONST;
this.b = (this.b + this.a) % ADLER_CONST;
}
fn void Adler32.update(Adler32* this, char[] data)
{
uint a = this.a;
uint b = this.b;
foreach (char x : data)
{
a = (a + x) % ADLER_CONST;
b = (b + a) % ADLER_CONST;
}
*this = { a, b };
}
fn uint Adler32.final(Adler32* this)
{
return (this.b << 16) | this.a;
}
fn uint encode(char[] data)
{
uint a = 1;
uint b = 0;
foreach (char x : data)
{
a = (a + x) % ADLER_CONST;
b = (b + a) % ADLER_CONST;
}
return (b << 16) | a;
}

111
lib/std/hash/crc32.c3 Normal file
View File

@@ -0,0 +1,111 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::hash::crc32;
struct Crc32
{
uint result;
}
fn void Crc32.init(Crc32* this, uint seed = 0)
{
this.result = ~seed;
}
fn void Crc32.updatec(Crc32* this, char c)
{
this.result = (this.result >> 8) ^ CRC32_TABLE[(this.result ^ c) & 0xFF];
}
fn void Crc32.update(Crc32* this, char[] data)
{
uint result = this.result;
foreach (char x : data)
{
result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF];
}
this.result = result;
}
fn uint Crc32.final(Crc32* this)
{
return ~this.result;
}
fn uint encode(char[] data)
{
uint result = ~(uint)(0);
foreach (char x : data)
{
result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF];
}
return ~result;
}
const uint[256] CRC32_TABLE @private = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};

111
lib/std/hash/crc64.c3 Normal file
View File

@@ -0,0 +1,111 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::hash::crc64;
struct Crc64
{
ulong result;
}
fn void Crc64.init(Crc64* this, uint seed = 0)
{
this.result = seed;
}
fn void Crc64.updatec(Crc64* this, char c)
{
this.result = (this.result << 8) ^ CRC64_TABLE[(char)((this.result >> 56) ^ c)];
}
fn void Crc64.update(Crc64* this, char[] data)
{
ulong result = this.result;
foreach (char x : data)
{
result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)];
}
this.result = result;
}
fn ulong Crc64.final(Crc64* this)
{
return this.result;
}
fn ulong encode(char[] data)
{
ulong result = (ulong)(0);
foreach (char x : data)
{
result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)];
}
return result;
}
const ulong[256] CRC64_TABLE @private = {
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
0xdb55aacf12c73561, 0x99a54b24bb2d03f2, 0x5eb4691841135847, 0x1c4488f3e8f96ed4,
0x663d78ff90e185ef, 0x24cd9914390bb37c, 0xe3dcbb28c335e8c9, 0xa12c5ac36adfde5a,
0x2f0e1eba9ea36930, 0x6dfeff5137495fa3, 0xaaefdd6dcd770416, 0xe81f3c86649d3285,
0xf45bb4758c645c51, 0xb6ab559e258e6ac2, 0x71ba77a2dfb03177, 0x334a9649765a07e4,
0xbd68d2308226b08e, 0xff9833db2bcc861d, 0x388911e7d1f2dda8, 0x7a79f00c7818eb3b,
0xcc7af1ff21c30bde, 0x8e8a101488293d4d, 0x499b3228721766f8, 0x0b6bd3c3dbfd506b,
0x854997ba2f81e701, 0xc7b97651866bd192, 0x00a8546d7c558a27, 0x4258b586d5bfbcb4,
0x5e1c3d753d46d260, 0x1cecdc9e94ace4f3, 0xdbfdfea26e92bf46, 0x990d1f49c77889d5,
0x172f5b3033043ebf, 0x55dfbadb9aee082c, 0x92ce98e760d05399, 0xd03e790cc93a650a,
0xaa478900b1228e31, 0xe8b768eb18c8b8a2, 0x2fa64ad7e2f6e317, 0x6d56ab3c4b1cd584,
0xe374ef45bf6062ee, 0xa1840eae168a547d, 0x66952c92ecb40fc8, 0x2465cd79455e395b,
0x3821458aada7578f, 0x7ad1a461044d611c, 0xbdc0865dfe733aa9, 0xff3067b657990c3a,
0x711223cfa3e5bb50, 0x33e2c2240a0f8dc3, 0xf4f3e018f031d676, 0xb60301f359dbe0e5,
0xda050215ea6c212f, 0x98f5e3fe438617bc, 0x5fe4c1c2b9b84c09, 0x1d14202910527a9a,
0x93366450e42ecdf0, 0xd1c685bb4dc4fb63, 0x16d7a787b7faa0d6, 0x5427466c1e109645,
0x4863ce9ff6e9f891, 0x0a932f745f03ce02, 0xcd820d48a53d95b7, 0x8f72eca30cd7a324,
0x0150a8daf8ab144e, 0x43a04931514122dd, 0x84b16b0dab7f7968, 0xc6418ae602954ffb,
0xbc387aea7a8da4c0, 0xfec89b01d3679253, 0x39d9b93d2959c9e6, 0x7b2958d680b3ff75,
0xf50b1caf74cf481f, 0xb7fbfd44dd257e8c, 0x70eadf78271b2539, 0x321a3e938ef113aa,
0x2e5eb66066087d7e, 0x6cae578bcfe24bed, 0xabbf75b735dc1058, 0xe94f945c9c3626cb,
0x676dd025684a91a1, 0x259d31cec1a0a732, 0xe28c13f23b9efc87, 0xa07cf2199274ca14,
0x167ff3eacbaf2af1, 0x548f120162451c62, 0x939e303d987b47d7, 0xd16ed1d631917144,
0x5f4c95afc5edc62e, 0x1dbc74446c07f0bd, 0xdaad56789639ab08, 0x985db7933fd39d9b,
0x84193f60d72af34f, 0xc6e9de8b7ec0c5dc, 0x01f8fcb784fe9e69, 0x43081d5c2d14a8fa,
0xcd2a5925d9681f90, 0x8fdab8ce70822903, 0x48cb9af28abc72b6, 0x0a3b7b1923564425,
0x70428b155b4eaf1e, 0x32b26afef2a4998d, 0xf5a348c2089ac238, 0xb753a929a170f4ab,
0x3971ed50550c43c1, 0x7b810cbbfce67552, 0xbc902e8706d82ee7, 0xfe60cf6caf321874,
0xe224479f47cb76a0, 0xa0d4a674ee214033, 0x67c58448141f1b86, 0x253565a3bdf52d15,
0xab1721da49899a7f, 0xe9e7c031e063acec, 0x2ef6e20d1a5df759, 0x6c0603e6b3b7c1ca,
0xf6fae5c07d3274cd, 0xb40a042bd4d8425e, 0x731b26172ee619eb, 0x31ebc7fc870c2f78,
0xbfc9838573709812, 0xfd39626eda9aae81, 0x3a28405220a4f534, 0x78d8a1b9894ec3a7,
0x649c294a61b7ad73, 0x266cc8a1c85d9be0, 0xe17dea9d3263c055, 0xa38d0b769b89f6c6,
0x2daf4f0f6ff541ac, 0x6f5faee4c61f773f, 0xa84e8cd83c212c8a, 0xeabe6d3395cb1a19,
0x90c79d3fedd3f122, 0xd2377cd44439c7b1, 0x15265ee8be079c04, 0x57d6bf0317edaa97,
0xd9f4fb7ae3911dfd, 0x9b041a914a7b2b6e, 0x5c1538adb04570db, 0x1ee5d94619af4648,
0x02a151b5f156289c, 0x4051b05e58bc1e0f, 0x87409262a28245ba, 0xc5b073890b687329,
0x4b9237f0ff14c443, 0x0962d61b56fef2d0, 0xce73f427acc0a965, 0x8c8315cc052a9ff6,
0x3a80143f5cf17f13, 0x7870f5d4f51b4980, 0xbf61d7e80f251235, 0xfd913603a6cf24a6,
0x73b3727a52b393cc, 0x31439391fb59a55f, 0xf652b1ad0167feea, 0xb4a25046a88dc879,
0xa8e6d8b54074a6ad, 0xea16395ee99e903e, 0x2d071b6213a0cb8b, 0x6ff7fa89ba4afd18,
0xe1d5bef04e364a72, 0xa3255f1be7dc7ce1, 0x64347d271de22754, 0x26c49cccb40811c7,
0x5cbd6cc0cc10fafc, 0x1e4d8d2b65facc6f, 0xd95caf179fc497da, 0x9bac4efc362ea149,
0x158e0a85c2521623, 0x577eeb6e6bb820b0, 0x906fc95291867b05, 0xd29f28b9386c4d96,
0xcedba04ad0952342, 0x8c2b41a1797f15d1, 0x4b3a639d83414e64, 0x09ca82762aab78f7,
0x87e8c60fded7cf9d, 0xc51827e4773df90e, 0x020905d88d03a2bb, 0x40f9e43324e99428,
0x2cffe7d5975e55e2, 0x6e0f063e3eb46371, 0xa91e2402c48a38c4, 0xebeec5e96d600e57,
0x65cc8190991cb93d, 0x273c607b30f68fae, 0xe02d4247cac8d41b, 0xa2dda3ac6322e288,
0xbe992b5f8bdb8c5c, 0xfc69cab42231bacf, 0x3b78e888d80fe17a, 0x7988096371e5d7e9,
0xf7aa4d1a85996083, 0xb55aacf12c735610, 0x724b8ecdd64d0da5, 0x30bb6f267fa73b36,
0x4ac29f2a07bfd00d, 0x08327ec1ae55e69e, 0xcf235cfd546bbd2b, 0x8dd3bd16fd818bb8,
0x03f1f96f09fd3cd2, 0x41011884a0170a41, 0x86103ab85a2951f4, 0xc4e0db53f3c36767,
0xd8a453a01b3a09b3, 0x9a54b24bb2d03f20, 0x5d45907748ee6495, 0x1fb5719ce1045206,
0x919735e51578e56c, 0xd367d40ebc92d3ff, 0x1476f63246ac884a, 0x568617d9ef46bed9,
0xe085162ab69d5e3c, 0xa275f7c11f7768af, 0x6564d5fde549331a, 0x279434164ca30589,
0xa9b6706fb8dfb2e3, 0xeb46918411358470, 0x2c57b3b8eb0bdfc5, 0x6ea7525342e1e956,
0x72e3daa0aa188782, 0x30133b4b03f2b111, 0xf7021977f9cceaa4, 0xb5f2f89c5026dc37,
0x3bd0bce5a45a6b5d, 0x79205d0e0db05dce, 0xbe317f32f78e067b, 0xfcc19ed95e6430e8,
0x86b86ed5267cdbd3, 0xc4488f3e8f96ed40, 0x0359ad0275a8b6f5, 0x41a94ce9dc428066,
0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9,
0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8,
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
};

41
lib/std/hash/fnv32a.c3 Normal file
View File

@@ -0,0 +1,41 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::hash::fnv32a;
def Fnv32a = distinct uint;
const FNV32A_START @private = 0x811c9dc5;
const FNV32A_MUL @private = 0x01000193;
macro void @update(uint &h, char x) @private => h = (h * FNV32A_MUL) ^ x;
fn void Fnv32a.init(Fnv32a* this)
{
*this = FNV32A_START;
}
fn void Fnv32a.update(Fnv32a* this, char[] data)
{
uint h = (uint)*this;
foreach (char x : data)
{
@update(h, x);
}
*this = (Fnv32a)h;
}
macro void Fnv32a.update_char(Fnv32a* this, char c)
{
@update(*this, x);
}
fn uint encode(char[] data)
{
uint h = FNV32A_START;
foreach (char x : data)
{
@update(h, x);
}
return h;
}

237
lib/std/hash/sha1.c3 Normal file
View File

@@ -0,0 +1,237 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// Implementation was off Steve Reid's SHA-1 C implementation
module std::hash::sha1;
import std::bits;
struct Sha1
{
uint[5] state;
uint[2] count;
char[64] buffer;
}
fn void Sha1.init(Sha1* this)
{
// SHA1 initialization constants
*this = {
.state = {
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0
}
};
}
/**
* @param [&inout] this
* @param [in] data
* @require data.len <= uint.max
**/
fn void Sha1.update(Sha1* this, char[] data)
{
uint j = this.count[0];
uint len = data.len;
if ((this.count[0] += len << 3) < j) this.count[1]++;
this.count[1] += len >> 29;
j = (j >> 3) & 63;
uint i;
if (j + len > 63)
{
i = 64 - j;
this.buffer[j..] = data[:i];
sha1_transform(&this.state, &this.buffer);
for (; i + 63 < len; i += 64)
{
sha1_transform(&this.state, &data[i]);
}
j = 0;
}
this.buffer[j:len - i] = data[i..];
}
fn char[20] Sha1.final(Sha1* this)
{
char[8] finalcount;
for (uint i = 0; i < 8; i++)
{
finalcount[i] = (char)((this.count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 0xFF);
}
this.update(char[] { 0o200 });
while ((this.count[0] & 504) != 448)
{
this.update(char[] { 0 });
}
this.update(&finalcount);
char[20] digest;
for (uint i = 0; i < 20; i++)
{
digest[i] = (char)((this.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF);
}
// Clear mem
mem::clear(this, Sha1.sizeof);
finalcount = {};
return digest;
}
union Long16 @local
{
char[64] c;
uint[16] l;
}
macro @blk(&block, i) @local
{
return (block.l[i & 15] = (block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15]
^ block.l[(i + 2) & 15] ^ block.l[i & 15]).rotl(1));
}
macro @blk0(&block, i) @local
{
$if env::BIG_ENDIAN:
return block.l[i];
$else
return block.l[i] = (block.l[i].rotl(24) & 0xFF00FF00)
| (block.l[i].rotl(8) & 0x00FF00FF);
$endif
}
macro @r0(&block, v, &w, x, y, &z, i) @local
{
z += ((w & (x ^ y)) ^ y) + @blk0(block, i) + 0x5A827999 + v.rotl(5);
w = w.rotl(30);
}
macro @r1(&block, v, &w, x, y, &z, i) @local
{
z += ((w & (x ^ y)) ^ y) + @blk(block, i) + 0x5A827999 + v.rotl(5);
w = w.rotl(30);
}
macro @r2(&block, v, &w, x, y, &z, i) @local
{
z += (w ^ x ^ y) + @blk(block, i) + 0x6ED9EBA1 + v.rotl(5);
w = w.rotl(30);
}
macro @r3(&block, v, &w, x, y, &z, i) @local
{
z += (((w | x) &y) | (w & x)) + @blk(block, i) + 0x8F1BBCDC + v.rotl(5);
w = w.rotl(30);
}
macro @r4(&block, v, &w, x, y, &z, i) @local
{
z += (w ^ x ^ y) + @blk(block, i) + 0xCA62C1D6 + v.rotl(5);
w = w.rotl(30);
}
/**
* @param [&inout] state
* @param [&in] buffer
**/
fn void sha1_transform(uint* state, char* buffer) @local
{
Long16 block;
block.c[..] = buffer[:64];
uint a = state[0];
uint b = state[1];
uint c = state[2];
uint d = state[3];
uint e = state[4];
@r0(block, a, b, c, d, e, 0);
@r0(block, e, a, b, c, d, 1);
@r0(block, d, e, a, b, c, 2);
@r0(block, c, d, e, a, b, 3);
@r0(block, b, c, d, e, a, 4);
@r0(block, a, b, c, d, e, 5);
@r0(block, e, a, b, c, d, 6);
@r0(block, d, e, a, b, c, 7);
@r0(block, c, d, e, a, b, 8);
@r0(block, b, c, d, e, a, 9);
@r0(block, a, b, c, d, e, 10);
@r0(block, e, a, b, c, d, 11);
@r0(block, d, e, a, b, c, 12);
@r0(block, c, d, e, a, b, 13);
@r0(block, b, c, d, e, a, 14);
@r0(block, a, b, c, d, e, 15);
@r1(block, e, a, b, c, d, 16);
@r1(block, d, e, a, b, c, 17);
@r1(block, c, d, e, a, b, 18);
@r1(block, b, c, d, e, a, 19);
@r2(block, a, b, c, d, e, 20);
@r2(block, e, a, b, c, d, 21);
@r2(block, d, e, a, b, c, 22);
@r2(block, c, d, e, a, b, 23);
@r2(block, b, c, d, e, a, 24);
@r2(block, a, b, c, d, e, 25);
@r2(block, e, a, b, c, d, 26);
@r2(block, d, e, a, b, c, 27);
@r2(block, c, d, e, a, b, 28);
@r2(block, b, c, d, e, a, 29);
@r2(block, a, b, c, d, e, 30);
@r2(block, e, a, b, c, d, 31);
@r2(block, d, e, a, b, c, 32);
@r2(block, c, d, e, a, b, 33);
@r2(block, b, c, d, e, a, 34);
@r2(block, a, b, c, d, e, 35);
@r2(block, e, a, b, c, d, 36);
@r2(block, d, e, a, b, c, 37);
@r2(block, c, d, e, a, b, 38);
@r2(block, b, c, d, e, a, 39);
@r3(block, a, b, c, d, e, 40);
@r3(block, e, a, b, c, d, 41);
@r3(block, d, e, a, b, c, 42);
@r3(block, c, d, e, a, b, 43);
@r3(block, b, c, d, e, a, 44);
@r3(block, a, b, c, d, e, 45);
@r3(block, e, a, b, c, d, 46);
@r3(block, d, e, a, b, c, 47);
@r3(block, c, d, e, a, b, 48);
@r3(block, b, c, d, e, a, 49);
@r3(block, a, b, c, d, e, 50);
@r3(block, e, a, b, c, d, 51);
@r3(block, d, e, a, b, c, 52);
@r3(block, c, d, e, a, b, 53);
@r3(block, b, c, d, e, a, 54);
@r3(block, a, b, c, d, e, 55);
@r3(block, e, a, b, c, d, 56);
@r3(block, d, e, a, b, c, 57);
@r3(block, c, d, e, a, b, 58);
@r3(block, b, c, d, e, a, 59);
@r4(block, a, b, c, d, e, 60);
@r4(block, e, a, b, c, d, 61);
@r4(block, d, e, a, b, c, 62);
@r4(block, c, d, e, a, b, 63);
@r4(block, b, c, d, e, a, 64);
@r4(block, a, b, c, d, e, 65);
@r4(block, e, a, b, c, d, 66);
@r4(block, d, e, a, b, c, 67);
@r4(block, c, d, e, a, b, 68);
@r4(block, b, c, d, e, a, 69);
@r4(block, a, b, c, d, e, 70);
@r4(block, e, a, b, c, d, 71);
@r4(block, d, e, a, b, c, 72);
@r4(block, c, d, e, a, b, 73);
@r4(block, b, c, d, e, a, 74);
@r4(block, a, b, c, d, e, 75);
@r4(block, e, a, b, c, d, 76);
@r4(block, d, e, a, b, c, 77);
@r4(block, c, d, e, a, b, 78);
@r4(block, b, c, d, e, a, 79);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
a = b = c = d = e = 0;
buffer[:64] = 0;
}

3
lib/std/io/dir.c3 Normal file
View File

@@ -0,0 +1,3 @@
module std::io::dir;
import std::io::os;

135
lib/std/io/io.c3 Normal file
View File

@@ -0,0 +1,135 @@
// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::io;
import libc;
struct File
{
CFile file;
}
enum Seek
{
SET,
CURSOR,
END
}
fault IoError
{
FILE_NOT_FOUND,
FILE_NOT_VALID,
INVALID_POSITION,
OVERFLOW,
FILE_IS_PIPE,
FILE_EOF,
INCOMPLETE_WRITE,
BUSY,
NO_PERMISSION,
OUT_OF_SPACE,
INVALID_PUSHBACK,
EOF,
CANNOT_READ_DIR,
TOO_MANY_DESCRIPTORS,
FILE_IS_DIR,
READ_ONLY,
FILE_NOT_DIR,
SYMLINK_FAILED,
ALREADY_EXISTS,
NOT_SEEKABLE,
NAME_TOO_LONG,
WOULD_BLOCK,
DIR_NOT_EMPTY,
INTERRUPTED,
GENERAL_ERROR,
UNKNOWN_ERROR,
UNSUPPORTED_OPERATION,
}
fn void putchar(char c) @inline
{
libc::putchar(c);
}
macro void print(x)
{
var $Type = $typeof(x);
$switch ($Type)
$case String:
(void)stdout().print(x);
$case ZString:
(void)stdout().print(x.as_str());
$case DString:
(void)stdout().print(x.str());
$default:
$if @convertible(x, String):
(void)stdout().print((String)x);
$else
(void)stdout().printf("%s", x);
$endif
$endswitch
}
macro void printn(x = "")
{
var $Type = $typeof(x);
$switch ($Type)
$case String:
(void)stdout().printn(x);
$case ZString:
(void)stdout().printn(x.as_str());
$case DString:
(void)stdout().printn(x.str());
$default:
$if @convertible(x, String):
(void)stdout().printn((String)x);
$else
(void)stdout().printfn("%s", x);
$endif
$endswitch
}
fn File stdout()
{
return { libc::stdout() };
}
fn File stderr()
{
return { libc::stderr() };
}
fn File stdin()
{
return { libc::stdin() };
}
/*
error FileError
{
ulong errno;
}
fn FileError errorFromErrno()
{
return FileError { };
}
pubic fn void! File.clearerr(File *file) @inline
{
clearerr(file->file);
}
fn void File.error(File *file) @inline
{
int err = ferror
}
*/

171
lib/std/io/io_file.c3 Normal file
View File

@@ -0,0 +1,171 @@
module std::io::file;
import libc;
fn File! open(String filename, String mode)
{
return { .file = os::native_fopen(filename, mode) };
}
fn File! open_path(Path path, String mode)
{
return { .file = os::native_fopen(path.as_str(), mode) };
}
/**
* @require file.file != null
**/
fn void! File.reopen(File* file, String filename, String mode)
{
file.file = os::native_freopen(file.file, filename, mode)!;
}
/**
* @require file.file != null
**/
fn usz! File.seek(File file, isz offset, Seek seek_mode = Seek.SET)
{
os::native_fseek(file.file, offset, seek_mode)!;
return os::native_ftell(file.file);
}
/*
Implement later
/**
* @require file.file == null
**/
fn void! File.memopen(File* file, char[] data, String mode)
{
@pool()
{
file.file = libc::memopen(data.ptr, data.len, mode.zstr_tcopy(), file.file);
// TODO errors
};
}
*/
/**
* @require file && file.file != null
*/
fn void! File.putc(File *file, char c)
{
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF?;
}
/**
* @require file != null
*/
fn void! File.close(File *file) @inline
{
if (file.file && libc::fclose(file.file))
{
switch (libc::errno())
{
case errno::ECONNRESET:
case errno::EBADF: return IoError.FILE_NOT_VALID?;
case errno::EINTR: return IoError.INTERRUPTED?;
case errno::EDQUOT:
case errno::EFAULT:
case errno::EAGAIN:
case errno::EFBIG:
case errno::ENETDOWN:
case errno::ENETUNREACH:
case errno::ENOSPC:
case errno::EIO: return IoError.INCOMPLETE_WRITE?;
default: return IoError.UNKNOWN_ERROR?;
}
}
file.file = null;
}
/**
* @require file && file.file
*/
fn bool File.eof(File* file) @inline
{
return libc::feof(file.file) != 0;
}
/**
* @param [in] buffer
*/
fn usz! File.read(File* file, char[] buffer)
{
return os::native_fread(file.file, buffer);
}
/**
* @param [&in] file
* @param [&out] buffer
* @require file.file `File must be initialized`
*/
fn usz! File.write(File file, char[] buffer)
{
return os::native_fwrite(file.file, buffer);
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn usz! File.printn(File file, String string = "")
{
usz len = file.print(string)!;
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR?;
return len + 1;
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn usz! File.print(File file, String string)
{
usz len = string.len;
if (len != file.write((char[])string)!) return IoError.UNKNOWN_ERROR?;
return len;
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn DString File.getline(File* file, Allocator* using = mem::heap())
{
DString s = dstring::new_with_capacity(120, using);
while (!file.eof())
{
int c = libc::fgetc(file.file);
if (c == -1) break;
if (c == '\n') break;
s.append_char((char)c);
}
return s;
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
* @return "a zero terminated String (the pointer may be safely cast into a ZString)"
*/
fn String File.tgetline(File* file)
{
return file.getline(mem::temp()).zstr().as_str();
}
fn char! File.getc(File* file)
{
int c = libc::fgetc(file.file);
if (c == -1) return IoError.FILE_EOF?;
return (char)c;
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn void File.flush(File* file)
{
libc::fflush(file.file);
}

14
lib/std/io/io_fileinfo.c3 Normal file
View File

@@ -0,0 +1,14 @@
module std::io::file;
import libc;
fn bool is_file(String path)
{
return os::native_is_file(path);
}
fn usz! get_size(String path)
{
return os::native_file_size(path);
}

View File

@@ -0,0 +1,667 @@
module std::io;
const char[16] XDIGITS_H = "0123456789ABCDEF";
const char[16] XDIGITS_L = "0123456789abcdef";
fn void! Formatter.left_adjust(Formatter* this, usz len) @local
{
if (!this.flags.left) return;
for (usz l = len; l < this.width; l++) this.out(' ')!;
}
fn void! Formatter.right_adjust(Formatter* this, usz len) @local
{
if (this.flags.left) return;
for (usz l = len; l < this.width; l++) this.out(' ')!;
}
fn uint128! int_from_any(any arg, bool *is_neg) @private
{
*is_neg = false;
if (arg.type.kindof == TypeKind.POINTER)
{
return (uint128)(uptr)*(void**)arg.ptr;
}
if (arg.type.kindof == TypeKind.DISTINCT)
{
return int_from_any(any { arg.ptr, arg.type.inner }, is_neg);
}
switch (arg)
{
case bool:
return (uint128)*arg;
case ichar:
int val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case short:
int val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int:
int val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case long:
long val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int128:
int128 val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case char:
return *arg;
case ushort:
return *arg;
case uint:
return *arg;
case ulong:
return *arg;
case uint128:
return *arg;
case float:
float f = *arg;
return (uint128)((*is_neg = f < 0) ? -f : f);
case double:
double d = *arg;
return (uint128)((*is_neg = d < 0) ? -d : d);
default:
return PrintFault.INVALID_ARGUMENT_TYPE?;
}
}
fn FloatType! float_from_any(any arg) @private
{
$if env::F128_SUPPORT:
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
$endif
$if env::F16_SUPPORT:
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
$endif
if (arg.type.kindof == TypeKind.DISTINCT)
{
return float_from_any(any { arg.ptr, arg.type.inner });
}
switch (arg)
{
case bool:
return (FloatType)*arg;
case ichar:
return *arg;
case short:
return *arg;
case int:
return *arg;
case long:
return *arg;
case int128:
return *arg;
case char:
return *arg;
case ushort:
return *arg;
case uint:
return *arg;
case ulong:
return *arg;
case uint128:
return *arg;
case float:
return (FloatType)*arg;
case double:
return (FloatType)*arg;
default:
return PrintFault.INVALID_ARGUMENT_TYPE?;
}
}
/**
* Read a simple integer value, typically for formatting.
*
* @param [inout] len_ptr "the length remaining."
* @param [in] buf "the buf to read from."
* @param maxlen "the maximum len that can be read."
* @return "The result of the atoi."
**/
fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private
{
uint i = 0;
usz len = *len_ptr;
while (len < maxlen)
{
char c = buf[len];
if (c < '0' || c > '9') break;
i = i * 10 + c - '0';
len++;
}
*len_ptr = len;
return i;
}
fn void! Formatter.out_substr(Formatter *this, String str) @private
{
usz l = conv::utf8_codepoints(str);
uint prec = this.prec;
if (this.flags.precision && l < prec) l = prec;
this.right_adjust(' ')!;
usz index = 0;
usz chars = str.len;
char* ptr = str.ptr;
while (index < chars)
{
char c = ptr[index];
// Break if we have precision set and we ran out...
if (c & 0xC0 != 0x80 && this.flags.precision && !prec--) break;
this.out(c)!;
index++;
}
return this.left_adjust(l);
}
fn void! Formatter.pad(Formatter* this, char c, isz width, isz len) @inline
{
for (isz i = len; i < width; i++) this.out(c)!;
}
fn char* fmt_u(uint128 x, char* s)
{
for (; x > ulong.max; x /= 10) *--s = '0' + (char)(x % 10);
for (ulong y = (ulong)x; y; y /= 10) *--s = '0' + (char)(y % 10);
return s;
}
fn void! Formatter.out_chars(Formatter* this, char[] s)
{
foreach (c : s) this.out(c)!;
}
enum FloatFormatting
{
FLOAT,
EXPONENTIAL,
ADAPTIVE,
HEX
}
fn void! Formatter.etoa(Formatter* this, double y) => this.floatformat(EXPONENTIAL, y);
fn void! Formatter.ftoa(Formatter* this, double y) => this.floatformat(FLOAT, y);
fn void! Formatter.gtoa(Formatter* this, double y) => this.floatformat(ADAPTIVE, y);
fn void! Formatter.atoa(Formatter* this, double y) => this.floatformat(HEX, y);
fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, double y) @private
{
// This code is heavily based on musl's printf code
const BUF_SIZE = (math::DOUBLE_MANT_DIG + 28) / 29 + 1
+ (math::DOUBLE_MAX_EXP + math::DOUBLE_MANT_DIG + 28 + 8) / 9;
uint[BUF_SIZE] big;
bool is_neg = false;
if (math::signbit(y))
{
is_neg = true;
y = -y;
}
int pl = is_neg || this.flags.plus ? 1 : 0;
// Print inf/nan
if (!math::is_finite(y))
{
// Add padding
if (!this.flags.left) this.pad(' ', this.width, 3 + pl)!;
String s = this.flags.uppercase ? "INF" : "inf";
if (y != y) this.flags.uppercase ? "NAN" : "nan";
if (pl) this.out(is_neg ? '-' : '+')!;
this.out_chars(s)!;
if (this.flags.left) this.pad(' ', this.width, 3 + pl)!;
return;
}
// Rescale
int e2;
y = math::frexp(y, &e2) * 2;
if (y) e2--;
char[12] ebuf0;
char* ebuf = 12 + (char*)&ebuf0;
char[9 + math::DOUBLE_MANT_DIG / 4] buf_array;
char* buf = &buf_array;
isz p = this.flags.precision ? this.prec : -1;
if (formatting == HEX)
{
double round = 8.0;
// 0x / 0X
pl += 2;
if (p > 0 && p < math::DOUBLE_MANT_DIG / 4 - 1)
{
int re = math::DOUBLE_MANT_DIG / 4 - 1 - (int)p;
round *= 1 << (math::DOUBLE_MANT_DIG % 4);
while (re--) round *= 16;
if (is_neg)
{
y = -y;
y -= round;
y += round;
y = -y;
}
else
{
y += round;
y -= round;
}
}
// Reverse print
char* estr = fmt_u(e2 < 0 ? (int128)-e2 : (int128)e2, ebuf);
if (estr == ebuf) *--estr = '0';
*--estr = (e2 < 0 ? '-' : '+');
*--estr = this.flags.uppercase ? 'P' : 'p';
char* s = buf;
char* xdigits = this.flags.uppercase ? &XDIGITS_H : &XDIGITS_L;
do
{
int x = (int)y;
*s++ = xdigits[x];
y = 16 * (y - x);
if (s - buf == 1 && (y || p > 0 || this.flags.hash)) *s++ = '.';
} while (y);
isz outlen = s - buf;
isz explen = ebuf - estr;
if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
usz l = p && outlen - 2 < p
? p + 2 + explen
: outlen + explen;
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
this.out_chars(this.flags.uppercase ? "0X" : "0x")!;
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
this.out_chars(buf[:outlen])!;
this.pad('0', l - outlen - explen, 0)!;
this.out_chars(estr[:explen])!;
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
return;
}
if (p < 0) p = 6;
if (y)
{
y *= 0x1p28;
e2 -= 28;
}
uint* a, z, r;
if (e2 < 0)
{
a = r = z = &big;
}
else
{
a = r = z = (uint*)&big + big.len - math::DOUBLE_MANT_DIG - 1;
}
do
{
uint v = z++[0] = (uint)y;
y = 1000000000 * (y - v);
} while (y);
while (e2 > 0)
{
uint carry = 0;
int sh = math::min(29, e2);
for (uint* d = z - 1; d >= a; d--)
{
ulong x = (ulong)*d << sh + carry;
*d = (uint)(x % 1000000000);
carry = (uint)(x / 1000000000);
}
if (carry) *--a = carry;
while (z > a && !z[-1]) z--;
e2 -= sh;
}
while (e2 < 0)
{
uint carry = 0;
uint* b;
int sh = math::min(9, -e2);
int need = (int)(1 + (p + math::DOUBLE_MANT_DIG / 3u + 8) / 9);
for (uint* d = a; d < z; d++)
{
// CHECK THIS
uint rm = *d & ((1 << sh) - 1);
*d = (*d >> sh) + carry;
carry = (1000000000 >> sh) * rm;
}
if (!a[0]) a++;
if (carry) z++[0] = carry;
// Avoid (slow!) computation past requested precision
b = formatting == FLOAT ? r : a;
if (z - b > need) z = b + need;
e2 += sh;
}
int e;
if (a < z)
{
for (int i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++);
}
// Perform rounding: j is precision after the radix (possibly neg)
int j = (int)(p - (isz)(formatting == FLOAT ? 0 : e - (int)(formatting == ADAPTIVE && p)));
if (j < 9 * (z - r - 1))
{
uint x;
// We avoid C's broken division of negative numbers
uint* d = r + 1 + ((j + 9 * math::DOUBLE_MAX_EXP) / 9 - math::DOUBLE_MAX_EXP);
j += 9 * math::DOUBLE_MAX_EXP;
j %= 9;
int i;
for (i = 10, j++; j < 9; i *= 10, j++);
x = *d % i;
// Are there any significant digits past j?
if (x || (d + 1) != z)
{
double round = 2 / math::DOUBLE_EPSILON;
double small;
if (((*d / i) & 1) || (i == 1000000000 && d > a && (d[-1] & 1)))
{
round += 2;
}
switch
{
case x < i / 2:
small = 0x0.8p0;
case x == i / 2 && d + 1 == z:
small = 0x1.0p0;
default:
small = 0x1.8p0;
}
if (pl && is_neg)
{
round *= -1;
small *= -1;
}
*d -= x;
// Decide whether to round by probing round+small
if (round + small != round)
{
*d = *d + i;
while (*d > 999999999)
{
*d-- = 0;
if (d < a) *--a = 0;
(*d)++;
}
for (i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++);
}
}
if (z > d + 1) z = d + 1;
}
for (; z>a && !z[-1]; z--);
if (formatting == ADAPTIVE)
{
if (!p) p++;
if (p > e && e >= -4)
{
formatting = FLOAT;
p -= (isz)e + 1;
}
else
{
formatting = EXPONENTIAL;
p--;
}
if (!this.flags.hash)
{
// Count trailing zeros in last place
if (z > a && z[-1])
{
for (int i = 10, j = 0; z[-1] % i == 0; i *= 10, j++);
}
else
{
j = 9;
}
if (formatting == FLOAT)
{
p = math::min(p, math::max((isz)0, 9 * (z - r - 1) - j));
}
else
{
p = math::min(p, math::max((isz)0, 9 * (z - r - 1) + e - j));
}
}
}
if (p > int.max - 1 - (isz)(p || this.flags.hash)) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
int l = (int)(1 + p + (isz)(p || this.flags.hash));
char* estr @noinit;
if (formatting == FLOAT)
{
if (e > int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (e > 0) l += e;
}
else
{
estr = fmt_u((uint128)(e < 0 ? -e : e), ebuf);
while (ebuf - estr < 2) (--estr)[0] = '0';
*--estr = (e < 0 ? '-' : '+');
*--estr = this.flags.uppercase ? 'E' : 'e';
if (ebuf - estr > (isz)int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
l += (int)(ebuf - estr);
}
if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
if (formatting == FLOAT)
{
if (a > r) a = r;
uint* d = a;
for (; d <= r; d++)
{
char* s = fmt_u(*d, buf + 9);
switch
{
case d != a:
while (s > buf) (--s)[0] = '0';
case s == buf + 9:
*--s = '0';
}
this.out_chars(s[:buf + 9 - s])!;
}
if (p || this.flags.hash) this.out('.')!;
for (; d < z && p > 0; d++, p -= 9)
{
char* s = fmt_u(*d, buf + 9);
while (s > buf) *--s = '0';
this.out_chars(s[:math::min((isz)9, p)])!;
}
this.pad('0', p + 9, 9)!;
}
else
{
if (z <= a) z = a + 1;
for (uint* d = a; d < z && p >= 0; d++)
{
char* s = fmt_u(*d, buf + 9);
if (s == buf + 9) (--s)[0] = '0';
if (d != a)
{
while (s > buf) (--s)[0] = '0';
}
else
{
this.out(s++[0])!;
if (p > 0 || this.flags.hash) this.out('.')!;
}
this.out_chars(s[:math::min(buf + 9 - s, p)])!;
p -= buf + 9 - s;
}
this.pad('0', p + 18, 18)!;
this.out_chars(estr[:ebuf - estr])!;
}
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
return;
}
fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base) @private
{
char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit;
usz len = 0;
// no hash for 0 values
if (!value) this.flags.hash = false;
// write if precision != 0 or value is != 0
if (!this.flags.precision || value)
{
char past_10 = (this.flags.uppercase ? 'A' : 'a') - 10;
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
char digit = (char)(value % base);
buf[len++] = digit + (digit < 10 ? '0' : past_10);
value /= base;
}
while (value);
}
return this.ntoa_format((String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
}
fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negative, uint base) @private
{
// pad leading zeros
if (!this.flags.left)
{
if (this.width && this.flags.zeropad && (negative || this.flags.plus || this.flags.space)) this.width--;
while (len < this.prec)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '0';
}
while (this.flags.zeropad && len < this.width)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '0';
}
}
// handle hash
if (this.flags.hash && base != 10)
{
if (!this.flags.precision && len && len == this.prec && len == this.width)
{
len--;
if (len) len--;
}
if (base != 10)
{
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
switch (base)
{
case 16:
buf[len++] = this.flags.uppercase ? 'X' : 'x';
case 8:
buf[len++] = this.flags.uppercase ? 'O' : 'o';
case 2:
buf[len++] = this.flags.uppercase ? 'B' : 'b';
default:
unreachable();
}
buf[len++] = '0';
}
}
switch (true)
{
case negative:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '-';
case this.flags.plus:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '+';
case this.flags.space:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = ' ';
}
if (!len) return;
return this.out_reverse(buf[:len]);
}
fn void! Formatter.ntoa_any(Formatter* this, any arg, uint base) @private
{
bool is_neg;
uint128 val = int_from_any(arg, &is_neg)!!;
return this.ntoa(val, is_neg, base) @inline;
}
fn void! Formatter.out_char(Formatter* this, any arg) @private
{
uint l = 1;
// pre padding
this.right_adjust(l)!;
// char output
Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD;
switch (true)
{
case c < 0x7f:
this.out((char)c)!;
case c < 0x7ff:
this.out((char)(0xC0 | c >> 6))!;
this.out((char)(0x80 | (c & 0x3F)))!;
case c < 0xffff:
this.out((char)(0xE0 | c >> 12))!;
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
this.out((char)(0x80 | (c & 0x3F)))!;
default:
this.out((char)(0xF0 | c >> 18))!;
this.out((char)(0x80 | (c >> 12 & 0x3F)))!;
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
this.out((char)(0x80 | (c & 0x3F)))!;
}
return this.left_adjust(l);
}
fn void! Formatter.out_reverse(Formatter* this, char[] buf) @private
{
usz buffer_start_idx = this.idx;
usz len = buf.len;
// pad spaces up to given width
if (!this.flags.left && !this.flags.zeropad)
{
for (usz i = len; i < this.width; i++)
{
this.out(' ')!;
}
}
// reverse string
while (len) this.out(buf[--len])!;
// append pad spaces up to given width
return this.left_adjust(this.idx - buffer_start_idx);
}
fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private
{
usz val = ++(*index_ptr);
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?;
}
fn any! next_any(any* args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
{
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG?;
return args_ptr[(*arg_index_ptr)++];
}
fn int! printf_parse_format_field(
any* args_ptr, usz args_len, usz* args_index_ptr,
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
{
char c = format_ptr[*index_ptr];
if (c >= '0' && c <= '9') return simple_atoi(format_ptr, format_len, index_ptr);
if (c != '*') return 0;
printf_advance_format(format_len, index_ptr)!;
any val = next_any(args_ptr, args_len, args_index_ptr)!;
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?;
uint! intval = types::any_to_int(val, int);
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;
}

427
lib/std/io/io_printf.c3 Normal file
View File

@@ -0,0 +1,427 @@
module std::io;
import std::collections::map;
import libc;
const int PRINTF_NTOA_BUFFER_SIZE = 256;
fault PrintFault
{
BUFFER_EXCEEDED,
INTERNAL_BUFFER_EXCEEDED,
INVALID_FORMAT_STRING,
MISSING_ARG,
INVALID_ARGUMENT_TYPE,
}
fault FormattingFault
{
UNTERMINATED_FORMAT,
MISSING_ARG,
INVALID_WIDTH_ARG,
INVALID_FORMAT_TYPE,
}
def OutputFn = fn void!(char c, void* buffer);
def FloatType = double;
fn String any.to_string(void* value, Allocator *using) @interface;
fn void! any.to_format(void* value, Formatter* formatter) @interface;
fn usz! printf(String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
return formatter.vprintf(format, args);
}
fn usz! printfn(String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
usz len = formatter.vprintf(format, args)!;
putchar('\n');
return len + 1;
}
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
{
Formatter formatter;
BufferData data = { .buffer = buffer };
formatter.init(&out_buffer_fn, &data);
usz size = formatter.vprintf(format, args)!;
return buffer[:data.written];
}
fn usz! File.printf(File file, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_fputchar_fn, &file);
return formatter.vprintf(format, args)!;
}
fn usz! File.printfn(File file, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_fputchar_fn, &file);
usz len = formatter.vprintf(format, args)!;
file.putc('\n')!;
file.flush();
return len + 1;
}
fn usz! Formatter.printf(Formatter* this, String format, args...)
{
return this.vprintf(format, args) @inline;
}
struct Formatter
{
void *data;
OutputFn out_fn;
struct
{
PrintFlags flags;
uint width;
uint prec;
usz idx;
}
}
bitstruct PrintFlags : uint
{
bool zeropad : 0;
bool left : 1;
bool plus : 2;
bool space : 3;
bool hash : 4;
bool uppercase : 5;
bool precision : 6;
}
fn void Formatter.init(Formatter* this, OutputFn out_fn, void* data = null)
{
*this = { .data = data, .out_fn = out_fn};
}
fn void! Formatter.out(Formatter* this, char c) @private
{
this.out_fn(c, this.data)!;
}
macro bool! Formatter.print_with_function(Formatter* this, any arg)
{
if (&arg.to_format)
{
PrintFlags old = this.flags;
uint old_width = this.width;
uint old_prec = this.prec;
defer
{
this.flags = old;
this.width = old_width;
this.prec = old_prec;
}
arg.to_format(this)!;
return true;
}
if (&arg.to_string)
{
PrintFlags old = this.flags;
uint old_width = this.width;
uint old_prec = this.prec;
defer
{
this.flags = old;
this.width = old_width;
this.prec = old_prec;
}
@pool()
{
this.out_substr(arg.to_string(mem::temp()))!;
return true;
};
}
return false;
}
fn void! Formatter.out_str(Formatter* this, any arg) @private
{
switch (arg.type.kindof)
{
case TYPEID:
return this.out_substr("typeid");
case VOID:
return this.out_substr("void");
case ANYFAULT:
case FAULT:
return this.out_substr((*(anyfault*)arg.ptr).nameof);
case ANY:
return this.out_str(*(any*)arg);
case ENUM:
if (this.print_with_function(arg)!) return;
return this.out_substr(arg.type.names[types::any_to_int(arg, usz)!!]);
case STRUCT:
if (this.print_with_function(arg)!) return;
return this.out_substr("<struct>");
case UNION:
if (this.print_with_function(arg)!) return;
return this.out_substr("<union>");
case BITSTRUCT:
if (this.print_with_function(arg)!) return;
return this.out_substr("<bitstruct>");
case FUNC:
if (this.print_with_function(arg)!) return;
return this.out_substr("<function>");
case OPTIONAL:
unreachable();
case DISTINCT:
if (this.print_with_function(arg)!) return;
if (arg.type == DString.typeid)
{
return this.out_substr(((DString*)arg).str());
}
return this.out_str(any { arg.ptr, arg.type.inner });
case POINTER:
if (this.print_with_function(arg)!) return;
return this.ntoa_any(arg, 16);
case SIGNED_INT:
case UNSIGNED_INT:
return this.ntoa_any(arg, 10);
case FLOAT:
return this.ftoa(float_from_any(arg)!!);
case ARRAY:
if (this.print_with_function(arg)!) return;
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz len = arg.type.len;
// Pretend this is a String
void* ptr = (void*)arg.ptr;
this.out('[')!;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")!;
this.out_str(any { ptr, inner })!;
ptr += size;
}
return this.out(']');
case VECTOR:
if (this.print_with_function(arg)!) return;
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz len = arg.type.len;
// Pretend this is a String
void* ptr = (void*)arg.ptr;
this.out_substr("[<")!;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")!;
this.out_str(any { ptr, inner })!;
ptr += size;
}
return this.out_substr(">]");
case SUBARRAY:
if (this.print_with_function(arg)!) return;
// this is SomeType[] so grab the "SomeType"
typeid inner = arg.type.inner;
if (inner == char.typeid)
{
return this.out_substr(*(String*)arg);
}
usz size = inner.sizeof;
// Pretend this is a String
String* temp = (void*)arg.ptr;
void* ptr = (void*)temp.ptr;
usz len = temp.len;
this.out('[')!;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")!;
this.out_str(any { ptr, inner })!;
ptr += size;
}
this.out(']')!;
case BOOL:
return this.out_substr(*(bool*)arg.ptr ? "true" : "false");
default:
if (this.print_with_function(arg)!) return;
return this.out_substr("Invalid type");
}
}
fn void! out_buffer_fn(char c, void *data) @private
{
BufferData *buffer_data = data;
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?;
buffer_data.buffer[buffer_data.written++] = c;
}
fn void! out_null_fn(char c @unused, void* data @unused) @private
{
}
fn void! out_putchar_fn(char c, void* data @unused) @private
{
libc::putchar(c);
}
fn void! out_fputchar_fn(char c, void* data) @private
{
File* f = data;
f.putc(c)!;
}
struct BufferData @private
{
char[] buffer;
usz written;
}
fn usz! Formatter.vprintf(Formatter* this, String format, any[] anys)
{
if (!this.out_fn)
{
// use null output function
this.out_fn = &out_null_fn;
}
usz format_len = format.len;
usz variant_index = 0;
for (usz i = 0; i < format_len; i++)
{
// format specifier? %[flags][width][.precision][length]
char c = format[i];
if (c != '%')
{
// no
this.out(c)!;
continue;
}
i++;
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
c = format[i];
if (c == '%')
{
this.out(c)!;
continue;
}
// evaluate flags
this.flags = {};
while FLAG_EVAL: (true)
{
switch (c)
{
case '0': this.flags.zeropad = true;
case '-': this.flags.left = true;
case '+': this.flags.plus = true;
case ' ': this.flags.space = true;
case '#': this.flags.hash = true;
default: break FLAG_EVAL;
}
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
c = format[i];
}
// evaluate width field
int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
c = format[i];
if (w < 0)
{
this.flags.left = true;
w = -w;
}
this.width = w;
// evaluate precision field
this.prec = 0;
if (c == '.')
{
this.flags.precision = true;
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
int prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
this.prec = prec < 0 ? 0 : prec;
c = format[i];
}
// evaluate specifier
uint base = 0;
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
any current = anys[variant_index++];
switch (c)
{
case 'd':
base = 10;
this.flags.hash = false;
case 'X' :
this.flags.uppercase = true;
nextcase;
case 'x' :
base = 16;
case 'O':
this.flags.uppercase = true;
nextcase;
case 'o' :
base = 8;
case 'B':
this.flags.uppercase = true;
nextcase;
case 'b' :
base = 2;
case 'A':
this.flags.uppercase = true;
nextcase;
case 'a':
this.atoa(float_from_any(current)!!)!;
continue;
case 'F' :
this.flags.uppercase = true;
nextcase;
case 'f':
this.ftoa(float_from_any(current)!!)!;
continue;
case 'E':
this.flags.uppercase = true;
nextcase;
case 'e':
this.etoa(float_from_any(current)!!)!;
continue;
case 'G':
this.flags.uppercase = true;
nextcase;
case 'g':
this.gtoa(float_from_any(current)!!)!;
continue;
case 'c':
this.out_char(current)!;
continue;
case 's':
this.out_str(current)!;
continue;
case 'p':
this.flags.zeropad = true;
this.flags.hash = true;
base = 16;
default:
return PrintFault.INVALID_FORMAT_STRING?;
}
if (base != 10)
{
this.flags.plus = false;
this.flags.space = false;
}
// ignore '0' flag when precision is given
if (this.flags.precision) this.flags.zeropad = false;
bool is_neg;
uint128 v = int_from_any(current, &is_neg)!!;
this.ntoa(v, is_neg, base)!;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return this.idx;
}

205
lib/std/io/io_stream.c3 Normal file
View File

@@ -0,0 +1,205 @@
module std::io;
def CloseStreamFn = fn void!(Stream*);
def FlushStreamFn = fn void!(Stream*);
def SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek);
def LenStreamFn = fn usz(Stream*);
def AvailableStreamFn = fn usz(Stream*);
def ReadStreamFn = fn usz!(Stream*, char[] bytes);
def ReadFromStreamFn = fn usz!(Stream*, Stream*);
def ReadByteStreamFn = fn char!(Stream*);
def PushbackByteStreamFn = fn void!(Stream*);
def WriteStreamFn = fn usz!(Stream*, char[] bytes);
def WriteToStreamFn = fn usz!(Stream*, Stream* out);
def WriteByteStreamFn = fn void!(Stream*, char c);
def DestroyStreamFn = fn void!(Stream*);
struct StreamInterface
{
CloseStreamFn close_fn;
FlushStreamFn flush_fn;
SeekStreamFn seek_fn;
LenStreamFn len_fn;
AvailableStreamFn available_fn;
ReadStreamFn read_fn;
ReadFromStreamFn read_stream_fn;
ReadByteStreamFn read_byte_fn;
PushbackByteStreamFn pushback_byte_fn;
WriteStreamFn write_fn;
WriteToStreamFn write_stream_fn;
WriteByteStreamFn write_byte_fn;
DestroyStreamFn destroy_fn;
}
struct Stream
{
StreamInterface *fns;
void* data;
}
fn bool Stream.supports_seek(Stream* s) @inline => (bool)s.fns.seek_fn;
fn bool Stream.supports_available(Stream* s) @inline => s.fns.available_fn || s.fns.seek_fn;
fn bool Stream.supports_len(Stream* s) @inline => s.fns.len_fn || s.fns.seek_fn;
fn bool Stream.supports_read(Stream* s) @inline => s.fns.read_fn || s.fns.read_byte_fn;
fn bool Stream.supports_read_from(Stream* s) @inline => (bool)s.fns.read_stream_fn;
fn bool Stream.supports_write_to(Stream* s) @inline => (bool)s.fns.write_stream_fn;
fn bool Stream.supports_pushback_byte(Stream* s) @inline => s.fns.pushback_byte_fn || s.fns.seek_fn;
fn bool Stream.supports_write(Stream* s) @inline => s.fns.write_fn || s.fns.write_byte_fn;
fn void! Stream.destroy(Stream* s) @inline @maydiscard
{
if (s.fns.destroy_fn) return s.fns.destroy_fn(s);
return s.close();
}
fn void! Stream.close(Stream* s) @inline @maydiscard
{
if (CloseStreamFn func = s.fns.close_fn) return func(s);
}
fn usz! Stream.seek(Stream* s, isz offset, Seek seek) @inline
{
if (SeekStreamFn func = s.fns.seek_fn) return func(s, offset, seek);
return IoError.NOT_SEEKABLE?;
}
fn usz! Stream.available(Stream* s) @inline
{
if (AvailableStreamFn func = s.fns.available_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
usz curr = func(s, 0, Seek.CURSOR)!;
usz len = func(s, 0, Seek.END)!;
func(s, curr, Seek.SET)!;
return len - curr;
}
return IoError.NOT_SEEKABLE?;
}
fn usz! Stream.read(Stream* s, char[] buffer)
{
if (ReadStreamFn func = s.fns.read_fn) return func(s, buffer);
if (ReadByteStreamFn func = s.fns.read_byte_fn)
{
usz len = 0;
foreach (&cptr : buffer)
{
char! c = func(s);
if (catch err = c)
{
case IoError.EOF: return len;
default: return err?;
}
*cptr = c;
len++;
}
}
return IoError.UNSUPPORTED_OPERATION?;
}
fn char! Stream.read_byte(Stream* s) @inline
{
if (ReadByteStreamFn func = s.fns.read_byte_fn) return func(s);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.write(Stream* s, char[] bytes) @inline
{
if (WriteStreamFn func = s.fns.write_fn) return func(s, bytes);
if (WriteByteStreamFn func = s.fns.write_byte_fn)
{
foreach (c : bytes) func(s, c)!;
return bytes.len;
}
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.write_byte(Stream* s, char b) @inline
{
if (WriteByteStreamFn func = s.fns.write_byte_fn) return func(s, b);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.write_to(Stream* s, Stream* to) @inline
{
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, to);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.read_from(Stream* s, Stream* from) @inline
{
if (ReadFromStreamFn func = s.fns.read_stream_fn) return func(s, from);
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.flush(Stream* s) @inline @maydiscard
{
if (FlushStreamFn func = s.fns.flush_fn) return func(s);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.len(Stream* s) @inline
{
if (LenStreamFn func = s.fns.len_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
usz curr = func(s, 0, Seek.CURSOR)!;
usz len = func(s, 0, Seek.END)!;
func(s, curr, Seek.SET)!;
return len;
}
return IoError.NOT_SEEKABLE?;
}
fn void! Stream.pushback_byte(Stream* s) @inline
{
if (PushbackByteStreamFn func = s.fns.pushback_byte_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
func(s, -1, CURSOR)!;
return;
}
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.write_string(Stream* s, String str) @inline => (void)(s.write((char[])str)!);
fn usz! Stream.copy_to(Stream* s, Stream* dst, char[] buffer = {})
{
if (buffer.len) return copy_through_buffer(s, dst, buffer);
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, dst);
if (ReadFromStreamFn func = dst.fns.read_stream_fn) return func(dst, s);
$switch (env::MEMORY_ENV)
$case NORMAL:
@pool()
{
return copy_through_buffer(s, dst, tmalloc(char, 4096));
};
$case SMALL:
@pool()
{
return copy_through_buffer(s, dst, tmalloc(char, 1024));
};
$case TINY:
$case NONE:
return copy_through_buffer(s, dst, &&(char[256]{}));
$endswitch
}
macro usz! copy_through_buffer(Stream* s, Stream* dst, char[] buffer) @local
{
usz total_copied;
while (true)
{
usz! len = s.read(buffer);
if (catch err = len)
{
case IoError.EOF: return total_copied;
default: return err?;
}
if (!len) return total_copied;
usz written = dst.write(buffer[:len])!;
total_copied += len;
if (written != len) return IoError.INCOMPLETE_WRITE?;
}
}

44
lib/std/io/os/chdir.c3 Normal file
View File

@@ -0,0 +1,44 @@
module std::io::os;
import libc;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
macro void! native_chdir(Path p)
{
if (posix::chdir(p.as_zstr()))
{
switch (libc::errno())
{
case errno::EACCES: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::ENOTDIR: return IoError.FILE_NOT_DIR?;
case errno::ENOENT: return IoError.FILE_NOT_FOUND?;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
default: return IoError.GENERAL_ERROR?;
}
}
}
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
macro void! native_chdir(Path path)
{
@pool()
{
// TODO improve with better error handling.
if (win32::win32_SetCurrentDirectoryW(path.as_str().to_temp_utf16()!!)) return;
};
return IoError.GENERAL_ERROR?;
}
$default:
fn void! native_chdir(Path path)
{
unreachable("'getcwd' not available");
}
$endswitch

187
lib/std/io/os/file.c3 Normal file
View File

@@ -0,0 +1,187 @@
module std::io::os;
import libc;
def FopenFn = fn void*!(String, String);
def FreopenFn = fn void*!(void*, String, String);
def FcloseFn = fn void!(void*);
def FseekFn = fn void!(void*, isz, Seek);
def FtellFn = fn usz!(void*);
def FwriteFn = fn usz!(void*, char[] buffer);
def FreadFn = fn usz!(void*, char[] buffer);
$if !$defined(native_fopen_fn):
FopenFn native_fopen_fn @weak;
$endif
$if !$defined(native_fclose_fn):
FcloseFn native_fclose_fn @weak;
$endif
$if !$defined(native_freopen_fn):
FreopenFn native_freopen_fn @weak;
$endif
$if !$defined(native_fseek_fn):
FseekFn native_fseek_fn @weak;
$endif
$if !$defined(native_ftell_fn):
FtellFn native_ftell_fn @weak;
$endif
$if !$defined(native_fwrite_fn):
FwriteFn native_fwrite_fn @weak;
$endif
$if !$defined(native_fread_fn):
FreadFn native_fread_fn @weak;
$endif
/**
* @require mode.len > 0
* @require filename.len > 0
**/
fn void*! native_fopen(String filename, String mode) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fopen_fn) return native_fopen_fn(filename, mode);
unreachable("Tried to call fopen without support.");
$else
@pool()
{
$if env::os_is_win32():
void* file = (CFile)_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())!;
$else
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
$endif
return file ?: file_open_errno()?;
};
$endif
}
/**
* @require mode.len > 0
* @require filename.len > 0
**/
fn void*! native_freopen(void* file, String filename, String mode) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_freopen_fn) return native_freopen_fn(file, filename, mode);
unreachable("Tried to call freopen without support.");
$else
@pool()
{
$if env::os_is_win32():
file = _wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)!;
$else
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
$endif
return file ?: file_open_errno()?;
};
$endif
}
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
unreachable("Tried to call fseek without support.");
$else
$if env::os_is_win32():
bool success = _fseeki64(file, (long)offset, (int)seek_mode) == 0;
$else
bool success = libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode) == 0;
$endif
if (!success) return file_seek_errno()?;
$endif
}
fn usz! native_ftell(CFile file) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_ftell_fn) return native_ftell_fn(file);
unreachable("Tried to call ftell without support.");
$else
$if env::os_is_win32():
long index = _ftelli64(file);
return index >= 0 ? index : file_seek_errno()?;
$else
SeekIndex index = libc::ftell(file);
return index >= 0 ? index : file_seek_errno()?;
$endif
$endif
}
fn usz! native_fwrite(CFile file, char[] buffer) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fwrite_fn) return native_fwrite_fn(file, buffer);
unreachable("Tried to call fwrite without support.");
$else
return libc::fwrite(buffer.ptr, 1, buffer.len, file);
$endif
}
fn usz! native_fread(CFile file, char[] buffer) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fread_fn) return native_fread_fn(file, buffer);
unreachable("Tried to call fread without support.");
$else
return libc::fread(buffer.ptr, 1, buffer.len, file);
$endif
}
macro anyfault file_open_errno() @local
{
switch (libc::errno())
{
case errno::EACCES: return IoError.NO_PERMISSION;
case errno::EDQUOT: return IoError.OUT_OF_SPACE;
case errno::EBADF: return IoError.FILE_NOT_VALID;
case errno::EEXIST: return IoError.ALREADY_EXISTS;
case errno::EINTR: return IoError.INTERRUPTED;
case errno::EFAULT: return IoError.GENERAL_ERROR;
case errno::EISDIR: return IoError.FILE_IS_DIR;
case errno::ELOOP: return IoError.SYMLINK_FAILED;
case errno::EMFILE: return IoError.TOO_MANY_DESCRIPTORS;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG;
case errno::ENFILE: return IoError.OUT_OF_SPACE;
case errno::ENOTDIR: return IoError.FILE_NOT_DIR;
case errno::ENOENT: return IoError.FILE_NOT_FOUND;
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
case errno::EOVERFLOW: return IoError.OVERFLOW;
case errno::EROFS: return IoError.READ_ONLY;
case errno::EOPNOTSUPP: return IoError.UNSUPPORTED_OPERATION;
case errno::EIO: return IoError.INCOMPLETE_WRITE;
case errno::EWOULDBLOCK: return IoError.WOULD_BLOCK;
default: return IoError.UNKNOWN_ERROR;
}
}
macro anyfault file_seek_errno() @local
{
switch (libc::errno())
{
case errno::ESPIPE: return IoError.FILE_IS_PIPE;
case errno::EPIPE: return IoError.FILE_IS_PIPE;
case errno::EOVERFLOW: return IoError.OVERFLOW;
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
case errno::EIO: return IoError.INCOMPLETE_WRITE;
case errno::EINVAL: return IoError.INVALID_POSITION;
case errno::EINTR: return IoError.INTERRUPTED;
case errno::EFBIG: return IoError.OUT_OF_SPACE;
case errno::EBADF: return IoError.FILE_NOT_VALID;
case errno::EAGAIN: return IoError.WOULD_BLOCK;
default: return IoError.UNKNOWN_ERROR;
}
}
// Win functions
$if env::os_is_win32():
extern fn void* _wfopen(Char16*, Char16*) @local;
extern fn void* _wfreopen(Char16*, Char16*, CFile) @local;
extern fn int _fseeki64(CFile, long, int) @local;
extern fn long _ftelli64(CFile) @local;
$endif
$if env::os_is_posix():
extern fn CInt access(ZString path, CInt mode);
$endif

View File

@@ -0,0 +1,101 @@
module std::io::file::os;
import libc;
$if env::os_is_darwin():
struct DarwinTimespec @private
{
long tv_sec;
long tv_nsec;
}
struct Darwin64Stat @private
{
int st_dev;
ushort st_mode;
ushort st_nlink;
ulong st_ino;
uint st_uid;
uint st_gid;
int st_rdev;
DarwinTimespec st_atimespec; // time of last access
DarwinTimespec st_mtimespec; // time of last data modification
DarwinTimespec st_ctimespec; // time of last status change
DarwinTimespec st_birthtimespec; // time of file creation(birth)
long st_size;
long st_blocks;
int st_blocksize;
uint st_flags;
uint st_gen;
int st_lspare;
long[2] st_qspare;
}
extern fn int _stat(ZString str, Darwin64Stat* stat) @extern("stat64");
const S_IFMT = 0o170000; // type of file mask
const S_IFIFO = 0o010000; // named pipe (fifo)
const S_IFCHR = 0o020000; // character special
const S_IFDIR = 0o040000; // directory
const S_IFBLK = 0o060000; // block special
const S_IFREG = 0o100000; // regular
const S_IFLNK = 0o120000; // symbolic link
const S_IFSOCK = 0o140000; // socket
fn usz! native_file_size(String path)
{
Darwin64Stat stat;
read_stat(&stat, path)!;
return stat.st_size;
}
fn bool native_file_or_dir_exists(String path)
{
Darwin64Stat stat;
return @ok(read_stat(&stat, path));
}
fn bool native_is_file(String path)
{
Darwin64Stat stat;
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFREG;
}
fn bool native_is_dir(String path)
{
Darwin64Stat stat;
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFDIR;
}
fn void! read_stat(Darwin64Stat* stat, String path) @local
{
@pool()
{
int res = _stat(path.zstr_tcopy(), stat);
if (res != 0)
{
switch (libc::errno())
{
case errno::EBADF:
return IoError.FILE_NOT_VALID?;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return IoError.GENERAL_ERROR?;
case errno::EACCES:
return IoError.NO_PERMISSION?;
case errno::ELOOP:
return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG:
return IoError.NAME_TOO_LONG?;
case errno::ENOENT:
return IoError.FILE_NOT_FOUND?;
case errno::ENOTDIR:
return IoError.FILE_NOT_DIR?;
case errno::EOVERFLOW:
return IoError.GENERAL_ERROR?;
default:
return IoError.UNKNOWN_ERROR?;
}
}
};
}
$endif

View File

@@ -0,0 +1,181 @@
module std::io::file::os;
// native_temp_directory, for non Win32
$if !env::os_is_win32():
fn Path! native_temp_directory(Allocator* using = mem::heap())
{
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
{
String tmpdir = env::get_var(env) ?? "";
if (tmpdir) return path::new(tmpdir, using);
}
return path::new("/tmp", using);
}
$if env::COMPILER_LIBC_AVAILABLE:
extern fn void* opendir(ZString);
extern fn void closedir(void*);
extern fn int remove(ZString);
const DT_UNKNOWN = 0;
const DT_FIFO = 1;
const DT_CHR = 2;
const DT_DIR = 4;
const DT_BLK = 6;
const DT_REG = 8;
const DT_LNK = 10;
const DT_SOCK = 12;
const DT_WHT = 14;
fn PathList! native_readdir(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* using)
{
PathList list;
list.init(.using = using);
void* directory = opendir(dir.as_str() ? dir.as_zstr() : (ZString)".");
defer if (directory) closedir(directory);
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
NativeDirentry* entry;
while ((entry = readdir(directory)))
{
String name = ((ZString)&entry.name).as_str();
if (!name || name == "." || name == "..") continue;
if (entry.type == DT_LNK && no_symlinks) continue;
if (entry.type == DT_DIR && no_dirs) continue;
Path path = path::new(name.copy(using), using)!!;
list.append(path);
}
return list;
}
/**
* @require dir.as_str()
**/
fn void! native_rmtree(Path dir)
{
void* directory = opendir(dir.as_zstr());
defer if (directory) closedir(directory);
if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?;
NativeDirentry* entry;
while ((entry = readdir(directory)))
{
@pool()
{
String name = ((ZString)&entry.name).as_str();
if (!name || name == "." || name == "..") continue;
Path new_path = dir.tappend(name)!;
if (entry.type == DT_DIR)
{
native_rmtree(new_path)!;
continue;
}
if (remove(new_path.as_zstr()))
{
// TODO improve
return IoError.GENERAL_ERROR?;
}
};
}
os::native_rmdir(dir)!;
}
$endif
$endif
$if !env::os_is_darwin() && !env::os_is_win32():
fn usz! native_file_size(String path)
{
File f = file::open(path, "r")!;
defer (void)f.close();
return f.seek(0, Seek.END)!;
}
$if env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE:
fn bool native_file_or_dir_exists(String path)
{
@pool()
{
return os::access(path.zstr_tcopy(), 0 /* F_OK */) != -1;
};
}
fn bool native_is_file(String path)
{
File! f = file::open(path, "r");
defer (void)f.close();
return @ok(f);
}
fn bool native_is_dir(String path)
{
return native_file_or_dir_exists(path) && !native_is_file(path);
}
$else
fn bool native_file_or_dir_exists(String path)
{
unreachable("Tried to call file_or_dir_exists without support.");
}
fn bool native_is_dir(String path)
{
unreachable("Tried to call is_dir without support.");
}
fn bool native_is_file(String path)
{
unreachable("Tried to call is_file without support.");
}
$endif
$endif
$switch (env::OS_TYPE)
$case IOS:
$case MACOS:
$case TVOS:
$case WATCHOS:
$if env::ARCH_TYPE == X86_64:
extern fn NativeDirentry* readdir(void*) @extern("readdir$INODE64");
$else
extern fn NativeDirentry* readdir(void*) @extern("readdir");
$endif
struct NativeDirentry
{
usz ino;
usz seekoff;
ushort reclen;
ushort namelen;
char type;
char[1024] name;
}
$case LINUX:
extern fn NativeDirentry* readdir(void*);
struct NativeDirentry
{
usz ino;
isz seekoff;
ushort reclen;
char type;
char[*] name;
}
$default:
// Fix this as we go along.
extern fn NativeDirentry* readdir(void*);
struct NativeDirentry
{
usz ino;
isz seekoff;
ushort reclen;
char type;
char[*] name;
}
$endswitch

View File

@@ -0,0 +1,113 @@
module std::io::file::os;
import std::os::win32;
$if env::os_is_win32():
const Win32_DWORD FILE_ATTRIBUTE_READONLY = 0x01;
const Win32_DWORD FILE_ATTRIBUTE_HIDDEN = 0x02;
const Win32_DWORD FILE_ATTRIBUTE_SYSTEM = 0x04;
const Win32_DWORD FILE_ATTRIBUTE_DIRECTORY = 0x10;
const Win32_DWORD FILE_ATTRIBUTE_ARCHIVE = 0x20;
const Win32_DWORD FILE_ATTRIBUTE_DEVICE = 0x40;
const Win32_DWORD FILE_ATTRIBUTE_NORMAL = 0x80;
const Win32_DWORD FILE_ATTRIBUTE_TEMPORARY = 0x100;
const Win32_DWORD FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
const Win32_DWORD FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
const Win32_DWORD FILE_ATTRIBUTE_COMPRESSED = 0x800;
const Win32_DWORD FILE_ATTRIBUTE_OFFLINE = 0x1000;
const Win32_DWORD FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
const Win32_DWORD FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
const Win32_DWORD FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
const Win32_DWORD FILE_ATTRIBUTE_VIRTUAL = 0x10000;
const Win32_DWORD FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
const Win32_DWORD FILE_ATTRIBUTE_EA = 0x40000;
const Win32_DWORD FILE_ATTRIBUTE_PINNED = 0x80000;
const Win32_DWORD FILE_ATTRIBUTE_UNPINNED = 0x100000;
const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
fn usz! native_file_size(String path)
{
@pool()
{
Char16[] path16 = path.to_temp_utf16()!;
Win32_FILE_ATTRIBUTE_DATA data;
win32::win32_GetFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
Win32_LARGE_INTEGER size;
size.lowPart = data.nFileSizeLow;
size.highPart = data.nFileSizeHigh;
return (usz)size.quadPart;
};
}
fn bool native_file_or_dir_exists(String path)
{
@pool()
{
return (bool)win32::win32_PathFileExistsW(path.to_temp_utf16()) ?? false;
};
}
fn bool native_is_file(String path)
{
File! f = file::open(path, "r");
defer (void)f.close();
return @ok(f);
}
fn bool native_is_dir(String path)
{
return native_file_or_dir_exists(path) && !native_is_file(path);
}
fn void! native_rmtree(Path path)
{
Win32_WIN32_FIND_DATAW find_data;
String s = path.as_str().tconcat("\\*");
Win32_HANDLE find = win32::win32_FindFirstFileW(s.to_utf16(mem::temp()), &find_data)!;
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
defer win32::win32_FindClose(find);
do
{
String filename = string::from_zutf16(&find_data.cFileName, mem::temp())!;
if (filename == "." || filename == "..") continue;
Path file_path = path.tappend(filename)!;
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
native_rmtree(file_path)!;
}
else
{
win32::win32_DeleteFileW(file_path.as_str().to_utf16(mem::temp()));
}
} while (win32::win32_FindNextFileW(find, &find_data) != 0);
os::native_rmdir(path)!;
}
fn Path! native_temp_directory(Allocator* using = mem::heap())
{
@stack_mem(256; Allocator* mem)
{
Win32_DWORD len = win32::win32_GetTempPathW(0, null);
if (!len) return IoError.GENERAL_ERROR?;
Char16[] buff = malloc(Char16, len + 1, .using = mem);
if (!win32::win32_GetTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
return path::new(string::from_utf16(buff[:len], .using = mem), using);
};
}
/*
}else if(method == file_size_methods::get_attributes){
WIN32_FILE_ATTRIBUTE_DATA file_attr_data;
if(GetFileAttributesEx(path, GetFileExInfoStandard, &file_attr_data)){
file_size.LowPart = file_attr_data.nFileSizeLow;
file_size.HighPart = file_attr_data.nFileSizeHigh;
}
}
*/
$endif

51
lib/std/io/os/getcwd.c3 Normal file
View File

@@ -0,0 +1,51 @@
module std::io::os;
import libc;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
macro String! getcwd(Allocator* using = mem::heap())
{
const DEFAULT_BUFFER = 256;
Char16[DEFAULT_BUFFER] buffer;
Char16 *res = win32::_wgetcwd(&buffer, DEFAULT_BUFFER);
bool free = false;
defer if (free) libc::free(res);
if (!res)
{
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
res = win32::_wgetcwd(null, 0);
free = true;
}
Char16[] str16 = res[:win32::wcslen(res)];
return string::from_utf16(str16, using);
}
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
macro String! getcwd(Allocator* using = mem::heap())
{
const usz DEFAULT_BUFFER = 256;
char[DEFAULT_BUFFER] buffer;
ZString res = posix::getcwd(&buffer, DEFAULT_BUFFER);
bool free = false;
if (!res)
{
// Improve error
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
res = posix::getcwd(null, 0);
free = true;
}
defer if (free) libc::free((void*)res);
return res.copy(using);
}
$default:
fn String! getcwd(Allocator* using = mem::heap())
{
unreachable("'getcwd' not available");
}
$endswitch

63
lib/std/io/os/mkdir.c3 Normal file
View File

@@ -0,0 +1,63 @@
module std::io::os;
import libc;
import std::io::path;
import std::os::win32;
import std::os::posix;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
{
if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true;
switch (libc::errno())
{
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::EDQUOT:
case errno::ENOSPC: return IoError.OUT_OF_SPACE?;
case errno::EISDIR:
case errno::EEXIST: return false;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
case errno::ENOTDIR: return IoError.FILE_NOT_FOUND?;
default: return IoError.GENERAL_ERROR?;
}
}
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
{
@pool()
{
// TODO security attributes
if (win32::win32_CreateDirectoryW(path.as_str().to_temp_utf16()!!, null)) return true;
switch (win32::win32_GetLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;
case win32::ERROR_DISK_FULL:
return IoError.OUT_OF_SPACE?;
case win32::ERROR_ALREADY_EXISTS:
return false;
case win32::ERROR_PATH_NOT_FOUND:
return IoError.FILE_NOT_FOUND?;
default:
return IoError.GENERAL_ERROR?;
}
};
}
$default:
fn bool! native_mkdir(Path path, MkdirPermissions permissions)
{
unreachable("'mkdir' not available");
return false;
}
$endswitch

61
lib/std/io/os/rmdir.c3 Normal file
View File

@@ -0,0 +1,61 @@
module std::io::os;
import libc;
import std::io::path;
import std::os::win32;
import std::os::posix;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
macro bool! native_rmdir(Path path)
{
if (!posix::rmdir(path.as_zstr())) return true;
switch (libc::errno())
{
case errno::EBUSY: return IoError.BUSY?;
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::ENOTDIR:
case errno::ENOENT: return false;
case errno::ENOTEMPTY: return IoError.DIR_NOT_EMPTY?;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
default: return IoError.GENERAL_ERROR?;
}
}
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
macro bool! native_rmdir(Path path)
{
@pool()
{
if (win32::win32_RemoveDirectoryW(path.as_str().to_temp_utf16()!!)) return true;
switch (win32::win32_GetLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;
case win32::ERROR_CURRENT_DIRECTORY:
return IoError.BUSY?;
case win32::ERROR_DIR_NOT_EMPTY:
return IoError.DIR_NOT_EMPTY?;
case win32::ERROR_DIRECTORY:
case win32::ERROR_PATH_NOT_FOUND:
return false;
default:
return IoError.GENERAL_ERROR?;
}
};
}
$default:
fn bool! native_rmdir(Path path)
{
unreachable("'rmdir' not available");
}
$endswitch

413
lib/std/io/path.c3 Normal file
View File

@@ -0,0 +1,413 @@
module std::io::path;
import std::collections::list;
const PathEnv DEFAULT_PATH_ENV = env::os_is_win32() ? PathEnv.WIN32 : PathEnv.POSIX;
const char PREFERRED_SEPARATOR_WIN32 = '\\';
const char PREFERRED_SEPARATOR_POSIX = '/';
const char PREFERRED_SEPARATOR = env::os_is_win32() ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
def PathList = List<Path>;
fault PathResult
{
INVALID_PATH,
NO_PARENT,
}
struct Path
{
String path_string;
PathEnv env;
}
enum PathEnv
{
WIN32,
POSIX
}
fn Path! getcwd(Allocator* using = mem::heap())
{
@stack_mem(256; Allocator* mem)
{
return new(os::getcwd(mem), using);
};
}
fn bool is_dir(Path path) => os::native_is_dir(path.as_str());
fn bool is_file(Path path) => os::native_is_file(path.as_str());
fn usz! file_size(Path path) => os::native_file_size(path.as_str());
fn bool exists(Path path) => os::native_file_or_dir_exists(path.as_str());
fn Path! tgetcwd() => getcwd(mem::temp()) @inline;
fn void! chdir(Path path) => os::native_chdir(path) @inline;
fn Path! temp_directory(Allocator* using = mem::heap()) => os::native_temp_directory(using);
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
{
return c == '/' || (c == '\\' && path_env == PathEnv.WIN32);
}
macro bool is_posix_separator(char c)
{
return c == '/' || c == '\\';
}
macro bool is_win32_separator(char c)
{
return c == '/' || c == '\\';
}
fn Path[]! ls(Path path)
{
unreachable();
}
enum MkdirPermissions
{
NORMAL,
USER_ONLY,
USER_AND_ADMIN
}
fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
if (is_dir(path)) return false;
if (exists(path)) return IoError.FILE_NOT_DIR?;
if (recursive)
{
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
}
if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?;
return os::native_mkdir(path, permissions);
}
fn bool! rmdir(Path path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
return os::native_rmdir(path);
}
fn void! rmtree(Path path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
$if $defined(os::native_rmtree):
return os::native_rmtree(path);
$else
assert(false, "rmtree is not available");
$endif
}
fn Path! new(String path, Allocator* using = mem::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
{
return { normalize(path.copy(using), path_env), path_env };
}
fn Path! new_windows(String path, Allocator* using = mem::heap())
{
return new(path, using, WIN32);
}
fn Path! new_posix(String path, Allocator* using = mem::heap())
{
return new(path, using, POSIX);
}
fn bool Path.equals(Path p1, Path p2)
{
return p1.env == p2.env && p1.path_string == p2.path_string;
}
/**
* Append the string to the current path.
*
* @param [in] path
* @param [in] filename
* @ensure return.path_string.len >= path.path_string.len
**/
fn Path! Path.append(Path path, String filename, Allocator* using = mem::heap())
{
if (!path.path_string.len) return new(filename, using, path.env)!;
assert(!is_separator(path.path_string[^1], path.env));
@stack_mem(256; Allocator* mem)
{
DString dstr = dstring::new_with_capacity(path.path_string.len + 1 + filename.len, .using = mem);
dstr.append(path.path_string);
dstr.append(PREFERRED_SEPARATOR);
dstr.append(filename);
return { normalize(dstr.copy_str(using), path.env), path.env };
};
}
fn Path! Path.tappend(Path path, String filename) => path.append(filename, mem::temp());
fn usz Path.start_of_base_name(Path path) @local
{
String path_str = path.path_string;
if (!path_str.len) return 0;
if (path.env == PathEnv.WIN32)
{
return path_str.rindex_of(`\`) + 1 ?? volume_name_len(path_str, path.env)!!;
}
return path_str.rindex_of("/") + 1 ?? 0;
}
fn String Path.basename(Path path)
{
usz basename_start = path.start_of_base_name();
String path_str = path.path_string;
if (basename_start == path_str.len) return "";
return path_str[basename_start..];
}
fn String Path.dirname(Path path)
{
usz basename_start = path.start_of_base_name();
String path_str = path.path_string;
if (basename_start == 0) return "";
usz start = volume_name_len(path_str, path.env)!!;
if (basename_start <= start + 1) return path_str[:basename_start];
return path_str[:basename_start - 1];
}
fn String! Path.extension(Path path)
{
String basename = path.basename();
usz index = basename.rindex_of(".")!;
// Plain ".foo" does not have an
if (index == 0) return SearchResult.MISSING?;
if (index == basename.len) return "";
return basename[index + 1..];
}
fn String Path.volume_name(Path path)
{
usz len = volume_name_len(path.as_str(), path.env)!!;
if (!len) return "";
return path.path_string[:len];
}
fn usz! volume_name_len(String path, PathEnv path_env) @local
{
usz len = path.len;
if (len < 2 || path_env != PathEnv.WIN32) return 0;
switch (path[0])
{
case '\\':
// "\\" paths.. must be longer than 2
if (len == 2) return 0;
int count = 1;
while (count < len && path[count] == '\\') count++;
// Not 2 => folded paths
if (count != 2) return 0;
// Check that we have a name followed by '/'
for (usz i = 2; i < len; i++)
{
char c = path[i];
if (is_win32_separator(c)) return i;
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
}
return PathResult.INVALID_PATH?;
case 'A'..'Z':
case 'a'..'z':
return path[1] == ':' ? 2 : 0;
default:
return 0;
}
}
fn Path! Path.parent(Path path)
{
if (path.path_string.len == 1 && is_separator(path.path_string[0], path.env)) return PathResult.NO_PARENT?;
foreach_r(i, c : path.path_string)
{
if (is_separator(c, path.env))
{
return { path.path_string[:i], path.env };
}
}
return PathResult.NO_PARENT?;
}
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
{
if (!path_str.len) return path_str;
usz path_start = volume_name_len(path_str, path_env)!;
usz path_len = path_str.len;
if (path_start == path_len) return path_str;
char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
usz len = path_start;
bool has_root = is_separator(path_str[path_start], path_env);
if (has_root)
{
path_str[len++] = path_separator;
path_start++;
}
// It is safe to write it as true, since we already dealt with /foo.
// This allows us to avoid checking whether it is the start of the path.
bool previous_was_separator = true;
for (usz i = path_start; i < path_len; i++)
{
char c = path_str[i];
// Fold foo///bar into foo/bar
if (is_separator(c, path_env))
{
// Fold //
if (previous_was_separator) continue;
// New /, so mark and rewrite
path_str.ptr[len++] = path_separator;
previous_was_separator = true;
continue;
}
// The rest are names of the path elements, so check that the
// characters are valid.
if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH?;
// If we have '.' after a separator
if (c == '.' && previous_was_separator)
{
// Get the number of dots until next separator, expecting 1 or 2
bool is_last = i == path_len - 1;
int dots = 1;
if (!is_last && path_str[i + 1] == '.')
{
dots = 2;
is_last = i == path_len - 2;
if (!is_last && !is_separator(path_str[i + 2], path_env))
{
dots = 0;
}
}
switch (dots)
{
case 1:
// /./abc -> skip to /./abc
// ^ ^
i++;
continue;
case 2:
// This is an error: /a/../..
if (len == path_start && has_root) return PathResult.INVALID_PATH?;
// If this .. at the start, or after ../? If so, we just copy ..
if (len == path_start ||
(len - path_start >= 3 && path_str[len - 1] == path_separator
&& path_str[len - 3] == '.' && path_str[len - 3] == '.' &&
(len - 3 == 0 || path_str[len - 4] == path_separator)))
{
if (i != len)
{
path_str[len] = '.';
path_str[len + 1] = '.';
}
len += 2;
if (len < path_len) path_str[len++] = path_separator;
i += 2;
continue;
}
// Step back, now looking at '/' abc/def/. -> abc/def/
len--;
// Step back until finding a separator or the start.
while (len > path_start && !is_separator(path_str[len - 1], path_env))
{
len--;
}
// Reading, we go from /../abc to /../abc
// ^ ^
i += 2;
continue;
default:
break;
}
}
if (i != len) path_str[len] = c;
previous_was_separator = false;
len++;
}
if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--;
path_str.ptr[len] = 0;
return path_str[:len];
}
fn ZString Path.as_zstr(Path path) => (ZString)path.path_string.ptr;
fn String Path.root_directory(Path path)
{
String path_str = path.as_str();
usz len = path_str.len;
if (!len) return "";
if (path.env == PathEnv.WIN32)
{
usz root_len = volume_name_len(path_str, path.env)!!;
if (root_len == len || !is_win32_separator(path_str[root_len])) return "";
return path_str[root_len..root_len];
}
if (!is_posix_separator(path_str[0])) return "";
for (usz i = 1; i < len; i++)
{
if (is_posix_separator(path_str[i]))
{
return path_str[:i];
}
}
return path_str;
}
fn String Path.as_str(Path path)
{
return path.path_string;
}
fn bool Path.has_suffix(Path path, String str)
{
return path.as_str().ends_with(str);
}
fn void Path.free(Path path)
{
free(path.path_string.ptr);
}
const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true,
['/'] = true,
};
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
[0..31] = true,
['>'] = true,
['<'] = true,
[':'] = true,
['\"'] = true,
['/'] = true,
['\\'] = true,
['|'] = true,
['?'] = true,
['*'] = true,
};
macro bool is_reserved_win32_path_char(char c)
{
return RESERVED_PATH_CHAR_WIN32[c];
}
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_PATH_ENV)
{
return path_env == PathEnv.WIN32
? RESERVED_PATH_CHAR_WIN32[c]
: RESERVED_PATH_CHAR_POSIX[c];
}

View File

@@ -0,0 +1,80 @@
module std::io;
import std::math;
struct ByteReader
{
char[] bytes;
usz index;
}
fn void ByteReader.init(ByteReader* reader, char[] bytes)
{
*reader = { .bytes = bytes };
}
fn Stream ByteReader.as_stream(ByteReader* reader)
{
return { .fns = &bytereader_interface, .data = reader };
}
fn usz! ByteReader.read(ByteReader* reader, char[] bytes)
{
if (reader.index >= reader.bytes.len) return IoError.EOF?;
usz len = math::min(reader.bytes.len - reader.index, bytes.len);
if (len == 0) return 0;
mem::copy(bytes.ptr, &reader.bytes[reader.index], len);
reader.index += len;
return len;
}
fn char! ByteReader.read_byte(ByteReader* reader)
{
if (reader.index >= reader.bytes.len) return IoError.EOF?;
return reader.bytes[reader.index++];
}
fn void! ByteReader.pushback_byte(ByteReader* reader)
{
if (!reader.index) return IoError.INVALID_PUSHBACK?;
reader.index--;
}
fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek)
{
isz new_index;
switch (seek)
{
case SET: new_index = offset;
case CURSOR: new_index = reader.index + offset;
case END: new_index = reader.bytes.len + offset;
}
if (new_index < 0) return IoError.INVALID_POSITION?;
reader.index = new_index;
return new_index;
}
fn usz! ByteReader.write_stream(ByteReader* reader, Stream* writer)
{
if (reader.index >= reader.bytes.len) return 0;
usz written = writer.write(reader.bytes[reader.index..])!;
reader.index += written;
assert(reader.index <= reader.bytes.len);
return written;
}
fn usz ByteReader.available(ByteReader* reader)
{
return math::max((isz)0, (isz)reader.bytes.len - reader.index);
}
StreamInterface bytereader_interface = {
.len_fn = fn (s) => ((ByteReader*)s.data).bytes.len,
.read_fn = fn (s, char[] bytes) => ((ByteReader*)s.data).read(bytes) @inline,
.read_byte_fn = fn (s) => ((ByteReader*)s.data).read_byte() @inline,
.pushback_byte_fn = fn (s) => ((ByteReader*)s.data).pushback_byte() @inline,
.seek_fn = fn (s, offset, seek) => ((ByteReader*)s.data).seek(offset, seek) @inline,
.write_stream_fn = fn (s, writer) => ((ByteReader*)s.data).write_stream(writer) @inline,
.available_fn = fn (s) => ((ByteReader*)s.data).available() @inline,
};

View File

@@ -0,0 +1,119 @@
module std::io;
struct ByteWriter
{
char[] bytes;
usz index;
Allocator* allocator;
}
/**
* @param [&inout] writer
* @param [&in] using
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure using != null, index == 0
**/
fn void ByteWriter.init(ByteWriter* writer, Allocator* using = mem::heap())
{
*writer = { .bytes = {}, .allocator = using };
}
fn void ByteWriter.init_buffer(ByteWriter* writer, char[] data)
{
*writer = { .bytes = data, .allocator = null };
}
/**
* @param [&inout] writer
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
**/
fn void ByteWriter.tinit(ByteWriter* writer)
{
*writer = { .bytes = {}, .allocator = mem::temp() };
}
fn Stream ByteWriter.as_stream(ByteWriter* writer)
{
return { .fns = &bytewriter_interface, .data = writer };
}
fn void ByteWriter.destroy(ByteWriter* writer)
{
if (!writer.allocator) return;
if (void* ptr = writer.bytes.ptr) free(ptr, .using = writer.allocator);
*writer = { };
}
fn String ByteWriter.as_str(ByteWriter* writer)
{
return (String)writer.bytes[:writer.index];
}
fn void! ByteWriter.ensure_capacity(ByteWriter* writer, usz len) @inline
{
if (writer.bytes.len > len) return;
if (!writer.allocator) return IoError.OUT_OF_SPACE?;
if (len < 16) len = 16;
usz new_capacity = math::next_power_of_2(len);
char* new_ptr = realloc_checked(writer.bytes.ptr, new_capacity, .using = writer.allocator)!;
writer.bytes = new_ptr[:new_capacity];
}
fn usz! ByteWriter.write(ByteWriter* writer, char[] bytes)
{
writer.ensure_capacity(writer.index + bytes.len)!;
mem::copy(&writer.bytes[writer.index], bytes.ptr, bytes.len);
writer.index += bytes.len;
return bytes.len;
}
fn void! ByteWriter.write_byte(ByteWriter* writer, char c)
{
writer.ensure_capacity(writer.index + 1)!;
writer.bytes[writer.index++] = c;
}
/**
* @param [&inout] writer
* @param [&inout] reader
**/
fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader)
{
if (reader.supports_available())
{
usz total_read = 0;
while (usz available = reader.available()!)
{
writer.ensure_capacity(writer.index + available)!;
usz len = reader.read(writer.bytes[writer.index..])!;
total_read += len;
writer.index += len;
}
return total_read;
}
usz total_read = 0;
while (true)
{
// See how much we can read.
usz len_to_read = writer.bytes.len - writer.index;
// Less than 16 bytes? Double the capacity
if (len_to_read < 16)
{
writer.ensure_capacity(writer.bytes.len * 2)!;
}
// Read into the rest of the buffer
usz read = reader.read(writer.bytes[writer.index..])!;
writer.index += read;
// Ok, we reached the end.
if (read < len_to_read) return total_read;
// Otherwise go another round
}
}
StreamInterface bytewriter_interface = {
.destroy_fn = fn (s) => ((ByteWriter*)s.data).destroy(),
.len_fn = fn (s) => ((ByteWriter*)s.data).bytes.len,
.write_fn = fn (s, char[] bytes) => ((ByteWriter*)s.data).write(bytes),
.write_byte_fn = fn (s, char c) => ((ByteWriter*)s.data).write_byte(c),
.read_stream_fn = fn (s, reader) => ((ByteWriter*)s.data).read_from(reader),
};

View File

@@ -0,0 +1,14 @@
module std::io;
fn Stream DString.as_stream(DString* dstring)
{
return { .fns = &dstring_interface, .data = dstring };
}
StreamInterface dstring_interface = {
.destroy_fn = fn (s) => ((DString*)s.data).free(),
.len_fn = fn (s) => ((DString*)s.data).len(),
.write_fn = fn (s, char[] bytes) { ((DString*)s.data).append_chars((String)bytes); return bytes.len; },
.write_byte_fn = fn (s, char c) => ((DString*)s.data).append_char(c),
.read_stream_fn = fn (s, reader) => ((DString*)s.data).read_from_stream(reader),
};

View File

@@ -0,0 +1,18 @@
module std::io;
fn Stream File.as_stream(File* file)
{
return { .fns = &filestream_interface, .data = file };
}
StreamInterface filestream_interface = {
.close_fn = fn (s) => ((File*)s.data).close(),
.seek_fn = fn (s, offset, seek) => ((File*)s.data).seek(offset, seek) @inline,
.read_fn = fn (s, char[] bytes) => ((File*)s.data).read(bytes) @inline,
.write_fn = fn (s, char[] bytes) => ((File*)s.data).write(bytes) @inline,
.write_byte_fn = fn (s, char c) => ((File*)s.data).putc(c) @inline,
.read_byte_fn = fn (s) => ((File*)s.data).getc() @inline,
.flush_fn = fn (s) => ((File*)s.data).flush() @inline,
};

640
lib/std/libc/libc.c3 Normal file
View File

@@ -0,0 +1,640 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module libc;
// Constants need to be per os/arch
const int EXIT_FAILURE = 1;
const int EXIT_SUCCESS = 0;
const int RAND_MAX = 0x7fffffff;
struct DivResult
{
int quot;
int rem;
}
struct LongDivResult
{
long quot;
long rem;
}
fn Errno errno()
{
return (Errno)os::errno();
}
fn void errno_set(Errno e)
{
os::errno_set((int)e);
}
def TerminateFunction = fn void();
def CompareFunction = fn int(void*, void*);
def JmpBuf = uptr[$$JMP_BUF_SIZE];
$if env::COMPILER_LIBC_AVAILABLE:
extern fn double atof(char* str);
extern fn int atoi(char* str);
extern fn CLongLong atoll(char* str);
extern fn double strtod(char* str, char** endptr);
extern fn CLong strtol(char* str, char** endptr, int base);
extern fn CULong stroul(char* str, char** endptr, int base);
extern fn void abort();
extern fn void atexit(TerminateFunction f);
extern fn void exit(int status);
extern fn ZString getenv(ZString name);
extern fn int setenv(ZString name, ZString value, int overwrite);
extern fn int unsetenv(ZString name);
extern fn int system(char* str);
extern fn void bsearch(void* key, void *base, usz items, usz size, CompareFunction compare);
extern fn void qsort(void* base, usz items, usz size, CompareFunction compare);
extern fn DivResult div(int numer, int denom);
extern fn long labs(long x);
extern fn LongDivResult ldiv(long number, long denom);
extern fn int rand();
extern fn void srand(uint seed);
extern fn void longjmp(JmpBuf* buffer, CInt value);
$if env::os_is_win32():
// TODO win32 aarch64
extern fn CInt _setjmp(void* frameptr, JmpBuf* buffer);
macro CInt setjmp(JmpBuf* buffer) => _setjmp($$frameaddress(), buffer);
$else
extern fn CInt setjmp(JmpBuf* buffer);
$endif
// MB functions omitted
// string
extern fn void* memchr(void* str, int c, usz n);
extern fn int memcmp(void* str1, void* str2, usz n);
extern fn void* memcpy(void* dest, void* src, usz n);
extern fn void* memmove(void* dest, void* src, usz n);
extern fn void* memset(void* dest, CInt value, usz n);
extern fn char* strcat(char* dest, char* src);
extern fn char* strncat(char* dest, char* src, usz n);
extern fn char* strchr(char* str, int c);
extern fn int strcmp(char* str1, char* str2);
extern fn int strncmp(char* str1, char* str2, usz n);
extern fn int strcoll(char* str1, char* str2);
extern fn char* strcpy(char* dst, char* src);
extern fn char* strncpy(char* dst, char* src, usz n);
extern fn usz strcspn(char* str1, char* str2);
extern fn char* strerror(int errn);
extern fn usz strlen(char* str);
extern fn char* strpbrk(char* str1, char* str2);
extern fn usz strspn(char* str1, char* str2);
extern fn char* strstr(char* haystack, char* needle);
extern fn char* strtok(char* str, char* delim);
extern fn usz strxfrm(char* dest, char* src, usz n);
// malloc
extern fn void* malloc(usz size);
extern fn void* calloc(usz count, usz size);
extern fn void* free(void*);
extern fn void* realloc(void* ptr, usz size);
$else
fn void longjmp(JmpBuf* buffer, CInt value) @weak @extern("longjmp") @nostrip
{
unreachable("longjmp unavailable");
}
fn CInt setjmp(JmpBuf* buffer) @weak @extern("setjmp") @nostrip
{
unreachable("setjmp unavailable");
}
fn void* malloc(usz size) @weak @extern("malloc") @nostrip
{
unreachable("malloc unavailable");
}
fn void* calloc(usz count, usz size) @weak @extern("calloc") @nostrip
{
unreachable("calloc unavailable");
}
fn void* free(void*) @weak @extern("free")
{
unreachable("free unavailable");
}
fn void* realloc(void* ptr, usz size) @weak @extern("realloc") @nostrip
{
unreachable("realloc unavailable");
}
fn void* memcpy(void* dest, void* src, usz n) @weak @extern("memcpy") @nostrip
{
for (usz i = 0; i < n; i++) ((char*)dest)[i] = ((char*)src)[i];
return dest;
}
fn void* memmove(void* dest, void* src, usz n) @weak @extern("memmove") @nostrip
{
return memcpy(dest, src, n) @inline;
}
fn void* memset(void* dest, CInt value, usz n) @weak @extern("memset") @nostrip
{
for (usz i = 0; i < n; i++) ((char*)dest)[i] = (char)value;
return dest;
}
$endif
// stdio
def Fpos = long;
def CFile = void*;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == LINUX:
extern CFile __stdin @extern("stdin");
extern CFile __stdout @extern("stdout");
extern CFile __stderr @extern("stderr");
extern fn usz malloc_usable_size(void* ptr);
macro usz malloc_size(void* ptr) { return malloc_usable_size(ptr); }
extern fn void* aligned_alloc(usz align, usz size);
macro CFile stdin() { return __stdin; }
macro CFile stdout() { return __stdout; }
macro CFile stderr() { return __stderr; }
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_darwin():
extern CFile __stdinp;
extern CFile __stdoutp;
extern CFile __stderrp;
extern fn usz malloc_size(void* ptr);
extern fn void* aligned_alloc(usz align, usz size);
macro CFile stdin() { return __stdinp; }
macro CFile stdout() { return __stdoutp; }
macro CFile stderr() { return __stderrp; }
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
extern fn CFile __acrt_iob_func(CInt c);
extern fn usz _msize(void* ptr);
macro usz malloc_size(void* ptr) { return _msize(ptr); }
macro CFile stdin() { return __acrt_iob_func(0); }
macro CFile stdout() { return __acrt_iob_func(1); }
macro CFile stderr() { return __acrt_iob_func(2); }
$default:
macro CFile stdin() { return (CFile*)(uptr)0; }
macro CFile stdout() { return (CFile*)(uptr)1; }
macro CFile stderr() { return (CFile*)(uptr)2; }
$endswitch
const HAS_MALLOC_SIZE =
env::OS_TYPE == LINUX
|| env::os_is_win32()
|| env::os_is_darwin();
// The following needs to be set per arch+os
// For now I have simply pulled the defaults from MacOS
const int SEEK_SET = 0;
const int SEEK_CUR = 1;
const int SEEK_END = 2;
const int _IOFBF = 0; // Fully buffered
const int _IOLBF = 1; // Line buffered
const int _IONBF = 2; // Unbuffered
const int BUFSIZ = 1024;
const int EOF = -1;
const int FOPEN_MAX = 20;
const int FILENAME_MAX = 1024;
def Errno = distinct CInt;
def SeekIndex = CLong;
$if env::COMPILER_LIBC_AVAILABLE:
extern fn int fclose(CFile stream);
extern fn void clearerr(CFile stream);
extern fn int feof(CFile stream);
extern fn int ferror(CFile stream);
extern fn int fflush(CFile stream);
extern fn int fgetpos(CFile stream, Fpos* pos);
extern fn CFile fopen(ZString filename, ZString mode);
extern fn usz fread(void* ptr, usz size, usz nmemb, CFile stream);
extern fn CFile freopen(ZString filename, ZString mode, CFile stream);
extern fn CFile fmemopen(void* ptr, usz size, ZString mode);
extern fn int fseek(CFile stream, SeekIndex offset, int whence);
extern fn int fsetpos(CFile stream, Fpos* pos);
extern fn SeekIndex ftell(CFile stream);
extern fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream);
extern fn int remove(char* filename);
extern fn int rename(char* old_name, char* new_name);
extern fn void rewind(CFile stream);
extern fn void setbuf(CFile stream, char* buffer);
extern fn void setvbuf(CFile stream, char* buffer, int mode, usz size);
extern fn CFile tmpnam(char* str);
extern fn int fprintf(CFile stream, char* format, ...);
extern fn int printf(char* format, ...);
extern fn int sprintf(char* str, char* format, ...);
extern fn int snprintf(char* str, usz size, char* format, ...);
extern fn int fscanf(CFile stream, char* format, ...);
extern fn int scanf(char* format, ...);
extern fn int sscanf(char* str, char* format, ...);
extern fn int fgetc(CFile stream);
extern fn char* fgets(char* str, int n, CFile stream);
extern fn int fputc(int c, CFile stream);
extern fn int getc(CFile stream);
extern fn int getchar();
extern fn int putc(int c, CFile stream);
extern fn int putchar(int c);
extern fn int puts(char* str);
extern fn int ungetc(int c, CFile stream);
extern fn void perror(char* str);
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
$else
fn int fseek(CFile stream, SeekIndex offset, int whence) @weak @extern("fseek") @nostrip
{
unreachable("'fseek' not available.");
}
fn CFile fopen(ZString filename, ZString mode) @weak @extern("fopen") @nostrip
{
unreachable("'fopen' not available.");
}
fn CFile freopen(ZString filename, ZString mode, CFile stream) @weak @extern("fopen") @nostrip
{
unreachable("'freopen' not available.");
}
fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fwrite") @nostrip
{
unreachable("'fwrite' not available.");
}
fn usz fread(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fread") @nostrip
{
unreachable("'fread' not available.");
}
fn CFile fclose(CFile) @weak @extern("fclose") @nostrip
{
unreachable("'fclose' not available.");
}
fn int fflush(CFile stream) @weak @extern("fflush") @nostrip
{
unreachable("'fflush' not available.");
}
fn int fputc(int c, CFile stream) @weak @extern("fputc") @nostrip
{
unreachable("'fputc' not available.");
}
fn char* fgets(ZString str, int n, CFile stream) @weak @extern("fgets") @nostrip
{
unreachable("'fgets' not available.");
}
fn int fgetc(CFile stream) @weak @extern("fgetc") @nostrip
{
unreachable("'fgetc' not available.");
}
fn int feof(CFile stream) @weak @extern("feof") @nostrip
{
unreachable("'feof' not available.");
}
fn int putc(int c, CFile stream) @weak @extern("putc") @nostrip
{
unreachable("'putc' not available.");
}
fn int putchar(int c) @weak @extern("putchar") @nostrip
{
unreachable("'putchar' not available.");
}
fn int puts(ZString str) @weak @extern("puts") @nostrip
{
unreachable("'puts' not available.");
}
$endif
// vsprintf vprintf not supported
// time.h
struct TmCommon @private
{
int tm_sec; /* seconds after the minute [0-60] */
int tm_min; /* minutes after the hour [0-59] */
int tm_hour; /* hours since midnight [0-23] */
int tm_mday; /* day of the month [1-31] */
int tm_mon; /* months since January [0-11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday [0-6] */
int tm_yday; /* days since January 1 [0-365] */
int tm_isdst; /* Daylight Savings Time flag */
}
$switch (env::OS_TYPE)
$case WIN32:
def Tm = TmCommon;
$case WASI:
def TimeOffset = int;
struct Tm
{
inline TmCommon common;
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
char *tm_zone; /* timezone abbreviation */
int tm_nsec;
}
$case MACOS:
$case IOS:
$case TVOS:
$case WATCHOS:
$case OPENBSD:
$case FREEBSD:
$default:
def TimeOffset = CLong;
struct Tm
{
inline TmCommon common;
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
char *tm_zone; /* timezone abbreviation */
}
$endswitch
$if env::os_is_win32():
struct TimeSpec
{
Time_t s;
ulong ns;
}
def Time_t = long;
def Clock_t = ulong;
$else
struct TimeSpec
{
Time_t s;
CLong ns;
}
def Time_t = CLong;
def Clock_t = CULong;
$endif
const int TIME_UTC = 1;
extern fn int timespec_get(TimeSpec* ts, int base);
extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
// Likely wrong, must be per platform.
const CLOCKS_PER_SEC = 1000000;
extern fn ZString asctime(Tm *timeptr);
extern fn Clock_t clock();
extern fn ZString ctime(Time_t *timer);
extern fn double difftime(Time_t time1, Time_t time2);
extern fn Tm* gmtime(Time_t *timer);
extern fn Tm* localtime(Time_t *timer);
$if env::os_is_win32():
extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer);
extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer);
extern fn void _get_timezone(CLong *timezone);
macro Tm* gmtime_r(Time_t *timer, Tm* buf) => _gmtime64_s(buf, timer);
macro Tm* localtime_r(Time_t *timer, Tm* buf) => _localtime64_s(buf, timer);
extern fn Time_t mktime(Tm *timeptr) @extern("_mktime64");
extern fn Time_t timegm(Tm *timeptr) @extern("_mkgmtime64");
$else
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf);
extern fn Tm* localtime_r(Time_t *timer, Tm* buf);
extern fn Time_t mktime(Tm *timeptr);
extern fn Time_t timegm(Tm *timeptr);
$endif
extern fn usz strftime(char* str, usz maxsize, char* format, Tm *timeptr);
extern fn Time_t time(Time_t *timer);
// signal
def SignalFunction = fn void(int);
extern fn SignalFunction signal(int sig, SignalFunction function);
// Incomplete
module libc::errno;
const Errno OK = 0;
const Errno EPERM = 1; // Operation not permitted
const Errno ENOENT = 2; // No such file or directory
const Errno ESRCH = 3; // No such process
const Errno EINTR = 4; // Interrupted system call
const Errno EIO = 5; // I/O error
const Errno ENXIO = 6; // No such device or address
const Errno E2BIG = 7; // Argument list too long
const Errno ENOEXEC = 8; // Exec format error
const Errno EBADF = 9; // Bad file number
const Errno ECHILD = 10; // No child processes
$if env::os_is_darwin():
const Errno EAGAIN = 35; // Try again Macos
$else
const Errno EAGAIN = 11; // Try again
$endif
const Errno ENOMEM = 12; // Out of memory
const Errno EACCES = 13; // Permission denied
const Errno EFAULT = 14; // Bad address
const Errno ENOTBLK = 15; // Block device required, not on Win32
const Errno EBUSY = 16; // Device or resource busy
const Errno EEXIST = 17; // File exists
const Errno EXDEV = 18; // Cross-device link
const Errno ENODEV = 19; // No such device
const Errno ENOTDIR = 20; // Not a directory
const Errno EISDIR = 21; // Is a directory
const Errno EINVAL = 22; // Invalid argument
const Errno ENFILE = 23; // File table overflow
const Errno EMFILE = 24; // Too many open files
const Errno ENOTTY = 25; // Not a typewriter
const Errno ETXTBSY = 26; // Text file busy, not on Win32
const Errno EFBIG = 27; // File too large
const Errno ENOSPC = 28; // No space left on device
const Errno ESPIPE = 29; // Illegal seek
const Errno EROFS = 30; // Read-only file system
const Errno EMLINK = 31; // Too many links
const Errno EPIPE = 32; // Broken pipe
const Errno EDOM = 33; // Math argument out of domain of func
const Errno ERANGE = 34; // Math result not representable
$switch (env::OS_TYPE)
$case MACOS:
const Errno EDEADLK = 11; // Resource deadlock would occur MacOS
const Errno ENAMETOOLONG = 63; // File name too long MacOS
const Errno ELOOP = 62; // Too many symbolic links encountered
const Errno EOVERFLOW = 84; // Value too large for defined data type Macos
const Errno ECONNRESET = 54; // Connection reset by peer Macos
const Errno ENETDOWN = 50; // Network is down MacOS
const Errno ENETUNREACH = 51; // Network is unreachable MacOS
const Errno ENETRESET = 52; // Network dropped connection because of reset MacOS
const Errno EOPNOTSUPP = 45; // Operation not supported on transport endpoint
const Errno ENOTEMPTY = 66; // Directory not empty
$case WIN32:
const Errno EDEADLK = 36; // Resource deadlock would occur Win32
const Errno ENAMETOOLONG = 38; // File name too long Win32
const Errno ELOOP = 114; // Too many symbolic links encountered
const Errno EOVERFLOW = 132; // Value too large for defined data type
const Errno ENETDOWN = 116; // Network is down
const Errno ECONNRESET = 108; // Connection reset by peer
const Errno ENETUNREACH = 118; // Network is unreachable
const Errno ENETRESET = 117; // Network dropped connection because of reset
const Errno EOPNOTSUPP = 130; // Operation not supported on transport endpoint
const Errno ENOTEMPTY = 41; // Directory not empty
$default:
const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?)
const Errno ENAMETOOLONG = 36; // File name too long Linux (others?)
const Errno ELOOP = 40; // Too many symbolic links encountered
const Errno EOVERFLOW = 75; // Value too large for defined data type
const Errno ENETDOWN = 100; // Network is down
const Errno ECONNRESET = 104; // Connection reset by peer
const Errno ENETUNREACH = 101; // Network is unreachable
const Errno ENETRESET = 102; // Network dropped connection because of reset
const Errno EOPNOTSUPP = 95; // Operation not supported on transport endpoint
const Errno ENOTEMPTY = 39; // Directory not empty
$endswitch
/*
const Errno ENOLCK = 37; /* No record locks available */
const Errno ENOSYS = 38; /* Function not implemented */
const Errno ENOMSG = 42; /* No message of desired type */
const Errno EIDRM = 43; /* Identifier removed */
const Errno ECHRNG = 44; /* Channel number out of range */
const Errno EL2NSYNC = 45; /* Level 2 not synchronized */
const Errno EL3HLT = 46; /* Level 3 halted */
const Errno EL3RST = 47; /* Level 3 reset */
const Errno ELNRNG = 48; /* Link number out of range */
const Errno EUNATCH = 49; /* Protocol driver not attached */
const Errno ENOCSI = 50; /* No CSI structure available */
const Errno EL2HLT = 51; /* Level 2 halted */
const Errno EBADE = 52; /* Invalid exchange */
const Errno EBADR = 53; /* Invalid request descriptor */
const Errno EXFULL = 54; /* Exchange full */
const Errno ENOANO = 55; /* No anode */
const Errno EBADRQC = 56; /* Invalid request code */
const Errno EBADSLT = 57; /* Invalid slot */
const Errno EBFONT = 59; /* Bad font file format */
const Errno ENOSTR = 60; /* Device not a stream */
const Errno ENODATA = 61; /* No data available */
const Errno ETIME = 62; /* Timer expired */
const Errno ENOSR = 63; /* Out of streams resources */
const Errno ENONET = 64; /* Machine is not on the network */
const Errno ENOPKG = 65; /* Package not installed */
const Errno EREMOTE = 66; /* Object is remote */
const Errno ENOLINK = 67; /* Link has been severed */
const Errno EADV = 68; /* Advertise error */
const Errno ESRMNT = 69; /* Srmount error */
const Errno ECOMM = 70; /* Communication error on send */
const Errno EPROTO = 71; /* Protocol error */
const Errno EMULTIHOP = 72; /* Multihop attempted */
const Errno EDOTDOT = 73; /* RFS specific error */
const Errno EBADMSG = 74; /* Not a data message */
const Errno ENOTUNIQ = 76; /* Name not unique on network */
const Errno EBADFD = 77; /* File descriptor in bad state */
const Errno EREMCHG = 78; /* Remote address changed */
const Errno ELIBACC = 79; /* Can not access a needed shared library */
const Errno ELIBBAD = 80; /* Accessing a corrupted shared library */
const Errno ELIBSCN = 81; /* .lib section in a.out corrupted */
const Errno ELIBMAX = 82; /* Attempting to link in too many shared libraries */
const Errno ELIBEXEC = 83; /* Cannot exec a shared library directly */
const Errno EILSEQ = 84; /* Illegal byte sequence */
const Errno ERESTART = 85; /* Interrupted system call should be restarted */
const Errno ESTRPIPE = 86; /* Streams pipe error */
const Errno EUSERS = 87; /* Too many users */
const Errno ENOTSOCK = 88; /* Socket operation on non-socket */
const Errno EDESTADDRREQ = 89; /* Destination address required */
const Errno EMSGSIZE = 90; /* Message too long */
const Errno EPROTOTYPE = 91; /* Protocol wrong type for socket */
const Errno ENOPROTOOPT = 92; /* Protocol not available */
const Errno EPROTONOSUPPORT = 93; /* Protocol not supported */
const Errno ESOCKTNOSUPPORT = 94; /* Socket type not supported */
const Errno EPFNOSUPPORT = 96; /* Protocol family not supported */
const Errno EAFNOSUPPORT = 97; /* Address family not supported by protocol */
const Errno EADDRINUSE = 98; /* Address already in use */
const Errno EADDRNOTAVAIL = 99; /* Cannot assign requested address */
const Errno ECONNABORTED = 103; /* Software caused connection abort */
const Errno ENOBUFS = 105; /* No buffer space available */
const Errno EISCONN = 106; /* Transport endpoint is already connected */
const Errno ENOTCONN = 107; /* Transport endpoint is not connected */
const Errno ESHUTDOWN = 108; /* Cannot send after transport endpoint shutdown */
const Errno ETOOMANYREFS = 109; /* Too many references: cannot splice */
const Errno ECONNREFUSED = 111; /* Connection refused */
const Errno EHOSTDOWN = 112; /* Host is down */
const Errno EHOSTUNREACH = 113; /* No route to host */
*/
$switch (env::OS_TYPE)
$case MACOS:
const Errno ETIMEDOUT = 60; // Connection timed out
const Errno EINPROGRESS = 36; // Operation now in progress MacOS
const Errno EALREADY = 37; // Operation already in progress MacOS
const Errno EDQUOT = 69; // Quota exceeded, MacOS
const Errno EWOULDBLOCK = 35; // Operation would block
$case WIN32:
const Errno ETIMEDOUT = 138; // Connection timed out
const Errno EALREADY = 103; // Operation already in progress
const Errno EINPROGRESS = 112; // Operation now in progress Win32
const Errno EDQUOT = -122; // Quota exceeded, not in Win32
const Errno EWOULDBLOCK = 140; // Operation would block
$default:
const Errno ETIMEDOUT = 110; // Connection timed out
const Errno EALREADY = 114; // Operation already in progress
const Errno EINPROGRESS = 115; // Operation now in progress
const Errno EDQUOT = 122; // Quota exceeded
const Errno EWOULDBLOCK = 41; // Operation would block
$endswitch
/*
const Errno ESTALE = 116; /* Stale NFS file handle */
const Errno EUCLEAN = 117; /* Structure needs cleaning */
const Errno ENOTNAM = 118; /* Not a XENIX named type file */
const Errno ENAVAIL = 119; /* No XENIX semaphores available */
const Errno EISNAM = 120; /* Is a named type file */
const Errno EREMOTEIO = 121; /* Remote I/O error */
const Errno ENOMEDIUM = 123; /* No medium found */
const Errno EMEDIUMTYPE = 124; /* Wrong medium type */
const Errno ECANCELED = 125; /* Operation Canceled */
const Errno ENOKEY = 126; /* Required key not available */
const Errno EKEYEXPIRED = 127; /* Key has expired */
const Errno EKEYREVOKED = 128; /* Key has been revoked */
const Errno EKEYREJECTED = 129; /* Key was rejected by service */
const Errno EOWNERDEAD = 130; /* Owner died */
const Errno ENOTRECOVERABLE = 131; /* State not recoverable */
*/

38
lib/std/libc/os/errno.c3 Normal file
View File

@@ -0,0 +1,38 @@
module libc::os;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == LINUX:
extern fn int* __errno_location();
macro int errno() => *__errno_location();
macro void errno_set(int err) => *(__errno_location()) = err;
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == MACOS:
extern fn int* __error();
macro int errno() => *__error();
macro void errno_set(int err) => *(__error()) = err;
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == WIN32:
macro int errno()
{
int holder;
_get_errno(&holder);
return holder;
}
macro void errno_set(int err) => _set_errno(err);
extern fn void _get_errno(int* result);
extern fn void _set_errno(int err);
$default:
tlocal int _errno_c3 = 0;
fn void errno_set(int err) => _errno_c3 = err;
fn int errno() => _errno_c3;
$endswitch

950
lib/std/math/math.c3 Normal file
View File

@@ -0,0 +1,950 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::math;
import std::math::complex;
import std::math::matrix;
import std::math::quaternion;
// TODO Define these using quad precision.
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
const LOG10E = 0.434294481903251827651128918916605082; // log10(e)
const LN2 = 0.693147180559945309417232121458176568; // ln(2)
const LN10 = 2.30258509299404568401799145468436421; // ln(10)
const PI = 3.14159265358979323846264338327950288419716939937510; // pi
const PI_2 = 1.57079632679489661923132169163975144; // pi / 2
const PI_4 = 0.785398163397448309615660845819875721; // pi / 4
const DIV_PI = 0.318309886183790671537767526745028724; // 1 / pi
const DIV_2_PI = 0.636619772367581343075535053490057448; // 2 / pi
const DIV_2_SQRTPI = 1.12837916709551257389615890312154517; // 2/sqrt(pi)
const SQRT2 = 1.41421356237309504880168872420969808; // sqrt(2)
const double DIV_1_SQRT2 = 0.707106781186547524400844362104849039; // 1 / sqrt(2)
const HALF_MAX = 6.5504e+4;
const HALF_MIN = 6.103515625e-5;
const HALF_DENORM_MIN = 5.9604644775390625e-8;
const HALF_DIG = 3;
const HALF_DEC_DIGITS = 5;
const HALF_MANT_DIG = 11;
const HALF_MAX_10_EXP = 4;
const HALF_MIN_10_EXP = -4;
const HALF_MAX_EXP = 16;
const HALF_MIN_EXP = -13;
const HALF_EPSILON = 9.765625e-4;
const FLOAT_MAX = 0x1.fffffep+127;
const FLOAT_MIN = 1.17549435e-38;
const FLOAT_DENORM_MIN = 1.40129846432481707092e-45;
const FLOAT_DIG = 6;
const FLOAT_DEC_DIGITS = 9;
const FLOAT_MANT_DIG = 24;
const FLOAT_MAX_10_EXP = 38;
const FLOAT_MIN_10_EXP = -37;
const FLOAT_MAX_EXP = 128;
const FLOAT_MIN_EXP = -125;
const FLOAT_EPSILON = 1.1920928955078125e-07;
const DOUBLE_MAX = 1.79769313486231570815e+308;
const DOUBLE_MIN = 2.2250738585072014e-308;
const DOUBLE_DENORM_MIN = 4.94065645841246544177e-324;
const DOUBLE_DIG = 15;
const DOUBLE_DEC_DIGITS = 17;
const DOUBLE_MANT_DIG = 53;
const DOUBLE_MAX_10_EXP = 308;
const DOUBLE_MIN_10_EXP = -307;
const DOUBLE_MAX_EXP = 1024;
const DOUBLE_MIN_EXP = -1021;
const DOUBLE_EPSILON = 2.22044604925031308085e-16;
const QUAD_MANT_DIG = 113;
/*
const QUAD_MAX = 1.18973149535723176508575932662800702e+4932;
const QUAD_MIN = 3.36210314311209350626267781732175260e-4932;
const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966;
const QUAD_DIG = 33;
const QUAD_DEC_DIGITS = 36;
const QUAD_MAX_10_EXP = 4932;
const QUAD_MIN_10_EXP = -4931;
const QUAD_MAX_EXP = 16384;
const QUAD_MIN_EXP = -16481;
const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34;
*/
enum RoundingMode : int
{
TOWARD_ZERO,
TO_NEAREST,
TOWARD_INFINITY,
TOWARD_NEG_INFINITY
}
fault MatrixError
{
MATRIX_INVERSE_DOESNT_EXIST,
}
def Complexf = Complex<float>;
def Complex = Complex<double>;
def complexf_identity = complex::identity<float>;
def complex_identity = complex::identity<double>;
def Quaternionf = Quaternion<float>;
def Quaternion = Quaternion<double>;
def quaternionf_identity = quaternion::identity<float>;
def quaternion_identity = quaternion::identity<double>;
def Matrix2f = Matrix2x2<float>;
def Matrix2 = Matrix2x2<double>;
def Matrix3f = Matrix3x3<float>;
def Matrix3 = Matrix3x3<double>;
def Matrix4f = Matrix4x4<float>;
def Matrix4 = Matrix4x4<double>;
def matrix4_ortho = matrix::ortho<double>;
def matrix4_perspective = matrix::perspective<double>;
def matrix4f_ortho = matrix::ortho<float>;
def matrix4f_perspective = matrix::perspective<float>;
def MATRIX2_IDENTITY = matrix::IDENTITY2<double>;
def MATRIX2F_IDENTITY = matrix::IDENTITY2<float>;
def MATRIX3_IDENTITY = matrix::IDENTITY3<double>;
def MATRIX3F_IDENTITY = matrix::IDENTITY3<float>;
def MATRIX4_IDENTITY = matrix::IDENTITY4<double>;
def MATRIX4F_IDENTITY = matrix::IDENTITY4<float>;
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
**/
macro abs(x) => $$abs(x);
/**
* @require values::@is_int(x) `The input must be an integer`
**/
macro sign(x)
{
var $Type = $typeof(x);
$if $Type.kindof == TypeKind.UNSIGNED_INT:
return ($Type)(x > 0);
$else
return ($Type)(x > 0) - ($Type)(x < 0);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
* @checked x + y
**/
macro atan2(x, y)
{
$if @typeis(x, float) && @typeis(y, float):
return _atan2f(x, y);
$else
return _atan2(x, y);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
* @checked (*y)[0] = x, y.len
* @require y.len == 2
**/
macro sincos(x, y)
{
$if $typeof(y[0]).typeid == float.typeid:
return _sincosf(x, y);
$else
return _sincos(x, y);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
* @checked x
**/
macro atan(x)
{
$if $typeof(x).typeid == float.typeid:
return _atanf(x);
$else
return _atan(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro atanh(x)
{
$if $typeof(x).typeid == float.typeid:
return _atanhf(x);
$else
return _atanh(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro acos(x)
{
$if $typeof(x).typeid == float.typeid:
return _acosf(x);
$else
return _acos(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro acosh(x)
{
$if $typeof(x).typeid == float.typeid:
return _acoshf(x);
$else
return _acosh(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro asin(x)
{
$if $typeof(x).typeid == float.typeid:
return _asinf(x);
$else
return _asin(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro asinh(x)
{
$if $typeof(x).typeid == float.typeid:
return _asinhf(x);
$else
return _asinh(x);
$endif
}
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro ceil(x) => $$ceil(x);
/**
* Constrain the value to lie within the given interval.
*
* @param x "the value to clamp, may be a number or a numerical vector."
* @param lower "the lower bounds"
* @param upper "the upper bounds"
* @return "lower if x < lower, upper if x > upper, otherwise return x."
*
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
* @checked $typeof(x) z = lower `The lower bound must be convertable to the value type.`
* @checked $typeof(x) z = upper `The upper bound must be convertable to the value type.`
**/
macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))upper));
/**
* @require values::@is_promotable_to_floatlike(mag) `The input must be a number value or float vector`
* @require @convertable(sgn, $typeof(values::promote_int(mag)))
**/
macro copysign(mag, sgn) => $$copysign(values::promote_int(mag), ($typeof(values::promote_int(mag)))sgn);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cos(x) => $$cos(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cosec(x) => 1 / sin(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cosech(x) => 2 / (exp(x) - exp(-x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cosh(x) => (exp(x) + exp(-x)) / 2.0;
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cotan(x) => cos(x) / sin(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cotanh(x) => (exp(2.0 * x) + 1.0) / (exp(2.0 * x) - 1.0);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro exp(x) => $$exp(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro exp2(x) => $$exp2(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro floor(x) => $$floor(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(a) `The input must be a number or float vector`
* @require values::@is_promotable_to_floatlike(b) `The input must be a number or float vector`
* @require values::@is_promotable_to_floatlike(c) `The input must be a number or float vector`
* @require types::@is_same_vector_type(a, b) `The input types must be equal`
* @require types::@is_same_vector_type(a, c) `The input types must match`
**/
macro fma(a, b, c) => $$fma(a, b, c);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
* @require values::@is_promotable_to_floatlike(y) `The input must be a number or a float vector`
* @require types::@is_same_vector_type(x, y) `The input types must match`
**/
macro hypot(x, y) => sqrt(sqr(x) + sqr(y));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro log(x) => $$log(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro log2(x) => $$log2(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro log10(x) => $$log10(values::promote_int(x));
/**
* @require types::is_numerical($typeof(x)) `The input must be a floating point value or float vector`
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
**/
macro max(x, y, ...)
{
$if $vacount == 0:
return $$max(x, y);
$else
var m = $$max(x, y);
$for (var $i = 0; $i < $vacount; $i++)
m = $$max(m, $vaarg($i));
$endfor
return m;
$endif
}
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
**/
macro min(x, y, ...)
{
$if $vacount == 0:
return $$min(x, y);
$else
var m = $$min(x, y);
$for (var $i = 0; $i < $vacount; $i++)
m = $$min(m, $vaarg($i));
$endfor
return m;
$endif
}
/**
* @require types::@is_float(a) `The input must be a floating point value`
* @require types::@has_same(a, b, c) `The input types must be equal`
**/
macro muladd(a, b, c) => $$fmuladd(a, b, c);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro nearbyint(x) => $$nearbyint(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
* @require values::@convertable_to(exp, x) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
**/
macro pow(x, exp)
{
$if types::is_floatlike($typeof(exp)):
return $$pow(x, ($typeof(x))exp);
$else
return $$pow_int(x, exp);
$endif
}
/**
* @require values::@is_promotable_to_float(x) : `The input must be integer or floating type`
**/
macro frexp(x, int* e)
{
$switch ($typeof(x))
$case float:
$case float16:
return _frexpf((float)x, e);
$default:
return _frexp((double)x, e);
$endswitch
}
/**
* @require values::@is_promotable_to_float(x) : `The input must be integer or floating type`
**/
macro int signbit(x)
{
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) >> 31;
$default:
return (int)(bitcast((double)x, ulong) >> 63);
$endswitch
}
/**
* @require values::@is_floatlike(x) `The input must be a number or a float vector`
**/
macro rint(x) => $$rint(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro round(x) => $$round(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro roundeven(x) => $$roundeven(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sec(x) => 1 / cos(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sech(x) => 2 / (exp(x) + exp(-x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sin(x) => $$sin(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sinh(x) => (exp(x) - exp(-x)) / 2.0;
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sqr(x) => values::promote_int(x) * values::promote_int(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sqrt(x) => $$sqrt(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro tan(x)
{
var $Type = $typeof(x);
$switch
$case types::is_vector($Type):
return $$sin(x) / $$cos(x);
$case $Type.typeid == float.typeid:
return _tanf(x);
$default:
return _tan(x);
$endswitch
}
/**
* @require values::@is_promotable_to_float(x) `The input must be a float`
**/
macro bool is_finite(x)
{
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) & 0x7fffffff < 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) < 0x7ffu64 << 52;
$endswitch
}
/**
* @require values::@is_promotable_to_float(x) `The input must be a float`
**/
macro is_nan(x)
{
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) & 0x7fffffff > 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ffu64 << 52;
$endswitch
}
/**
* @require values::@is_promotable_to_float(x) `The input must be a float`
**/
macro is_inf(x)
{
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) & 0x7fffffff == 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ffu64 << 52;
$endswitch
}
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro tanh(x) => (exp(2.0 * x) - 1.0) / (exp(2.0 * x) + 1.0);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro trunc(x) => $$trunc(x);
macro lerp(x, y, amount) @private => x + (y - x) * amount;
macro reflect(x, y) @private
{
var dot = x.dot(y);
return x - 2 * y * dot;
}
macro normalize(x) @private
{
var len = x.length();
if (len == 0) return x;
return x * (1 / len);
}
macro float float.ceil(float x) => $$ceil(x);
macro float float.clamp(float x, float lower, float upper) => $$max(lower, $$min(x, upper));
macro float float.copysign(float mag, float sgn) => $$copysign(mag, sgn);
macro float float.floor(float x) => $$floor(x);
macro float float.fma(float a, float b, float c) => $$fma(a, b, c);
macro float float.muladd(float a, float b, float c) => $$fmuladd(a, b, c);
macro float float.nearbyint(float x) => $$nearbyint(x);
macro float float.pow(float x, exp) => pow(x, exp);
macro float float.rint(float x) => $$rint(x);
macro float float.round(float x) => $$round(x);
macro float float.roundeven(float x) => $$roundeven(x);
macro float float.trunc(float x) => $$trunc(x);
macro float float[<*>].sum(float[<*>] x, float start = 0.0) => $$reduce_fadd(x, start);
macro float float[<*>].product(float[<*>] x, float start = 1.0) => $$reduce_fmul(x, start);
macro float float[<*>].max(float[<*>] x) => $$reduce_max(x);
macro float float[<*>].min(float[<*>] x) => $$reduce_min(x);
macro float[<*>] float[<*>].ceil(float[<*>] x) => $$ceil(x);
macro float[<*>] float[<*>].clamp(float[<*>] x, float[<*>] lower, float[<*>] upper) => $$max(lower, $$min(x, upper));
macro float[<*>] float[<*>].copysign(float[<*>] mag, float[<*>] sgn) => $$copysign(mag, sgn);
macro float[<*>] float[<*>].fma(float[<*>] a, float[<*>] b, float[<*>] c) => $$fma(a, b, c);
macro float[<*>] float[<*>].floor(float[<*>] x) => $$floor(x);
macro float[<*>] float[<*>].nearbyint(float[<*>] x) => $$nearbyint(x);
macro float[<*>] float[<*>].pow(float[<*>] x, exp) => pow(x, exp);
macro float[<*>] float[<*>].rint(float[<*>] x) => $$rint(x);
macro float[<*>] float[<*>].round(float[<*>] x) => $$round(x);
macro float[<*>] float[<*>].roundeven(float[<*>] x) => $$roundeven(x);
macro float[<*>] float[<*>].trunc(float[<*>] x) => $$trunc(x);
macro float float[<*>].dot(float[<*>] x, float[<*>] y) => (x * y).sum();
macro float float[<*>].length(float[<*>] x) => $$sqrt(x.dot(x));
macro float float[<*>].distance(float[<*>] x, float[<*>] y) => (x - y).length();
macro float[<*>] float[<*>].normalize(float[<*>] x) => normalize(x);
macro float[<*>] float[<*>].lerp(float[<*>] x, float[<*>] y, float amount) => lerp(x, y, amount);
macro float[<*>] float[<*>].reflect(float[<*>] x, float[<*>] y) => reflect(x, y);
macro bool float[<*>].equals(float[<*>] x, float[<*>] y) => equals_vec(x, y);
macro bool[<*>] float[<*>].comp_lt(float[<*>] x, float[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] float[<*>].comp_le(float[<*>] x, float[<*>] y) => $$veccomple(x, y);
macro bool[<*>] float[<*>].comp_eq(float[<*>] x, float[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] float[<*>].comp_gt(float[<*>] x, float[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] float[<*>].comp_ge(float[<*>] x, float[<*>] y) => $$veccompge(x, y);
macro bool[<*>] float[<*>].comp_ne(float[<*>] x, float[<*>] y) => $$veccompne(x, y);
macro double double.ceil(double x) => $$ceil(x);
macro double double.clamp(double x, double lower, double upper) => $$max(lower, $$min(x, upper));
macro double double.copysign(double mag, double sgn) => $$copysign(mag, sgn);
macro double double.floor(double x) => $$floor(x);
macro double double.fma(double a, double b, double c) => $$fma(a, b, c);
macro double double.muladd(double a, double b, double c) => $$fmuladd(a, b, c);
macro double double.nearbyint(double x) => $$nearbyint(x);
macro double double.pow(double x, exp) => pow(x, exp);
macro double double.rint(double x) => $$rint(x);
macro double double.round(double x) => $$round(x);
macro double double.roundeven(double x) => $$roundeven(x);
macro double double.trunc(double x) => $$trunc(x);
macro double double[<*>].sum(double[<*>] x, double start = 0.0) => $$reduce_fadd(x, start);
macro double double[<*>].product(double[<*>] x, double start = 1.0) => $$reduce_fmul(x, start);
macro double double[<*>].max(double[<*>] x) => $$reduce_fmax(x);
macro double double[<*>].min(double[<*>] x) => $$reduce_fmin(x);
macro double[<*>] double[<*>].ceil(double[<*>] x) => $$ceil(x);
macro double[<*>] double[<*>].clamp(double[<*>] x, double[<*>] lower, double[<*>] upper) => $$max(lower, $$min(x, upper));
macro double[<*>] double[<*>].copysign(double[<*>] mag, double[<*>] sgn) => $$copysign(mag, sgn);
macro double[<*>] double[<*>].floor(double[<*>] x) => $$floor(x);
macro double[<*>] double[<*>].fma(double[<*>] a, double[<*>] b, double[<*>] c) => $$fma(a, b, c);
macro double[<*>] double[<*>].nearbyint(double[<*>] x) => $$nearbyint(x);
macro double[<*>] double[<*>].pow(double[<*>] x, exp) => pow(x, exp);
macro double[<*>] double[<*>].rint(double[<*>] x) => $$rint(x);
macro double[<*>] double[<*>].round(double[<*>] x) => $$round(x);
macro double[<*>] double[<*>].roundeven(double[<*>] x) => $$roundeven(x);
macro double[<*>] double[<*>].trunc(double[<*>] x) => $$trunc(x);
macro double double[<*>].dot(double[<*>] x, double[<*>] y) => (x * y).sum();
macro double double[<*>].length(double[<*>] x) => $$sqrt(x.dot(x));
macro double double[<*>].distance(double[<*>] x, double[<*>] y) => (x - y).length();
macro double[<*>] double[<*>].normalize(double[<*>] x) => normalize(x);
macro double[<*>] double[<*>].reflect(double[<*>] x, double[<*>] y) => reflect(x, y);
macro double[<*>] double[<*>].lerp(double[<*>] x, double[<*>] y, double amount) => lerp(x, y, amount);
macro bool double[<*>].equals(double[<*>] x, double[<*>] y) => equals_vec(x, y);
macro bool[<*>] double[<*>].comp_lt(double[<*>] x, double[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] double[<*>].comp_le(double[<*>] x, double[<*>] y) => $$veccomple(x, y);
macro bool[<*>] double[<*>].comp_eq(double[<*>] x, double[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] double[<*>].comp_gt(double[<*>] x, double[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] double[<*>].comp_ge(double[<*>] x, double[<*>] y) => $$veccompge(x, y);
macro bool[<*>] double[<*>].comp_ne(double[<*>] x, double[<*>] y) => $$veccompne(x, y);
macro bool[<*>] ichar[<*>].comp_lt(ichar[<*>] x, ichar[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ichar[<*>].comp_le(ichar[<*>] x, ichar[<*>] y) => $$veccomple(x, y);
macro bool[<*>] ichar[<*>].comp_eq(ichar[<*>] x, ichar[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] ichar[<*>].comp_gt(ichar[<*>] x, ichar[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] ichar[<*>].comp_ge(ichar[<*>] x, ichar[<*>] y) => $$veccompge(x, y);
macro bool[<*>] ichar[<*>].comp_ne(ichar[<*>] x, ichar[<*>] y) => $$veccompne(x, y);
macro ichar ichar[<*>].sum(ichar[<*>] x) => $$reduce_add(x);
macro ichar ichar[<*>].product(ichar[<*>] x) => $$reduce_mul(x);
macro ichar ichar[<*>].and(ichar[<*>] x) => $$reduce_and(x);
macro ichar ichar[<*>].or(ichar[<*>] x) => $$reduce_or(x);
macro ichar ichar[<*>].xor(ichar[<*>] x) => $$reduce_xor(x);
macro ichar ichar[<*>].max(ichar[<*>] x) => $$reduce_max(x);
macro ichar ichar[<*>].min(ichar[<*>] x) => $$reduce_min(x);
macro bool[<*>] short[<*>].comp_lt(short[<*>] x, short[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] short[<*>].comp_le(short[<*>] x, short[<*>] y) => $$veccomple(x, y);
macro bool[<*>] short[<*>].comp_eq(short[<*>] x, short[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] short[<*>].comp_gt(short[<*>] x, short[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] short[<*>].comp_ge(short[<*>] x, short[<*>] y) => $$veccompge(x, y);
macro bool[<*>] short[<*>].comp_ne(short[<*>] x, short[<*>] y) => $$veccompne(x, y);
macro short short[<*>].sum(short[<*>] x) => $$reduce_add(x);
macro short short[<*>].product(short[<*>] x) => $$reduce_mul(x);
macro short short[<*>].and(short[<*>] x) => $$reduce_and(x);
macro short short[<*>].or(short[<*>] x) => $$reduce_or(x);
macro short short[<*>].xor(short[<*>] x) => $$reduce_xor(x);
macro short short[<*>].max(short[<*>] x) => $$reduce_max(x);
macro short short[<*>].min(short[<*>] x) => $$reduce_min(x);
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) => $$veccomple(x, y);
macro bool[<*>] int[<*>].comp_eq(int[<*>] x, int[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] int[<*>].comp_gt(int[<*>] x, int[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] int[<*>].comp_ge(int[<*>] x, int[<*>] y) => $$veccompge(x, y);
macro bool[<*>] int[<*>].comp_ne(int[<*>] x, int[<*>] y) => $$veccompne(x, y);
macro int int[<*>].sum(int[<*>] x) => $$reduce_add(x);
macro int int[<*>].product(int[<*>] x) => $$reduce_mul(x);
macro int int[<*>].and(int[<*>] x) => $$reduce_and(x);
macro int int[<*>].or(int[<*>] x) => $$reduce_or(x);
macro int int[<*>].xor(int[<*>] x) => $$reduce_xor(x);
macro int int[<*>].max(int[<*>] x) => $$reduce_max(x);
macro int int[<*>].min(int[<*>] x) => $$reduce_min(x);
macro bool[<*>] long[<*>].comp_lt(long[<*>] x, long[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] long[<*>].comp_le(long[<*>] x, long[<*>] y) => $$veccomple(x, y);
macro bool[<*>] long[<*>].comp_eq(long[<*>] x, long[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] long[<*>].comp_gt(long[<*>] x, long[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] long[<*>].comp_ge(long[<*>] x, long[<*>] y) => $$veccompge(x, y);
macro bool[<*>] long[<*>].comp_ne(long[<*>] x, long[<*>] y) => $$veccompne(x, y);
macro long long[<*>].sum(long[<*>] x) => $$reduce_add(x);
macro long long[<*>].product(long[<*>] x) => $$reduce_mul(x);
macro long long[<*>].and(long[<*>] x) => $$reduce_and(x);
macro long long[<*>].or(long[<*>] x) => $$reduce_or(x);
macro long long[<*>].xor(long[<*>] x) => $$reduce_xor(x);
macro long long[<*>].max(long[<*>] x) => $$reduce_max(x);
macro long long[<*>].min(long[<*>] x) => $$reduce_min(x);
macro bool[<*>] int128[<*>].comp_lt(int128[<*>] x, int128[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] int128[<*>].comp_le(int128[<*>] x, int128[<*>] y) => $$veccomple(x, y);
macro bool[<*>] int128[<*>].comp_eq(int128[<*>] x, int128[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] int128[<*>].comp_gt(int128[<*>] x, int128[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] int128[<*>].comp_ge(int128[<*>] x, int128[<*>] y) => $$veccompge(x, y);
macro bool[<*>] int128[<*>].comp_ne(int128[<*>] x, int128[<*>] y) => $$veccompne(x, y);
macro int128 int128[<*>].sum(int128[<*>] x) => $$reduce_add(x);
macro int128 int128[<*>].product(int128[<*>] x) => $$reduce_mul(x);
macro int128 int128[<*>].and(int128[<*>] x) => $$reduce_and(x);
macro int128 int128[<*>].or(int128[<*>] x) => $$reduce_or(x);
macro int128 int128[<*>].xor(int128[<*>] x) => $$reduce_xor(x);
macro int128 int128[<*>].max(int128[<*>] x) => $$reduce_max(x);
macro int128 int128[<*>].min(int128[<*>] x) => $$reduce_min(x);
macro bool[<*>] bool[<*>].comp_lt(bool[<*>] x, bool[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] bool[<*>].comp_le(bool[<*>] x, bool[<*>] y) => $$veccomple(x, y);
macro bool[<*>] bool[<*>].comp_eq(bool[<*>] x, bool[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] bool[<*>].comp_gt(bool[<*>] x, bool[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] bool[<*>].comp_ge(bool[<*>] x, bool[<*>] y) => $$veccompge(x, y);
macro bool[<*>] bool[<*>].comp_ne(bool[<*>] x, bool[<*>] y) => $$veccompne(x, y);
macro bool bool[<*>].sum(bool[<*>] x) => $$reduce_add(x);
macro bool bool[<*>].product(bool[<*>] x) => $$reduce_mul(x);
macro bool bool[<*>].and(bool[<*>] x) => $$reduce_and(x);
macro bool bool[<*>].or(bool[<*>] x) => $$reduce_or(x);
macro bool bool[<*>].xor(bool[<*>] x) => $$reduce_xor(x);
macro bool bool[<*>].max(bool[<*>] x) => $$reduce_max(x);
macro bool bool[<*>].min(bool[<*>] x) => $$reduce_min(x);
macro bool[<*>] char[<*>].comp_lt(char[<*>] x, char[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] char[<*>].comp_le(char[<*>] x, char[<*>] y) => $$veccomple(x, y);
macro bool[<*>] char[<*>].comp_eq(char[<*>] x, char[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] char[<*>].comp_gt(char[<*>] x, char[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] char[<*>].comp_ge(char[<*>] x, char[<*>] y) => $$veccompge(x, y);
macro bool[<*>] char[<*>].comp_ne(char[<*>] x, char[<*>] y) => $$veccompne(x, y);
macro char char[<*>].sum(char[<*>] x) => $$reduce_add(x);
macro char char[<*>].product(char[<*>] x) => $$reduce_mul(x);
macro char char[<*>].and(char[<*>] x) => $$reduce_and(x);
macro char char[<*>].or(char[<*>] x) => $$reduce_or(x);
macro char char[<*>].xor(char[<*>] x) => $$reduce_xor(x);
macro char char[<*>].max(char[<*>] x) => $$reduce_max(x);
macro char char[<*>].min(char[<*>] x) => $$reduce_min(x);
macro bool[<*>] ushort[<*>].comp_lt(ushort[<*>] x, ushort[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ushort[<*>].comp_le(ushort[<*>] x, ushort[<*>] y) => $$veccomple(x, y);
macro bool[<*>] ushort[<*>].comp_eq(ushort[<*>] x, ushort[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] ushort[<*>].comp_gt(ushort[<*>] x, ushort[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] ushort[<*>].comp_ge(ushort[<*>] x, ushort[<*>] y) => $$veccompge(x, y);
macro bool[<*>] ushort[<*>].comp_ne(ushort[<*>] x, ushort[<*>] y) => $$veccompne(x, y);
macro ushort ushort[<*>].sum(ushort[<*>] x) => $$reduce_add(x);
macro ushort ushort[<*>].product(ushort[<*>] x) => $$reduce_mul(x);
macro ushort ushort[<*>].and(ushort[<*>] x) => $$reduce_and(x);
macro ushort ushort[<*>].or(ushort[<*>] x) => $$reduce_or(x);
macro ushort ushort[<*>].xor(ushort[<*>] x) => $$reduce_xor(x);
macro ushort ushort[<*>].max(ushort[<*>] x) => $$reduce_max(x);
macro ushort ushort[<*>].min(ushort[<*>] x) => $$reduce_min(x);
macro bool[<*>] uint[<*>].comp_lt(uint[<*>] x, uint[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] uint[<*>].comp_le(uint[<*>] x, uint[<*>] y) => $$veccomple(x, y);
macro bool[<*>] uint[<*>].comp_eq(uint[<*>] x, uint[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] uint[<*>].comp_gt(uint[<*>] x, uint[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] uint[<*>].comp_ge(uint[<*>] x, uint[<*>] y) => $$veccompge(x, y);
macro bool[<*>] uint[<*>].comp_ne(uint[<*>] x, uint[<*>] y) => $$veccompne(x, y);
macro uint uint[<*>].sum(uint[<*>] x) => $$reduce_add(x);
macro uint uint[<*>].product(uint[<*>] x) => $$reduce_mul(x);
macro uint uint[<*>].and(uint[<*>] x) => $$reduce_and(x);
macro uint uint[<*>].or(uint[<*>] x) => $$reduce_or(x);
macro uint uint[<*>].xor(uint[<*>] x) => $$reduce_xor(x);
macro uint uint[<*>].max(uint[<*>] x) => $$reduce_max(x);
macro uint uint[<*>].min(uint[<*>] x) => $$reduce_min(x);
macro bool[<*>] ulong[<*>].comp_lt(ulong[<*>] x, ulong[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ulong[<*>].comp_le(ulong[<*>] x, ulong[<*>] y) => $$veccomple(x, y);
macro bool[<*>] ulong[<*>].comp_eq(ulong[<*>] x, ulong[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] ulong[<*>].comp_gt(ulong[<*>] x, ulong[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] ulong[<*>].comp_ge(ulong[<*>] x, ulong[<*>] y) => $$veccompge(x, y);
macro bool[<*>] ulong[<*>].comp_ne(ulong[<*>] x, ulong[<*>] y) => $$veccompne(x, y);
macro ulong ulong[<*>].sum(ulong[<*>] x) => $$reduce_add(x);
macro ulong ulong[<*>].product(ulong[<*>] x) => $$reduce_mul(x);
macro ulong ulong[<*>].and(ulong[<*>] x) => $$reduce_and(x);
macro ulong ulong[<*>].or(ulong[<*>] x) => $$reduce_or(x);
macro ulong ulong[<*>].xor(ulong[<*>] x) => $$reduce_xor(x);
macro ulong ulong[<*>].max(ulong[<*>] x) => $$reduce_max(x);
macro ulong ulong[<*>].min(ulong[<*>] x) => $$reduce_min(x);
macro bool[<*>] uint128[<*>].comp_lt(uint128[<*>] x, uint128[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] uint128[<*>].comp_le(uint128[<*>] x, uint128[<*>] y) => $$veccomple(x, y);
macro bool[<*>] uint128[<*>].comp_eq(uint128[<*>] x, uint128[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] uint128[<*>].comp_gt(uint128[<*>] x, uint128[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] uint128[<*>].comp_ge(uint128[<*>] x, uint128[<*>] y) => $$veccompge(x, y);
macro bool[<*>] uint128[<*>].comp_ne(uint128[<*>] x, uint128[<*>] y) => $$veccompne(x, y);
macro uint128 uint128[<*>].sum(uint128[<*>] x) => $$reduce_add(x);
macro uint128 uint128[<*>].product(uint128[<*>] x) => $$reduce_mul(x);
macro uint128 uint128[<*>].and(uint128[<*>] x) => $$reduce_and(x);
macro uint128 uint128[<*>].or(uint128[<*>] x) => $$reduce_or(x);
macro uint128 uint128[<*>].xor(uint128[<*>] x) => $$reduce_xor(x);
macro uint128 uint128[<*>].max(uint128[<*>] x) => $$reduce_max(x);
macro uint128 uint128[<*>].min(uint128[<*>] x) => $$reduce_min(x);
macro char char.sat_add(char x, char y) => $$sat_add(x, y);
macro char char.sat_sub(char x, char y) => $$sat_sub(x, y);
macro char char.sat_mul(char x, char y) => $$sat_mul(x, y);
macro char char.sat_shl(char x, char y) => $$sat_shl(x, y);
macro ichar ichar.sat_add(ichar x, ichar y) => $$sat_add(x, y);
macro ichar ichar.sat_sub(ichar x, ichar y) => $$sat_sub(x, y);
macro ichar ichar.sat_mul(ichar x, ichar y) => $$sat_mul(x, y);
macro ichar ichar.sat_shl(ichar x, ichar y) => $$sat_shl(x, y);
macro ushort ushort.sat_add(ushort x, ushort y) => $$sat_add(x, y);
macro ushort ushort.sat_sub(ushort x, ushort y) => $$sat_sub(x, y);
macro ushort ushort.sat_mul(ushort x, ushort y) => $$sat_mul(x, y);
macro ushort ushort.sat_shl(ushort x, ushort y) => $$sat_shl(x, y);
macro short short.sat_add(short x, short y) => $$sat_add(x, y);
macro short short.sat_sub(short x, short y) => $$sat_sub(x, y);
macro short short.sat_mul(short x, short y) => $$sat_mul(x, y);
macro short short.sat_shl(short x, short y) => $$sat_shl(x, y);
macro uint uint.sat_add(uint x, uint y) => $$sat_add(x, y);
macro uint uint.sat_sub(uint x, uint y) => $$sat_sub(x, y);
macro uint uint.sat_mul(uint x, uint y) => $$sat_mul(x, y);
macro uint uint.sat_shl(uint x, uint y) => $$sat_shl(x, y);
macro int int.sat_add(int x, int y) => $$sat_add(x, y);
macro int int.sat_sub(int x, int y) => $$sat_sub(x, y);
macro int int.sat_mul(int x, int y) => $$sat_mul(x, y);
macro int int.sat_shl(int x, int y) => $$sat_shl(x, y);
macro ulong ulong.sat_add(ulong x, ulong y) => $$sat_add(x, y);
macro ulong ulong.sat_sub(ulong x, ulong y) => $$sat_sub(x, y);
macro ulong ulong.sat_mul(ulong x, ulong y) => $$sat_mul(x, y);
macro ulong ulong.sat_shl(ulong x, ulong y) => $$sat_shl(x, y);
macro long long.sat_add(long x, long y) => $$sat_add(x, y);
macro long long.sat_sub(long x, long y) => $$sat_sub(x, y);
macro long long.sat_mul(long x, long y) => $$sat_mul(x, y);
macro long long.sat_shl(long x, long y) => $$sat_shl(x, y);
macro uint128 uint128.sat_add(uint128 x, uint128 y) => $$sat_add(x, y);
macro uint128 uint128.sat_sub(uint128 x, uint128 y) => $$sat_sub(x, y);
macro uint128 uint128.sat_mul(uint128 x, uint128 y) => $$sat_mul(x, y);
macro uint128 uint128.sat_shl(uint128 x, uint128 y) => $$sat_shl(x, y);
macro int128 int128.sat_add(int128 x, int128 y) => $$sat_add(x, y);
macro int128 int128.sat_sub(int128 x, int128 y) => $$sat_sub(x, y);
macro int128 int128.sat_mul(int128 x, int128 y) => $$sat_mul(x, y);
macro int128 int128.sat_shl(int128 x, int128 y) => $$sat_shl(x, y);
/**
* @checked x & 1
*/
macro bool is_power_of_2(x)
{
return x != 0 && (x & (x - 1)) == 0;
}
macro next_power_of_2(x)
{
$typeof(x) y = 1;
while (y < x) y += y;
return y;
}
macro equals_vec(v1, v2) @private
{
var $elements = v1.len;
var abs_diff = math::abs(v1 - v2);
var abs_v1 = math::abs(v1);
var abs_v2 = math::abs(v2);
$typeof(abs_v2) eps = 1;
return abs_diff.comp_le(FLOAT_EPSILON * math::max(abs_v1, abs_v2, eps)).and();
}
macro uint double.high_word(double d) => (uint)(bitcast(d, ulong) >> 32);
macro uint double.low_word(double d) => (uint)bitcast(d, ulong);
macro uint float.word(float d) => bitcast(d, uint);
macro double scalbn(double x, int n) => _scalbn(x, n);
extern fn double _atan(double x) @extern("atan");
extern fn float _atanf(float x) @extern("atanf");
extern fn double _atan2(double, double) @extern("atan2");
extern fn float _atan2f(float, float) @extern("atan2f");
extern fn void _sincos(double, double*) @extern("sincos");
extern fn void _sincosf(float, float*) @extern("sincosf");
extern fn double _tan(double x) @extern("tan");
extern fn float _tanf(float x) @extern("tanf");
extern fn double _scalbn(double x, int n) @extern("scalbn");
extern fn double _acos(double x) @extern("acos");
extern fn double _asin(double x) @extern("asin");
extern fn double _acosh(double x) @extern("acosh");
extern fn double _asinh(double x) @extern("asinh");
extern fn double _atanh(double x) @extern("atanh");
extern fn float _acosf(float x) @extern("acosf");
extern fn float _asinf(float x) @extern("asinf");
extern fn float _acoshf(float x) @extern("acoshf");
extern fn float _asinhf(float x) @extern("asinhf");
extern fn float _atanhf(float x) @extern("atanhf");
fn double _frexp(double x, int* e)
{
ulong i = bitcast(x, ulong);
int ee = (int)((i >> 52) & 0x7ff);
switch
{
case !ee:
if (!x)
{
*e = 0;
return x;
}
x = _frexp(x * 0x1p64, e);
*e -= 64;
return x;
case ee == 0x7ff:
return x;
default:
*e = ee - 0x3fe;
i &= 0x800fffffffffffffu64;
i |= 0x3fe0000000000000u64;
return bitcast(i, double);
}
}
fn float _frexpf(float x, int* e)
{
uint i = bitcast(x, uint);
int ee = (i >> 23) & 0xff;
switch
{
case !ee:
if (!x)
{
*e = 0;
return x;
}
x = _frexpf(x * 0x1p64, e);
*e -= 64;
return x;
case ee == 0xff:
return x;
default:
*e = ee - 0x7e;
i &= 0x807fffffu32;
i |= 0x3f000000u32;
return bitcast(i, float);
}
}

View File

@@ -0,0 +1,24 @@
module std::math::complex<Real>;
union Complex
{
struct
{
Real r, c;
}
Real[<2>] v;
}
macro Complex identity() => { 1, 0 };
macro Complex Complex.add(Complex a, Complex b) => Complex { .v = a.v + b.v };
macro Complex Complex.add_each(Complex a, Real b) => Complex { .v = a.v + b };
macro Complex Complex.sub(Complex a, Complex b) => Complex { .v = a.v - b.v };
macro Complex Complex.sub_each(Complex a, Real b) => Complex { .v = a.v - b };
macro Complex Complex.scale(Complex a, Real s) => Complex { .v = a.v * s };
macro Complex Complex.mul(Complex a, Complex b) => { a.r * b.r - a.c * b.c, a.r * b.c + b.r * a.c };
macro Complex Complex.div(Complex a, Complex b)
{
Real div = b.v.dot(b.v);
return Complex{ (a.r * b.r + a.c * b.c) / div, (a.c * b.r - a.r * b.c) / div };
}

434
lib/std/math/math.matrix.c3 Normal file
View File

@@ -0,0 +1,434 @@
module std::math::matrix<Real>;
struct Matrix2x2
{
union
{
struct
{
Real m00, m01;
Real m10, m11;
}
Real[4] m;
}
}
struct Matrix3x3
{
union
{
struct
{
Real m00, m01, m02;
Real m10, m11, m12;
Real m20, m21, m22;
}
Real[9] m;
}
}
struct Matrix4x4
{
union
{
struct
{
Real m00, m01, m02, m03;
Real m10, m11, m12, m13;
Real m20, m21, m22, m23;
Real m30, m31, m32, m33;
}
Real[16] m;
}
}
fn Real[<2>] Matrix2x2.apply(Matrix2x2* mat, Real[<2>] vec)
{
return Real[<2>] {
mat.m00 * vec[0] + mat.m01 * vec[1],
mat.m10 * vec[0] + mat.m11 * vec[1],
};
}
fn Real[<3>] Matrix3x3.apply(Matrix3x3* mat, Real[<3>] vec)
{
return Real[<3>] {
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2],
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2],
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2],
};
}
fn Real[<4>] Matrix4x4.apply(Matrix4x4* mat, Real[<4>] vec)
{
return Real[<4>] {
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2] + mat.m03 * vec[3],
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2] + mat.m13 * vec[3],
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2] + mat.m23 * vec[3],
mat.m30 * vec[0] + mat.m31 * vec[1] + mat.m32 * vec[2] + mat.m33 * vec[3],
};
}
fn Matrix2x2 Matrix2x2.mul(Matrix2x2* a, Matrix2x2 b)
{
return Matrix2x2 {
a.m00 * b.m00 + a.m01 * b.m10, a.m00 * b.m01 + a.m01 * b.m11,
a.m10 * b.m00 + a.m11 * b.m10, a.m10 * b.m01 + a.m11 * b.m11,
};
}
fn Matrix3x3 Matrix3x3.mul(Matrix3x3* a, Matrix3x3 b)
{
return Matrix3x3 {
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20,
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21,
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22,
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20,
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21,
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22,
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20,
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21,
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22,
};
}
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* a, Matrix4x4 b)
{
return Matrix4x4 {
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30,
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31,
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32,
a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33,
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30,
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31,
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32,
a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33,
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30,
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31,
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32,
a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33,
a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30,
a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31,
a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32,
a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33,
};
}
fn Matrix2x2 Matrix2x2.component_mul(Matrix2x2* mat, Real s) => matrix_component_mul(mat, s);
fn Matrix3x3 Matrix3x3.component_mul(Matrix3x3* mat, Real s) => matrix_component_mul(mat, s);
fn Matrix4x4 Matrix4x4.component_mul(Matrix4x4* mat, Real s) => matrix_component_mul(mat, s);
fn Matrix2x2 Matrix2x2.add(Matrix2x2* mat, Matrix2x2 mat2) => matrix_add(mat, mat2);
fn Matrix3x3 Matrix3x3.add(Matrix3x3* mat, Matrix3x3 mat2) => matrix_add(mat, mat2);
fn Matrix4x4 Matrix4x4.add(Matrix4x4* mat, Matrix4x4 mat2) => matrix_add(mat, mat2);
fn Matrix2x2 Matrix2x2.sub(Matrix2x2* mat, Matrix2x2 mat2) => matrix_sub(mat, mat2);
fn Matrix3x3 Matrix3x3.sub(Matrix3x3* mat, Matrix3x3 mat2) => matrix_sub(mat, mat2);
fn Matrix4x4 Matrix4x4.sub(Matrix4x4* mat, Matrix4x4 mat2) => matrix_sub(mat, mat2);
fn Matrix2x2 Matrix2x2.transpose(Matrix2x2* mat)
{
return Matrix2x2 {
mat.m00, mat.m10,
mat.m01, mat.m11
};
}
fn Matrix3x3 Matrix3x3.transpose(Matrix3x3* mat)
{
return Matrix3x3 {
mat.m00, mat.m10, mat.m20,
mat.m01, mat.m11, mat.m21,
mat.m02, mat.m12, mat.m22,
};
}
fn Matrix4x4 Matrix4x4.transpose(Matrix4x4* mat)
{
return Matrix4x4 {
mat.m00, mat.m10, mat.m20, mat.m30,
mat.m01, mat.m11, mat.m21, mat.m31,
mat.m02, mat.m12, mat.m22, mat.m32,
mat.m03, mat.m13, mat.m23, mat.m33,
};
}
fn Real Matrix2x2.determinant(Matrix2x2* mat)
{
return mat.m00 * mat.m11 - mat.m01 * mat.m10;
}
fn Real Matrix3x3.determinant(Matrix3x3* mat)
{
return
mat.m00 * (mat.m11 * mat.m22 - mat.m21 * mat.m12) -
mat.m01 * (mat.m10 * mat.m22 - mat.m20 * mat.m12) +
mat.m02 * (mat.m10 * mat.m21 - mat.m20 * mat.m11);
}
fn Real Matrix4x4.determinant(Matrix4x4* mat)
{
return
mat.m00 * (mat.m11 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m12 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
mat.m13 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) ) -
mat.m01 * (mat.m10 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m12 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m13 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) ) +
mat.m02 * (mat.m10 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
mat.m11 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m13 * (mat.m20 * mat.m31 - mat.m30 * mat.m21) ) -
mat.m03 * (mat.m10 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
mat.m11 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
mat.m12 * (mat.m20 * mat.m31 - mat.m30 * mat.m21) );
}
fn Matrix2x2 Matrix2x2.adjoint(Matrix2x2* mat)
{
return Matrix2x2 { mat.m00, -mat.m01, -mat.m10, mat.m11 };
}
fn Matrix3x3 Matrix3x3.adjoint(Matrix3x3* mat)
{
return Matrix3x3 {
(mat.m11 * mat.m22 - mat.m21 * mat.m12),
-(mat.m10 * mat.m22 - mat.m20 * mat.m12),
(mat.m10 * mat.m21 - mat.m20 * mat.m11),
-(mat.m01 * mat.m22 - mat.m21 * mat.m02),
(mat.m00 * mat.m22 - mat.m20 * mat.m02),
-(mat.m00 * mat.m21 - mat.m20 * mat.m01),
(mat.m01 * mat.m12 - mat.m11 * mat.m02),
-(mat.m00 * mat.m12 - mat.m10 * mat.m02),
(mat.m00 * mat.m11 - mat.m10 * mat.m01),
};
}
fn Matrix4x4 Matrix4x4.adjoint(Matrix4x4* mat)
{
return Matrix4x4 {
(mat.m11 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m12 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
mat.m13 * (mat.m21 * mat.m32 - mat.m31 * mat.m22)),
-(mat.m10 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m12 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m13 * (mat.m20 * mat.m32 - mat.m30 * mat.m22)),
(mat.m10 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
mat.m11 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m13 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
-(mat.m10 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
mat.m11 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
mat.m12 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
-(mat.m01 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m02 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
mat.m03 * (mat.m21 * mat.m32 - mat.m31 * mat.m22)),
(mat.m00 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m02 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m03 * (mat.m20 * mat.m32 - mat.m30 * mat.m22)),
-(mat.m00 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
mat.m01 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m03 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
(mat.m00 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
mat.m01 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
mat.m02 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
(mat.m01 * (mat.m12 * mat.m33 - mat.m32 * mat.m13) -
mat.m02 * (mat.m11 * mat.m33 - mat.m31 * mat.m13) +
mat.m03 * (mat.m11 * mat.m32 - mat.m31 * mat.m12)),
-(mat.m00 * (mat.m12 * mat.m33 - mat.m32 * mat.m13) -
mat.m02 * (mat.m10 * mat.m33 - mat.m30 * mat.m13) +
mat.m03 * (mat.m10 * mat.m32 - mat.m30 * mat.m12)),
(mat.m00 * (mat.m11 * mat.m33 - mat.m31 * mat.m13) -
mat.m01 * (mat.m10 * mat.m33 - mat.m30 * mat.m13) +
mat.m03 * (mat.m10 * mat.m31 - mat.m30 * mat.m11)),
-(mat.m00 * (mat.m11 * mat.m32 - mat.m31 * mat.m12) -
mat.m01 * (mat.m10 * mat.m32 - mat.m30 * mat.m12) +
mat.m02 * (mat.m10 * mat.m31 - mat.m30 * mat.m11)),
-(mat.m01 * (mat.m12 * mat.m23 - mat.m22 * mat.m13) -
mat.m02 * (mat.m11 * mat.m23 - mat.m21 * mat.m13) +
mat.m03 * (mat.m11 * mat.m22 - mat.m21 * mat.m12)),
(mat.m00 * (mat.m12 * mat.m23 - mat.m22 * mat.m13) -
mat.m02 * (mat.m10 * mat.m23 - mat.m20 * mat.m13) +
mat.m03 * (mat.m10 * mat.m22 - mat.m20 * mat.m12)),
-(mat.m00 * (mat.m11 * mat.m23 - mat.m21 * mat.m13) -
mat.m01 * (mat.m10 * mat.m23 - mat.m20 * mat.m13) +
mat.m03 * (mat.m10 * mat.m21 - mat.m20 * mat.m11)),
(mat.m00 * (mat.m11 * mat.m22 - mat.m21 * mat.m12) -
mat.m01 * (mat.m10 * mat.m22 - mat.m20 * mat.m12) +
mat.m02 * (mat.m10 * mat.m21 - mat.m20 * mat.m11)),
};
}
fn Matrix2x2! Matrix2x2.inverse(Matrix2x2* m)
{
Real det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
Matrix2x2 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
fn Matrix3x3! Matrix3x3.inverse(Matrix3x3* m)
{
Real det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
Matrix3x3 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
fn Matrix4x4! Matrix4x4.inverse(Matrix4x4* m)
{
Real det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
Matrix4x4 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
fn Matrix3x3 Matrix3x3.translate(Matrix3x3* m, Real[<2>] v)
{
return m.mul(Matrix3x3 {
1, 0, v[0],
0, 1, v[1],
0, 0, 1,
});
}
fn Matrix4x4 Matrix4x4.translate(Matrix4x4* m, Real[<3>] v)
{
return m.mul(Matrix4x4 {
1, 0, 0, v[0],
0, 1, 0, v[1],
0, 0, 1, v[2],
0, 0, 0, 1,
});
}
// r in radians
fn Matrix3x3 Matrix3x3.rotate(Matrix3x3* m, Real r)
{
return m.mul(Matrix3x3 {
math::cos(r), -math::sin(r), 0,
math::sin(r), math::cos(r), 0,
0, 0, 1,
});
}
// r in radians
fn Matrix4x4 Matrix4x4.rotate_z(Matrix4x4* m, Real r)
{
return m.mul(Matrix4x4 {
math::cos(r), -math::sin(r), 0, 0,
math::sin(r), math::cos(r), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
});
}
// r in radians
fn Matrix4x4 Matrix4x4.rotate_y(Matrix4x4* m, Real r)
{
return m.mul(Matrix4x4 {
math::cos(r), 0, -math::sin(r), 0,
0, 1, 0, 0,
math::sin(r), 0, math::cos(r), 0,
0, 0, 0, 1,
});
}
// r in radians
fn Matrix4x4 Matrix4x4.rotate_x(Matrix4x4* m, Real r)
{
return m.mul(Matrix4x4 {
1, 0, 0, 0,
0, math::cos(r), -math::sin(r), 0,
0, math::sin(r), math::cos(r), 0,
0, 0, 0, 1,
});
}
fn Matrix3x3 Matrix3x3.scale(Matrix3x3* m, Real[<2>] v)
{
return m.mul(Matrix3x3 {
v[0], 0, 0,
0, v[1], 0,
0, 0, 1,
});
}
fn Real Matrix2x2.trace(Matrix2x2* m) => m.m00 + m.m11;
fn Real Matrix3x3.trace(Matrix3x3* m) => m.m00 + m.m11 + m.m22;
fn Real Matrix4x4.trace(Matrix4x4* m) => m.m00 + m.m11 + m.m22 + m.m33;
fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, Real[<3>] v)
{
return m.mul(Matrix4x4 {
v[0], 0, 0, 0,
0, v[1], 0, 0,
0, 0, v[2], 0,
0, 0, 0, 1,
});
}
fn Matrix4x4 ortho(Real left, Real right, Real top, Real bottom, Real near, Real far)
{
Real width = right - left;
Real height = top - bottom;
Real depth = far - near;
return Matrix4x4 {
2 / width, 0, 0, 0,
0, 2 / height, 0, 0,
0, 0, -2 / depth, 0,
-(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1
};
}
// fov in radians
fn Matrix4x4 perspective(Real fov, Real aspect_ratio, Real near, Real far)
{
Real top = (math::sin(fov / 2) / math::cos(fov / 2)) * near;
Real right = top * aspect_ratio;
Real depth = far - near;
return Matrix4x4 {
1 / right, 0, 0, 0,
0, 1 / top, 0, 0,
0, 0, -2 / depth, 0,
0, 0, -(far + near) / depth, 1,
};
}
const Matrix2x2 IDENTITY2 = { .m = { [0] = 1, [3] = 1 } };
const Matrix3x3 IDENTITY3 = { .m = { [0] = 1, [4] = 1, [8] = 1 } };
const Matrix4x4 IDENTITY4 = { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } };
macro matrix_component_mul(mat, val) @private
{
var $Type = Real[<$typeof(mat.m).len>];
return $typeof(*mat) { .m = val * ($Type)mat.m };
}
macro matrix_add(mat, mat2) @private
{
var $Type = Real[<$typeof(mat.m).len>];
return $typeof(*mat) { .m = ($Type)mat.m + ($Type)mat2.m };
}
macro matrix_sub(mat, mat2) @private
{
var $Type = Real[<$typeof(mat.m).len>];
return $typeof(*mat) { .m = ($Type)mat.m - ($Type)mat2.m };
}

119
lib/std/math/math.random.c3 Normal file
View File

@@ -0,0 +1,119 @@
module std::math;
struct Random
{
RandomInterface fns;
void* state;
}
def RandomSeedFn = fn void(Random*, char[] seed);
def RandomNextBytesFn = fn void(Random*, char[] buffer);
def RandomNextFn = fn uint(Random*, int bits);
struct RandomInterface
{
RandomSeedFn seed_fn;
RandomNextBytesFn next_bytes_fn;
RandomNextFn next_fn;
}
/**
* @param [&inout] random
* @param [inout] buffer
**/
fn void Random.next_bytes(Random* random, char[] buffer)
{
if (!buffer.len) return;
if (RandomNextBytesFn func = random.fns.next_bytes_fn)
{
func(random, buffer);
return;
}
if (RandomNextFn func = random.fns.next_fn)
{
usz current = 0;
while (current < buffer.len)
{
char[4] res = bitcast(func(random, 32), char[4]);
foreach (c : res)
{
buffer[current++] = c;
if (current == buffer.len) return;
}
}
return;
}
unreachable("Invalid Random type.");
}
/**
* @param [&inout] random
* @require bits >= 0 && bits <= 32
**/
fn uint Random.next(Random* random, int bits)
{
if (bits == 0) return 0;
if (RandomNextFn func = random.fns.next_fn) return func(random, bits);
int bytes = (bits + 7) / 8;
char[4] buffer;
char[] b = buffer[:bytes];
assert(random.fns.next_bytes_fn, "Invalid Random");
random.fns.next_bytes_fn(random, b) @inline;
uint next = 0;
foreach (char c : b)
{
next = next << 8 + c;
}
if (bits < 32) next >>= bytes * 8 - bits;
return next;
}
/**
* @param [&inout] random
**/
fn ulong Random.next_long(Random* random)
{
char[8] buffer;
random.next_bytes(&buffer);
return bitcast(buffer, ulong);
}
/**
* @param [&inout] random
**/
fn void Random.set_seed(Random* random, long seed)
{
random.fns.seed_fn(random, &&bitcast(seed, char[8])) @inline;
}
/**
* @param [&inout] random
* @param [in] seed
**/
fn void Random.set_seeds(Random* random, char[] seed)
{
random.fns.seed_fn(random, seed);
}
/**
* @param [&inout] random
**/
fn bool Random.next_bool(Random* random)
{
return random.next(1) != 0;
}
fn float Random.next_float(Random* r)
{
return r.next(24) / (float)(1 << 24);
}
fn double Random.next_double(Random* r)
{
return (((long)(r.next(26)) << 27) + r.next(27)) * 0x1.0p-53;
}
fn uint Random.next_int(Random* r)
{
return r.next(32) @inline;
}

View File

@@ -0,0 +1,13 @@
module std::math;
fn float __roundevenf(float f) @extern("roundevenf") @weak @nostrip
{
// Slow implementation
return round(f / 2) * 2;
}
fn double __roundeven(double d) @extern("roundeven") @weak @nostrip
{
// Slow implementation
return round(d / 2) * 2;
}

View File

@@ -0,0 +1,201 @@
module std::math::easing;
/* Easing equations ported from Robert Penner's equations */
/*
*
* TERMS OF USE - EASING EQUATIONS
*
* Open source under the BSD License.
*
* Copyright © 2001 Robert Penner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Linear Easing functions
fn float linear_none(float t, float b, float c, float d) @inline => c * t / d + b;
fn float linear_in(float t, float b, float c, float d) @inline => c * t / d + b;
fn float linear_out(float t, float b, float c, float d) @inline => c * t / d + b;
fn float linear_inout(float t, float b, float c, float d) @inline => c * t / d + b;
// Sine Easing functions
fn float sine_in(float t, float b, float c, float d) @inline => -c * math::cos(t / d * (float)math::PI_2) + c + b;
fn float sine_out(float t, float b, float c, float d) @inline => c * math::sin(t / d * (float)math::PI_2) + b;
fn float sine_inout(float t, float b, float c, float d) @inline => (-c / 2) * (math::cos((float)math::PI * t / d) - 1) + b;
// Circular Easing functions
fn float circ_in(float t, float b, float c, float d) @inline => -c * (math::sqrt(1 - sq(t / d)) - 1) + b;
fn float circ_out(float t, float b, float c, float d) @inline => c * math::sqrt(1 - sq(t / d - 1)) + b;
fn float circ_inout(float t, float b, float c, float d) @inline
{
t /= d / 2;
return t < 1
? (-c / 2) * (math::sqrt(1 - sq(t)) - 1) + b
: (c / 2) * (math::sqrt(1 - sq(t - 2)) + 1) + b;
}
// Cubic Easing functions
fn float cubic_in(float t, float b, float c, float d) @inline => c * cube(t / d) + b;
fn float cubic_out(float t, float b, float c, float d) @inline => c * (cube(t / d - 1) + 1) + b;
fn float cubic_inout(float t, float b, float c, float d) @inline
{
t /= d / 2;
return t < 1
? (c / 2) * cube(t) + b
: c / 2 * (cube(t - 2) + 2) + b;
}
// Quadratic Easing functions
fn float quad_in(float t, float b, float c, float d) @inline => c * sq(t / d) + b;
fn float quad_out(float t, float b, float c, float d) @inline
{
t /= d;
return -c * t * (t - 2) + b;
}
fn float quad_inout(float t, float b, float c, float d) @inline
{
t /= d / 2;
return t < 1
? (c / 2) * sq(t) + b
: (-c / 2) * ((t - 1) * (t - 3) - 1) + b;
}
// Exponential Easing functions
fn float expo_in(float t, float b, float c, float d) @inline => t ? b : c * math::pow(2.0f, 10 * (t / d - 1)) + b;
fn float expo_out(float t, float b, float c, float d) @inline
{
return (t == d) ? b + c : c * (-math::pow(2.0f, -10 * t / d) + 1) + b;
}
fn float expo_inout(float t, float b, float c, float d) @inline // Ease: Exponential In Out
{
if (t == 0) return b;
if (t == d) return b + c;
t /= d / 2;
return t < 1
? (c / 2) * math::pow(2.0f, 10 * (t - 1)) + b
: (c / 2) * (-math::pow(2.0f, -10 * (t - 1)) + 2) + b;
}
// Back Easing functions
fn float back_in(float t, float b, float c, float d, float s = 1.70158f) @inline
{
t /= d;
return c * sq(t) * ((s + 1) * t - s) + b;
}
fn float back_out(float t, float b, float c, float d, float s = 1.70158f) @inline
{
t = t / d - 1;
return c * (sq(t) * ((s + 1) * t + s) + 1) + b;
}
fn float back_inout(float t, float b, float c, float d, float s = 1.70158f) @inline
{
s *= 1.525f;
t /= d / 2;
if (t < 1)
{
return (c / 2) * sq(t) * ((s + 1) * t - s) + b;
}
t -= 2.0f;
return (c / 2) * (sq(t) * ((s + 1) * t + s) + 2) + b;
}
// Bounce Easing functions
fn float bounce_out(float t, float b, float c, float d) @inline
{
t /= d;
switch
{
case t < 1 / 2.75f:
return c * 7.5625f * sq(t) + b;
case t < 2 / 2.75f:
t -= 1.5f / 2.75f;
return c * (7.5625f * sq(t) + 0.75f) + b;
case t < 2.5f / 2.75f:
t -= 2.25f / 2.75f;
return c * (7.5625f * sq(t) + 0.9375f) + b;
default:
t -= 2.625f / 2.75f;
return c * (7.5625f * sq(t) + 0.984375f) + b;
}
}
fn float bounce_in(float t, float b, float c, float d) @inline => c - bounce_out(d - t, 0, c, d) + b;
fn float bounce_inout(float t, float b, float c, float d) @inline
{
return t < d / 2
? bounce_in(t * 2, 0, c, d) * 0.5f + b
: bounce_out(t * 2 - d, 0, c, d) * 0.5f + b;
}
// Elastic Easing functions
fn float elastic_in(float t, float b, float c, float d) @inline
{
if (t == 0) return b;
t /= d;
if (t == 1) return b + c;
float p = d * 0.3f;
float a = c;
float s = p / 4;
t -= 1;
return -a * math::pow(2.0f, 10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) + b;
}
fn float elastic_out(float t, float b, float c, float d) @inline
{
if (t == 0) return b;
t /= d;
if (t == 1) return b + c;
float p = d * 0.3f;
float a = c;
float s = p / 4;
return a * math::pow(2.0f, -10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) + c + b;
}
fn float elastic_inout(float t, float b, float c, float d) @inline
{
if (t == 0) return b;
t /= d / 2;
if (t == 2) return b + c;
float p = d * (0.3f * 1.5f);
float a = c;
float s = p / 4;
t -= 1;
return t < 0
? -0.5f * a * math::pow(2.0f, 10 * t) * math::sin((t * d - s) * (2 * (float)math::PI)/p) + b
: a * math::pow(2.0f, -10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) * 0.5f + c + b;
}
macro sq(x) @private => x * x;
macro cube(x) @private => x * x * x;

381
lib/std/math/math_i128.c3 Normal file
View File

@@ -0,0 +1,381 @@
module std::math;
fn int128 __divti3(int128 a, int128 b) @extern("__divti3") @weak @nostrip
{
int128 sign_a = a >> 127; // -1 : 0
int128 sign_b = b >> 127; // -1 : 0
uint128 unsigned_a = (uint128)(a ^ sign_a) + (-sign_a);
uint128 unsigned_b = (uint128)(b ^ sign_b) + (-sign_b);
sign_a ^= sign_b; // quotient sign
return __udivti3(unsigned_a, unsigned_b) @inline ^ sign_a + (-sign_a);
}
fn uint128 __umodti3(uint128 n, uint128 d) @extern("__umodti3") @weak @nostrip
{
// Ignore d = 0
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
// If n < d then sr is wrapping.
// which means we can just return n.
if (sr > 127) return n;
// If d == 1 and n = MAX
if (sr == 127) return 0;
sr++;
uint128 r = n >> sr;
// Follow known algorithm:
n <<= 128 - sr;
for (uint128 carry = 0; sr > 0; sr--)
{
r = (r << 1) | (n >> 127);
n = (n << 1) | carry;
int128 sign = (int128)(d - r - 1) >> 127;
carry = sign & 1;
r -= d & sign;
}
return r;
}
fn uint128 __udivti3(uint128 n, uint128 d) @extern("__udivti3") @weak @nostrip
{
// Ignore d = 0
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
// If n < d then sr is wrapping.
// which means we can just return 0.
if (sr > 127) return 0;
// If d == 1 and n = MAX
if (sr == 127) return n;
sr++;
uint128 r = n >> sr;
// Follow known algorithm:
n <<= 128 - sr;
uint128 carry = 0;
for (; sr > 0; sr--)
{
r = (r << 1) | (n >> 127);
n = (n << 1) | carry;
int128 sign = (int128)(d - r - 1) >> 127;
carry = sign & 1;
r -= d & sign;
}
n = (n << 1) | carry;
return n;
}
fn int128 __modti3(int128 a, int128 b) @extern("__modti3") @weak @nostrip
{
int128 sign = b >> 127;
uint128 unsigned_b = (uint128)(b ^ sign) + (-sign);
sign = a >> 127;
uint128 unsigned_a = (uint128)(a ^ sign) + (-sign);
return __umodti3(unsigned_a, unsigned_b) ^ sign + (-sign);
}
union Int128bits @private
{
struct
{
ulong ulow, uhigh;
}
struct
{
long ilow, ihigh;
}
uint128 all;
}
fn uint128 __lshrti3(uint128 a, uint b) @extern("__lshrti3") @weak @nostrip
{
Int128bits result;
result.all = a;
if (b >= 64)
{
result.ulow = result.uhigh >> (b - 64);
result.uhigh = 0;
}
else
{
if (b == 0) return a;
result.ulow = (result.uhigh << (64 - b)) | (result.ulow >> b);
result.uhigh = result.uhigh >> b;
}
return result.all;
}
fn int128 __ashrti3(int128 a, uint b) @extern("__ashrti3") @weak @nostrip
{
Int128bits result;
result.all = a;
if (b >= 64)
{
result.ilow = result.ihigh >> (b - 64);
result.ihigh = result.ihigh >> 63;
}
else
{
if (b == 0) return a;
result.ilow = result.ihigh << (64 - b) | (result.ilow >> b);
result.ihigh = result.ihigh >> b;
}
return result.all;
}
fn int128 __ashlti3(int128 a, uint b) @extern("__ashlti3") @weak @nostrip
{
Int128bits result;
result.all = a;
if (b >= 64)
{
result.ulow = 0;
result.uhigh = result.ulow << (b - 64);
}
else
{
if (b == 0) return a;
result.uhigh = (result.uhigh << b) | (result.ulow >> (64 - b));
result.ulow = result.ulow << b;
}
return result.all;
}
// Returns: a * b
fn int128 __mulddi3(ulong a, ulong b) @private
{
Int128bits r;
const ulong LOWER_MASK = 0xffff_ffff;
r.ulow = (a & LOWER_MASK) * (b & LOWER_MASK);
ulong t = r.ulow >> 32;
r.ulow &= LOWER_MASK;
t += (a >> 32) * (b & LOWER_MASK);
r.ulow += (t & LOWER_MASK) << 32;
r.uhigh = t >> 32;
t = r.ulow >> 32;
r.ulow &= LOWER_MASK;
t += (b >> 32) * (a & LOWER_MASK);
r.ulow += (t & LOWER_MASK) << 32;
r.uhigh += t >> 32;
r.uhigh += (a >> 32) * (b >> 32);
return r.all;
}
fn int128 __multi3(int128 a, int128 b) @extern("__multi3") @weak @nostrip
{
Int128bits x = { .all = a };
Int128bits y = { .all = b };
Int128bits r = { .all = __mulddi3(x.ulow, y.ulow) };
r.uhigh += x.uhigh * y.ulow + x.ulow * y.uhigh;
return r.all;
}
fn float __floattisf(int128 a) @extern("__floattisf") @weak @nostrip => float_from_i128(float, a);
fn double __floattidf(int128 a) @extern("__floattidf") @weak @nostrip => float_from_i128(double, a);
fn float __floatuntisf(uint128 a) @extern("__floatuntisf") @weak @nostrip => float_from_u128(float, a);
fn double __floatuntidf(uint128 a) @extern("__floatuntidf") @weak @nostrip => float_from_u128(double, a);
fn uint128 __fixunsdfti(double a) @weak @extern("__fixunsdfti") @nostrip => fixuint(a);
fn uint128 __fixunssfti(float a) @weak @extern("__fixunssfti") @nostrip => fixuint(a);
fn int128 __fixdfti(double a) @weak @extern("__fixdfti") @nostrip => fixint(a);
fn int128 __fixsfti(float a) @weak @extern("__fixsfti") @nostrip => fixint(a);
macro float_from_i128($Type, a) @private
{
var $Rep;
$switch ($Type)
$case double:
$Rep = ulong;
const MANT_DIG = DOUBLE_MANT_DIG;
const SIGNIFICANT_BITS = 52;
const EXP_BIAS = 1023;
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
const SIGN_BIT = 1u64 << 63;
$case float:
$Rep = uint;
const MANT_DIG = FLOAT_MANT_DIG;
const EXP_BIAS = 127;
const SIGNIFICANT_BITS = 23;
const MANTISSA_MASK = 0x7F_FFFFu32;
const SIGN_BIT = 1u32 << 31;
$case float16:
$Rep = ushort;
const MANT_DIG = HALF_MANT_DIG;
$case float128:
$Rep = uint128;
const MANT_DIG = QUAD_MANT_DIG;
$endswitch
if (a == 0) return ($Type)0;
// Grab and remove sign.
int128 sign = a >> 127;
a = (a ^ sign) - sign;
int sd = 128 - (int)$$clz(a); // digits
int e = sd - 1; // exponent
if (sd > MANT_DIG)
{
switch (sd)
{
case MANT_DIG + 1:
a <<= 1;
case MANT_DIG + 2:
break;
default:
a = (a >> (sd - (MANT_DIG + 2)))
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
}
a |= (uint128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << MANT_DIG))
{
a >>= 1;
e++;
}
}
else
{
a <<= (MANT_DIG - sd);
}
return bitcast((($Rep)sign & SIGN_BIT) | ((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
}
macro float_from_u128($Type, a) @private
{
var $Rep;
$switch ($Type)
$case double:
$Rep = ulong;
const MANT_DIG = DOUBLE_MANT_DIG;
const SIGNIFICANT_BITS = 52;
const EXP_BIAS = 1023;
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
$case float:
$Rep = uint;
const MANT_DIG = FLOAT_MANT_DIG;
const EXP_BIAS = 127;
const SIGNIFICANT_BITS = 23;
const MANTISSA_MASK = 0x7F_FFFFu32;
$case float16:
$Rep = ushort;
const MANT_DIG = HALF_MANT_DIG;
$case float128:
$Rep = uint128;
const MANT_DIG = QUAD_MANT_DIG;
$endswitch
if (a == 0) return ($Type)0;
int sd = 128 - (int)$$clz(a); // digits
int e = sd - 1; // exponent
if (sd > MANT_DIG)
{
switch (sd)
{
case MANT_DIG + 1:
a <<= 1;
case MANT_DIG + 2:
break;
default:
a = (a >> (sd - (MANT_DIG + 2)))
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
}
a |= (uint128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << MANT_DIG))
{
a >>= 1;
e++;
}
}
else
{
a <<= (MANT_DIG - sd);
}
return bitcast(((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
}
macro fixuint(a) @private
{
var $Rep;
$switch ($typeof(a))
$case double:
$Rep = ulong;
const EXPONENT_BITS = 11;
const SIGNIFICANT_BITS = 52;
$case float:
$Rep = uint;
const EXPONENT_BITS = 8;
const SIGNIFICANT_BITS = 23;
$case float16:
$Rep = ushort;
const EXPONENT_BITS = 5;
const SIGNIFICANT_BITS = 10;
$case float128:
$Rep = uint128;
const EXPONENT_BITS = 15;
const SIGNIFICANT_BITS = 112;
$endswitch
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
const $Rep ONE_REP =EXPONENT_BIAS << SIGNIFICANT_BITS;
const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS);
const $Rep ABS_MASK = SIGN_BIT - 1u;
const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS;
const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u;
const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK;
const $Rep QUIET_BIT = IMPLICIT_BIT >> 1;
const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT;
const $Rep INF_REP = EXPONENT_MASK;
$Rep rep = bitcast(a, $Rep);
$Rep abs = rep & ABS_MASK;
int sign = rep & SIGN_BIT ? -1 : 1;
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
if (sign == -1 || exponent < 0) return 0u128;
if ((uint)exponent >= uint128.sizeof * 8) return ~0u128;
if (exponent < SIGNIFICANT_BITS) return (uint128)significand >> (SIGNIFICANT_BITS - exponent);
return (uint128)significand << (exponent - SIGNIFICANT_BITS);
}
macro fixint(a) @private
{
var $Rep;
$switch ($typeof(a))
$case double:
$Rep = ulong;
const EXPONENT_BITS = 11;
const SIGNIFICANT_BITS = 52;
$case float:
$Rep = uint;
const EXPONENT_BITS = 8;
const SIGNIFICANT_BITS = 23;
$case float16:
$Rep = ushort;
const EXPONENT_BITS = 5;
const SIGNIFICANT_BITS = 10;
$case float128:
$Rep = uint128;
const EXPONENT_BITS = 15;
const SIGNIFICANT_BITS = 112;
$endswitch
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
const $Rep ONE_REP = EXPONENT_BIAS << SIGNIFICANT_BITS;
const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS);
const $Rep ABS_MASK = SIGN_BIT - 1u;
const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS;
const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u;
const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK;
const $Rep QUIET_BIT = IMPLICIT_BIT >> 1;
const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT;
const $Rep INF_REP = EXPONENT_MASK;
$Rep rep = bitcast(a, $Rep);
$Rep abs = rep & ABS_MASK;
int sign = rep & SIGN_BIT ? -1 : 1;
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
if (exponent < 0) return 0;
if ((uint)exponent >= uint128.sizeof * 8) return sign == 1 ? int128.max : int128.min;
if (exponent < SIGNIFICANT_BITS) return sign * ((int128)significand >> (SIGNIFICANT_BITS - exponent));
return sign * ((int128)significand << (exponent - SIGNIFICANT_BITS));
}

47
lib/std/math/math_libc.c3 Normal file
View File

@@ -0,0 +1,47 @@
/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
/* atan(x)
* Method
* 1. Reduce x to positive by atan(x) = -atan(-x).
* 2. According to the integer k=4t+0.25 chopped, t=x, the argument
* is further reduced to one of the following intervals and the
* arctangent of t is evaluated by the corresponding formula:
*
* [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...)
* [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) )
* [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) )
* [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) )
* [39/16,INF] atan(x) = atan(INF) + atan( -1/t )
*
* Constants:
* The hexadecimal values are the intended ones for the following
* constants. The decimal values may be used, provided that the
* compiler will convert from decimal to binary accurately enough
* to produce the hexadecimal values shown.
*/
/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
*/
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
module std::math;

View File

@@ -0,0 +1,34 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
fn double __cos(double x, double y) @extern("__cos") @weak @nostrip
{
const C1 = 4.16666666666666019037e-02; /* 0x3FA55555, 0x5555554C */
const C2 = -1.38888888888741095749e-03; /* 0xBF56C16C, 0x16C15177 */
const C3 = 2.48015872894767294178e-05; /* 0x3EFA01A0, 0x19CB1590 */
const C4 = -2.75573143513906633035e-07; /* 0xBE927E4F, 0x809C52AD */
const C5 = 2.08757232129817482790e-09; /* 0x3E21EE9E, 0xBDB4B1C4 */
const C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */
double z = x * x;
double w = z * z;
double r = z * (C1 + z * (C2 + z * C3)) + w * w * (C4 + z * (C5 + z * C6));
double hz = 0.5 * z;
w = 1.0 - hz;
return w + (((1.0 - w) - hz) + (z * r - x * y));
}
$endif

View File

@@ -0,0 +1,36 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
* Debugged and optimized by Bruce D. Evans.
*/
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
/* |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]). */
const double C0 @private = -0x1ffffffd0c5e81.0p-54; /* -0.499999997251031003120 */
const double C1 @private = 0x155553e1053a42.0p-57; /* 0.0416666233237390631894 */
const double C2 @private = -0x16c087e80f1e27.0p-62; /* -0.00138867637746099294692 */
const double C3 @private = 0x199342e0ee5069.0p-68; /* 0.0000243904487962774090654 */
fn float __cosdf(double x) @extern("__cosdf") @weak @nostrip
{
/* Try to optimize for parallel evaluation as in __tandf.c. */
double z = x * x;
double w = z * z;
double r = C2 + z * C3;
return (float)(((1.0f + z * C0) + w * C1) + (w * z) * r);
}
$endif

View File

@@ -0,0 +1,35 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
fn double __sin(double x, double y, int iy) @extern("__sin") @weak @nostrip
{
const S1 = -1.66666666666666324348e-01; /* 0xBFC55555, 0x55555549 */
const S2 = 8.33333333332248946124e-03; /* 0x3F811111, 0x1110F8A6 */
const S3 = -1.98412698298579493134e-04; /* 0xBF2A01A0, 0x19C161D5 */
const S4 = 2.75573137070700676789e-06; /* 0x3EC71DE3, 0x57B1FE7D */
const S5 = -2.50507602534068634195e-08; /* 0xBE5AE5E6, 0x8A2B9CEB */
const S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */
double z = x * x;
double w = z * z;
double r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6);
double v = z * x;
return iy == 0
? x + v * (S1 + z * r)
: x - ((z * (0.5 * y - v * r) - y) - v * S1);
}
$endif

View File

@@ -0,0 +1,34 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
* Optimized by Bruce D. Evans.
*/
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
// |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]).
fn float __sindf(double x) @extern("__sindf") @weak @nostrip
{
const S1F = -0x15555554cbac77.0p-55; /* -0.166666666416265235595 */
const S2F = 0x111110896efbb2.0p-59; /* 0.0083333293858894631756 */
const S3F = -0x1a00f9e2cae774.0p-65; /* -0.000198393348360966317347 */
const S4F = 0x16cd878c3b46a7.0p-71; /* 0.0000027183114939898219064 */
double z = x * x;
double w = z * z;
double r = S3F + z * S4F;
double s = z * x;
return (float)((x + s * (S1F + z * S2F)) + s * w * r);
}
$endif

View File

@@ -0,0 +1,83 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */
/*
* ====================================================
* Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
const double[*] TAN_T = {
3.33333333333334091986e-01, /* 3FD55555, 55555563 */
1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */
5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */
2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */
8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */
3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */
1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */
5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */
2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */
7.81794442939557092300e-05, /* 3F147E88, A03792A6 */
7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */
-1.85586374855275456654e-05, /* BEF375CB, DB605373 */
2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */
};
fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
{
const double PIO4 = 7.85398163397448278999e-01; /* 3FE921FB, 54442D18 */
const double PIO4LO = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */
uint hx = (uint)(bitcast(x, ulong) >> 32);
bool big = (hx &0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744
int sign @noinit;
if (big)
{
sign = hx >> 31;
if (sign)
{
x = -x;
y = -y;
}
x = (PIO4 - x) + (PIO4LO - y);
y = 0.0;
}
double z = x * x;
double w = z * z;
/*
* Break x^5*(T[1]+x^2*T[2]+...) into
* x^5(T[1]+x^4*T[3]+...+x^20*T[11]) +
* x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))
*/
double r = TAN_T[1] + w * (TAN_T[3] + w * (TAN_T[5] + w * (TAN_T[7] + w * (TAN_T[9] + w * TAN_T[11]))));
double v = z * (TAN_T[2] + w * (TAN_T[4] + w * (TAN_T[6] + w * (TAN_T[8] + w * (TAN_T[10] + w * TAN_T[12])))));
double s = z * x;
r = y + z * (s * (r + v) + y) + s * TAN_T[0];
w = x + r;
if (big)
{
s = 1 - 2 * odd;
v = s - 2.0 * (x + (r - w*w/(w + s)));
return sign ? -v : v;
}
if (!odd) return w;
// -1.0/(x+r) has up to 2ulp error, so compute it accurately
// Clear low word
ulong d = bitcast(w, ulong) & 0xFFFF_FFFF_0000_0000;
double w0 = bitcast(d, double);
v = r - (w0 - x); // w0+v = r+x
double a = -1.0 / w;
d = bitcast(a, ulong) & 0xFFFF_FFFF_0000_0000;
double a0 = bitcast(d, double);
return a0 + a * (1.0 + a0 * w0 + a0 * v);
}
$endif

View File

@@ -0,0 +1,56 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_tanf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
* Optimized by Bruce D. Evans.
*/
/*
* ====================================================
* Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
// |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]).
const double[*] TANDF = {
0x15554d3418c99f.0p-54, /* 0.333331395030791399758 */
0x1112fd38999f72.0p-55, /* 0.133392002712976742718 */
0x1b54c91d865afe.0p-57, /* 0.0533812378445670393523 */
0x191df3908c33ce.0p-58, /* 0.0245283181166547278873 */
0x185dadfcecf44e.0p-61, /* 0.00297435743359967304927 */
0x1362b9bf971bcd.0p-59, /* 0.00946564784943673166728 */
};
fn float __tandf(double x, int odd) @extern("__tandf") @weak @nostrip
{
double z = x * x;
/*
* Split up the polynomial into small independent terms to give
* opportunities for parallel evaluation. The chosen splitting is
* micro-optimized for Athlons (XP, X64). It costs 2 multiplications
* relative to Horner's method on sequential machines.
*
* We add the small terms from lowest degree up for efficiency on
* non-sequential machines (the lowest degree terms tend to be ready
* earlier). Apart from this, we don't care about order of
* operations, and don't need to to care since we have precision to
* spare. However, the chosen splitting is good for accuracy too,
* and would give results as accurate as Horner's method if the
* small terms were added from highest degree down.
*/
double r = TANDF[4] + z * TANDF[5];
double t = TANDF[2] + z * TANDF[3];
double w = z * z;
double s = z * x;
double u = TANDF[0] + z * TANDF[1];
r = (x + s * u) + (s * w) * (t + w * r);
return (float)(odd ? -1.0 / r : r);
}
$endif

View File

@@ -0,0 +1,324 @@
module std::math::nolibc::atan;
$if !env::COMPILER_LIBC_AVAILABLE:
const double[*] ATANHI @private = {
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */
9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */
1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */
};
const double[*] ATANLO @private = {
2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */
3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */
1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */
6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */
};
const double[*] AT @private = {
3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */
-1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */
1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */
-1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */
9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */
-7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */
6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */
-5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */
4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */
-3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */
1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
};
fn double _atan(double x) @weak @extern("atan") @nostrip
{
int id @noinit;
uint ix = x.high_word();
uint sign = ix >> 31;
ix &= 0x7fffffff;
switch
{
case ix >= 0x44100000:
/* if |x| >= 2^66 */
if (math::is_nan(x)) return x;
double z = ATANHI[3] + 0x1p-120f;
return sign ? -z : z;
case ix < 0x3fdc0000:
/* |x| < 0.4375 */
if (ix < 0x3e400000)
{
/* |x| < 2^-27 */
if (ix < 0x00100000)
{
/* raise underflow for subnormal x */
(float)@volatile_load(x);
}
return x;
}
id = -1;
case ix < 0x3ff30000:
/* |x| < 1.1875 */
x = math::abs(x);
if (ix < 0x3fe60000)
{
/* 7/16 <= |x| < 11/16 */
id = 0;
x = (2 * x - 1) / (2 + x);
}
else
{ /* 11/16 <= |x| < 19/16 */
id = 1;
x = (x - 1) / (x + 1);
}
case ix < 0x40038000:
x = math::abs(x);
/* |x| < 2.4375 */
id = 2;
x = (x - 1.5) / (1 + 1.5 * x);
default:
/* 2.4375 <= |x| < 2^66 */
id = 3;
x = -1 / math::abs(x);
}
/* end of argument reduction */
double z = x * x;
double w = z * z;
/* break sum from i=0 to 10 AT[i]z**(i+1) into odd and even poly */
double s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10])))));
double s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9]))));
if (id < 0) return x - x * (s1 + s2);
z = ATANHI[id] - (x * (s1 + s2) - ATANLO[id] - x);
return sign ? -z : z;
}
const float[*] ATANHIF @private = {
4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */
1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */
};
const float[*] ATANLOF @private = {
5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */
3.7748947079e-08, /* atan(1.0)lo 0x33222168 */
3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */
7.5497894159e-08, /* atan(inf)lo 0x33a22168 */
};
const float[*] ATF @private = {
3.3333328366e-01,
-1.9999158382e-01,
1.4253635705e-01,
-1.0648017377e-01,
6.1687607318e-02,
};
fn float _atanf(float x) @weak @extern("atanf") @nostrip
{
int id @noinit;
uint ix = x.word();
uint sign = ix >> 31;
ix &= 0x7fffffff;
if (ix >= 0x4c800000)
{
/* if |x| >= 2**26 */
if (math::is_nan(x)) return x;
float z = ATANHIF[3] + 0x1p-120f;
return sign ? -z : z;
}
switch
{
case ix < 0x3ee00000:
/* |x| < 0.4375 */
if (ix < 0x39800000)
{
/* |x| < 2**-12 */
if (ix < 0x00800000)
{
/* raise underflow for subnormal x */
float f = @volatile_load(x);
f = f * f;
}
return x;
}
id = -1;
case ix < 0x3f980000:
/* |x| < 1.1875 */
x = math::abs(x);
if (ix < 0x3f300000)
{
/* 7/16 <= |x| < 11/16 */
id = 0;
x = (2.0f * x - 1.0f) / (2.0f + x);
break;
}
/* 11/16 <= |x| < 19/16 */
id = 1;
x = (x - 1.0f) / (x + 1.0f);
case ix < 0x401c0000:
x = math::abs(x);
/* |x| < 2.4375 */
id = 2;
x = (x - 1.5f) / (1.0f + 1.5f * x);
default:
/* 2.4375 <= |x| < 2**26 */
x = math::abs(x);
id = 3;
x = -1.0f / x;
}
/* end of argument reduction */
float z = x * x;
float w = z * z;
/* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
float s1 = z * (ATF[0] + w * (ATF[2] + w * ATF[4]));
float s2 = w * (ATF[1] + w * ATF[3]);
if (id < 0) return x - x * (s1 + s2) * 10000;
z = ATANHIF[id] - ((x * (s1 + s2) - ATANLOF[id]) - x);
return sign ? -z : z;
}
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
macro void extract_words(double d, uint* hi, uint* lo) @private
{
ulong rep = bitcast(d, ulong);
*hi = (uint)(rep >> 32);
*lo = (uint)rep;
}
fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
{
if (math::is_nan(x) || math::is_nan(y)) return x + y;
uint lx @noinit;
uint ix @noinit;
extract_words(x, &ix, &lx);
uint ly @noinit;
uint iy @noinit;
extract_words(y, &iy, &ly);
// x = 1.0
if ((ix - 0x3ff00000) | lx == 0) return _atan(y);
// 2*sign(x) + sign(y)
uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2);
ix = ix & 0x7fffffff;
iy = iy & 0x7fffffff;
// when y = 0
if (iy | ly == 0)
{
switch (m)
{
case 0:
case 1: return y; /* atan(+-0,+anything)=+-0 */
case 2: return math::PI; /* atan(+0,-anything) = pi */
case 3: return -math::PI; /* atan(-0,-anything) =-pi */
}
}
// when x = 0 */
if (ix | lx == 0) return m & 1 ? -math::PI_2 : math::PI_2;
/* when x is INF */
if (ix == 0x7ff00000)
{
if (iy == 0x7ff00000)
{
switch (m)
{
case 0: return math::PI_4; /* atan(+INF,+INF) */
case 1: return -math::PI_4; /* atan(-INF,+INF) */
case 2: return math::PI_4 + math::PI_2; /* atan(+INF,-INF) */
case 3: return - (math::PI_4 + math::PI_2); /* atan(-INF,-INF) */
}
unreachable();
}
switch (m)
{
case 0: return 0.0; /* atan(+...,+INF) */
case 1: return -0.0; /* atan(-...,+INF) */
case 2: return math::PI; /* atan(+...,-INF) */
case 3: return -math::PI; /* atan(-...,-INF) */
}
unreachable();
}
/* |y/x| > 0x1p64 */
if (ix + (64 << 20) < iy || iy == 0x7ff00000) return m & 1 ? -math::PI_2 : math::PI_2;
/* z = atan(|y/x|) without spurious underflow */
double z = ((m & 2) && iy + (64 << 20) < ix) ? 0 : _atan(math::abs(y/x));
switch (m)
{
case 0: return z; /* atan(+,+) */
case 1: return -z; /* atan(-,+) */
case 2: return math::PI - (z - PI_LO); /* atan(+,-) */
default: return (z - PI_LO) - math::PI; /* atan(-,-) */
}
}
const float PI_F @private = 3.1415927410e+00; /* 0x40490fdb */
const float PI_LO_F @private = -8.7422776573e-08; /* 0xb3bbbd2e */
fn float _atan2f(float y, float x) @weak @extern("atan2f") @nostrip
{
if (math::is_nan(x) || math::is_nan(y)) return x + y;
uint ix = bitcast(x, uint);
uint iy = bitcast(y, uint);
/* x=1.0 */
if (ix == 0x3f800000) return _atanf(y);
/* 2*sign(x)+sign(y) */
uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2);
ix &= 0x7fffffff;
iy &= 0x7fffffff;
/* when y = 0 */
if (iy == 0)
{
switch (m)
{
case 0:
case 1: return y; /* atan(+-0,+anything)=+-0 */
case 2: return PI_F; /* atan(+0,-anything) = pi */
case 3: return -PI_F; /* atan(-0,-anything) =-pi */
}
unreachable();
}
/* when x = 0 */
if (ix == 0) return m & 1 ? -(float)math::PI_2 : (float)math::PI_2;
/* when x is INF */
if (ix == 0x7f800000)
{
if (iy == 0x7f800000)
{
switch (m)
{
case 0: return (float)math::PI_4; /* atan(+INF,+INF) */
case 1: return (float)-math::PI_4; /* atan(-INF,+INF) */
case 2: return (float)(math::PI_4 + math::PI_2); /*atan(+INF,-INF)*/
case 3: return -(float)(math::PI_4 + math::PI_2); /*atan(-INF,-INF)*/
}
unreachable();
}
switch (m)
{
case 0: return 0.0f; /* atan(+...,+INF) */
case 1: return -0.0f; /* atan(-...,+INF) */
case 2: return PI_F; /* atan(+...,-INF) */
case 3: return -PI_F; /* atan(-...,-INF) */
}
unreachable();
}
/* |y/x| > 0x1p26 */
if (ix + 26 << 23 < iy || iy == 0x7f800000) return m & 1 ? -(float)math::PI_2 : (float)math::PI_2;
/* z = atan(|y/x|) with correct underflow */
/*|y/x| < 0x1p-26, x < 0 */
float z = (m & 2 && iy + 26 << 23 < ix) ? 0.0f : _atanf(math::abs(y / x));
switch (m)
{
case 0: return z; /* atan(+,+) */
case 1: return -z; /* atan(-,+) */
case 2: return PI_F - (z - PI_LO_F); /* atan(+,-) */
default: return (z - PI_LO_F) - PI_F; /* atan(-,-) */
}
}
$endif

View File

@@ -0,0 +1,45 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
fn double _ceil(double x) @weak @extern("ceil") @nostrip
{
ulong ui = bitcast(x, ulong);
int e = (int)((ui >> 52) & 0x7ff);
if (e >= 0x3ff + 52 || x == 0) return x;
// y = int(x) - x, where int(x) is an integer neighbor of x
double y = ui >> 63 ? (x - TOINT) + TOINT - x : (x + TOINT) - TOINT - x;
// special case because of non-nearest rounding modes
if (e <= 0x3ff - 1)
{
force_eval_add(y, 0);
return ui >> 63 ? -0.0 : 1;
}
return y < 0 ? x + y + 1 : x + y;
}
fn float _ceilf(float x) @weak @extern("ceilf") @nostrip
{
uint u = bitcast(x, uint);
int e = (int)((u >> 23) & 0xff) - 0x7f;
switch
{
case e >= 23:
return x;
case e >= 0:
uint m = 0x007fffff >> e;
if (u & m == 0) return x;
force_eval_add(x, 0x1p120f);
if (u >> 31 == 0) u += m;
u &= ~m;
case u >> 31 != 0:
force_eval_add(x, 0x1p120f);
return -0.0f;
case u << 1 != 0:
return 1;
}
return bitcast(u, float);
}
$endif

View File

@@ -0,0 +1,91 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
fn float _cosf(float x) @extern("cosf") @weak @nostrip
{
uint ix = bitcast(x, uint);
uint sign = ix >> 31;
ix &= 0x7fffffff;
switch
{
case (ix <= 0x3f490fda):
// |x| < 2**-12
if (ix < 0x39800000)
{
/* raise inexact if x != 0 */
force_eval_add(x, 0x1p120f);
return 1.0f;
}
return __cosdf(x);
// |x| ~<= 5*pi/4
case (ix <= 0x407b53d1):
// |x| ~> 3*pi/4
if (ix > 0x4016cbe3) return -__cosdf(sign ? x + S2PI2 : x - S2PI2);
return sign ? __sindf(x + S1PI2) : __sindf(S1PI2 - x);
// |x| ~<= 9*pi/4
case (ix <= 0x40e231d5):
if (ix > 0x40afeddf) return __cosdf(sign ? x + S4PI2 : x - S4PI2);
return sign ? __sindf((double)(-x) - S3PI2) : __sindf(x - S3PI2);
}
// general argument reduction needed
double y @noinit;
switch (__rem_pio2f(x, &y) & 3)
{
case 0: return __cosdf(y);
case 1: return __sindf(-y);
case 2: return -__cosdf(y);
default: return __sindf(y);
}
unreachable();
}
/* origin: FreeBSD /usr/src/lib/msun/src/s_cos.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
fn double _cos(double x) @extern("cos") @weak @nostrip
{
// High word of x.
uint ix = (uint)(bitcast(x, ulong) >> 32);
ix &= 0x7fffffff;
switch
{
// |x| ~< pi/4
case ix <= 0x3fe921fb:
if (ix < 0x3e46a09e)
{
// |x| < 2**-27 * sqrt(2)
// raise inexact if x!=0
force_eval_add(x, 0x1p120f);
return 1.0;
}
return __cos(x, 0);
case ix >= 0x7ff00000:
// cos(Inf or NaN) is NaN
return x - x;
default:
// argument reduction
double[2] y @noinit;
switch (__rem_pio2(x, &y) & 3)
{
case 0: return __cos(y[0], y[1]);
case 1: return -__sin(y[0], y[1], 1);
case 2: return -__cos(y[0], y[1]);
default: return __sin(y[0], y[1], 1);
}
}
}
$endif

View File

@@ -0,0 +1,144 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
macro uint _top12f(float x) @private => bitcast(x, uint) >> 20;
fn float _exp2f(float x) @extern("exp2f") @weak @nostrip
{
double xd = x;
uint abstop = _top12f(x) & 0x7ff;
if (abstop >= _top12f(128.0f)) /* @unlikely */
{
switch
{
// |x| >= 128 or x is nan.
case bitcast(x, uint) == bitcast(-float.inf, uint):
return 0;
case abstop >= _top12f(float.inf):
return x + x;
case x > 0:
return __math_oflowf(0);
case x <= -150.0f:
return __math_uflowf(0);
}
}
const SHIFT = __EXP2F_DATA.shift_scaled;
const uint N = 1U << EXP2F_TABLE_BITS;
// x = k/N + r with r in [-1/(2N), 1/(2N)] and int k.
double kd = xd + SHIFT;
ulong ki = bitcast(kd, ulong);
kd -= SHIFT; /* k/N for int k. */
double r = xd - kd;
// exp2(x) = 2^(k/N) * 2^r ~= s * (C0*r^3 + C1*r^2 + C2*r + 1)
ulong t = __EXP2F_DATA.tab[ki % N];
t += ki << (52 - EXP2F_TABLE_BITS);
double s = bitcast(t, double);
double z = __EXP2F_DATA.poly[0] * r + __EXP2F_DATA.poly[1];
double r2 = r * r;
double y = __EXP2F_DATA.poly[2] * r + 1;
y = z * r2 + y;
y = y * s;
return (float)y;
}
fn double _exp2_specialcase(double tmp, ulong sbits, ulong ki) @private
{
if (ki & 0x80000000 == 0)
{
// k > 0, the exponent of scale might have overflowed by 1.
sbits -= 1u64 << 52;
double scale = bitcast(sbits, double);
double y = 2 * (scale + scale * tmp);
return y;
}
// k < 0, need special care in the subnormal range.
sbits += 1022u64 << 52;
double scale = bitcast(sbits, double);
double y = scale + scale * tmp;
if (y >= 1.0)
{
return 0x1p-1022 * y;
}
// Round y to the right precision before scaling it into the subnormal
// range to avoid double rounding that can cause 0.5+E/2 ulp error where
// E is the worst-case ulp error outside the subnormal range. So this
// is only useful if the goal is better than 1 ulp worst-case error.
double lo = scale - y + scale * tmp;
double hi = 1.0 + y;
lo = 1.0 - hi + y + lo;
y = hi + lo - 1.0;
/* Avoid -0.0 with downward rounding. */
if (WANT_ROUNDING && y == 0.0) y = 0.0;
/* The underflow exception needs to be signaled explicitly. */
return __math_xflow(0, 0x1p-1022);
}
macro uint _top12d(double x) @private
{
return (uint)(bitcast(x, ulong) >> 52);
}
fn double _exp2(double x) @extern("exp2") @weak @nostrip
{
uint abstop = _top12d(x) & 0x7ff;
ulong u = bitcast(x, ulong);
if (abstop - _top12d(0x1p-54) >= _top12d(512.0) - _top12d(0x1p-54)) /* @unlikely */
{
if (abstop - _top12d(0x1p-54) >= 0x80000000)
{
// Avoid spurious underflow for tiny x. */
// Note: 0 is common input. */
return WANT_ROUNDING ? 1.0 + x : 1.0;
}
if (abstop >= _top12d(1024.0))
{
switch
{
case u == bitcast(-double.inf, ulong):
return 0.0;
case abstop >= _top12d(double.inf):
return 1.0 + x;
case !(u >> 63):
return __math_oflow(0);
case u >= bitcast(-1075.0, ulong):
return __math_uflow(0);
}
}
// Large x is special cased below.
if (2 * u > 2 * bitcast(928.0, ulong)) abstop = 0;
}
const SHIFT = __EXP2_DATA.exp2_shift;
// exp2(x) = 2^(k/N) * 2^r, with 2^r in [2^(-1/2N),2^(1/2N)]. */
// x = k/N + r, with int k and r in [-1/2N, 1/2N]. */
double kd = x + SHIFT;
ulong ki = bitcast(kd, ulong); /* k. */
kd -= SHIFT; /* k/N for int k. */
double r = x - kd;
/* 2^(k/N) ~= scale * (1 + tail). */
ulong idx = 2 * (ki % EXP_DATA_WIDTH);
ulong top = ki << (52 - EXP_TABLE_BITS);
double tail = __EXP2_DATA.tab[idx];
/* This is only a valid scale when -1023*N < k < 1024*N. */
ulong sbits = __EXP2_DATA.tab[idx + 1] + top;
/* exp2(x) = 2^(k/N) * 2^r ~= scale + scale * (tail + 2^r - 1). */
/* Evaluation is optimized assuming superscalar pipelined execution. */
double r2 = r * r;
const C1 = __EXP2_DATA.exp2_poly[0];
const C2 = __EXP2_DATA.exp2_poly[1];
const C3 = __EXP2_DATA.exp2_poly[2];
const C4 = __EXP2_DATA.exp2_poly[3];
const C5 = __EXP2_DATA.exp2_poly[4];
/* Without fma the worst case error is 0.5/N ulp larger. */
/* Worst case error is less than 0.5+0.86/N+(abs poly error * 2^53) ulp. */
double tmp = tail + r * C1 + r2 * (C2 + r * C3) + r2 * r2 * (C4 + r * C5);
if (abstop == 0 /* @unlikely */) return _exp2_specialcase(tmp, sbits, ki);
double scale = bitcast(sbits, double);
/* Note: tmp == 0 or |tmp| > 2^-65 and scale > 2^-928, so there
is no spurious underflow here even without fma. */
return scale + scale * tmp;
}
$endif

View File

@@ -0,0 +1,45 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
fn double _floor(double x) @weak @extern("floor") @nostrip
{
ulong ui = bitcast(x, ulong);
int e = (int)((ui >> 52) & 0x7ff);
if (e >= 0x3ff + 52 || x == 0) return x;
// y = int(x) - x, where int(x) is an integer neighbor of x
double y = ui >> 63 ? (x - TOINT) + TOINT - x : (x + TOINT) - TOINT - x;
// special case because of non-nearest rounding modes
if (e <= 0x3ff - 1)
{
force_eval_add(y, 0);
return ui >> 63 ? -1 : 0;
}
return y > 0 ? x + y - 1 : x + y;
}
fn float _floorf(float x) @weak @extern("floorf") @nostrip
{
uint u = bitcast(x, uint);
int e = (int)((u >> 23) & 0xff) - 0x7f;
switch
{
case e >= 23:
return x;
case e >= 0:
uint m = 0x007fffff >> e;
if (u & m == 0) return x;
force_eval_add(x, 0x1p120f);
if (u >> 31) u += m;
u &= ~m;
case u >> 31 == 0:
force_eval_add(x, 0x1p120f);
u = 0;
case (u << 1 != 0):
return -1;
}
return bitcast(u, float);
}
$endif

View File

@@ -0,0 +1,256 @@
module std::math::nolibc;
const double TOINT = 1 / math::DOUBLE_EPSILON;
const double TOINT15 = 1.5 / math::DOUBLE_EPSILON;
const float TOINTF = (float)(1 / math::FLOAT_EPSILON);
const double S1PI2 @private = math::PI_2; /* 0x3FF921FB, 0x54442D18 */
const double S2PI2 @private = math::PI; /* 0x400921FB, 0x54442D18 */
const double S3PI2 @private = math::PI + math::PI_2; /* 0x4012D97C, 0x7F3321D2 */
const double S4PI2 @private = math::PI + math::PI; /* 0x401921FB, 0x54442D18 */
// Shared between expf, exp2f and powf.
const EXP2F_TABLE_BITS @private = 5;
const EXP2F_POLY_ORDER @private = 3;
struct Exp2fData @private
{
ulong[1 << EXP2F_TABLE_BITS] tab;
double shift_scaled;
double[EXP2F_POLY_ORDER] poly;
double shift;
double invln2_scaled;
double[EXP2F_POLY_ORDER] poly_scaled;
}
const Exp2fData __EXP2F_DATA @private = {
.tab = {
0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f, 0x3fef9301d0125b51,
0x3fef72b83c7d517b, 0x3fef54873168b9aa, 0x3fef387a6e756238, 0x3fef1e9df51fdee1,
0x3fef06fe0a31b715, 0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d,
0x3feebfdad5362a27, 0x3feeb42b569d4f82, 0x3feeab07dd485429, 0x3feea47eb03a5585,
0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74, 0x3feea11473eb0187, 0x3feea589994cce13,
0x3feeace5422aa0db, 0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d,
0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c, 0x3fef3720dcef9069,
0x3fef5818dcfba487, 0x3fef7c97337b9b5f, 0x3fefa4afa2a490da, 0x3fefd0765b6e4540,
},
.shift_scaled = 0x1.8p+52 / (1 << 5),
.poly = {
0x1.c6af84b912394p-5, 0x1.ebfce50fac4f3p-3, 0x1.62e42ff0c52d6p-1,
},
.shift = 0x1.8p+52,
.invln2_scaled = 0x1.71547652b82fep+0 * (1 << 5),
.poly_scaled = {
0x1.c6af84b912394p-5 / (1 << 5) / (1 << 5) / (1 << 5),
0x1.ebfce50fac4f3p-3 / (1 << 5) / (1 << 5),
0x1.62e42ff0c52d6p-1 / (1 << 5),
},
};
const EXP_TABLE_BITS = 7;
const EXP_POLY_ORDER = 5;
const EXP2_POLY_ORDER = 5;
const EXP_DATA_WIDTH = 1 << EXP_TABLE_BITS;
struct Exp2Data @private
{
double invln2N;
double shift;
double negln2hiN;
double negln2loN;
double[4] poly; // Last four coefficients.
double exp2_shift;
double[EXP2_POLY_ORDER] exp2_poly;
ulong[2 * EXP_DATA_WIDTH] tab;
}
/*
* Shared data between exp, exp2 and pow.
*
* Copyright (c) 2018, Arm Limited.
* SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
*/
const Exp2Data __EXP2_DATA = {
// N/ln2
.invln2N = 0x1.71547652b82fep0 * EXP_DATA_WIDTH,
// -ln2/N
.negln2hiN = -0x1.62e42fefa0000p-8,
.negln2loN = -0x1.cf79abc9e3b3ap-47,
.shift = 0x1.8p52,
// exp polynomial coefficients.
.poly = {
// abs error: 1.555*2^-66
// ulp error: 0.509 (0.511 without fma)
// if |x| < ln2/256+eps
// abs error if |x| < ln2/256+0x1p-15: 1.09*2^-65
// abs error if |x| < ln2/128: 1.7145*2^-56
0x1.ffffffffffdbdp-2,
0x1.555555555543cp-3,
0x1.55555cf172b91p-5,
0x1.1111167a4d017p-7,
},
.exp2_shift = 0x1.8p52 / EXP_DATA_WIDTH,
// exp2 polynomial coefficients.
.exp2_poly = {
// abs error: 1.2195*2^-65
// ulp error: 0.507 (0.511 without fma)
// if |x| < 1/256
// abs error if |x| < 1/128: 1.9941*2^-56
0x1.62e42fefa39efp-1,
0x1.ebfbdff82c424p-3,
0x1.c6b08d70cf4b5p-5,
0x1.3b2abd24650ccp-7,
0x1.5d7e09b4e3a84p-10,
},
// 2^(k/N) ~= H[k]*(1 + T[k]) for int k in [0,N)
// tab[2*k] = asuint64(T[k])
// tab[2*k+1] = asuint64(H[k]) - (k << 52)/N
.tab = {
0x0, 0x3ff0000000000000,
0x3c9b3b4f1a88bf6e, 0x3feff63da9fb3335,
0xbc7160139cd8dc5d, 0x3fefec9a3e778061,
0xbc905e7a108766d1, 0x3fefe315e86e7f85,
0x3c8cd2523567f613, 0x3fefd9b0d3158574,
0xbc8bce8023f98efa, 0x3fefd06b29ddf6de,
0x3c60f74e61e6c861, 0x3fefc74518759bc8,
0x3c90a3e45b33d399, 0x3fefbe3ecac6f383,
0x3c979aa65d837b6d, 0x3fefb5586cf9890f,
0x3c8eb51a92fdeffc, 0x3fefac922b7247f7,
0x3c3ebe3d702f9cd1, 0x3fefa3ec32d3d1a2,
0xbc6a033489906e0b, 0x3fef9b66affed31b,
0xbc9556522a2fbd0e, 0x3fef9301d0125b51,
0xbc5080ef8c4eea55, 0x3fef8abdc06c31cc,
0xbc91c923b9d5f416, 0x3fef829aaea92de0,
0x3c80d3e3e95c55af, 0x3fef7a98c8a58e51,
0xbc801b15eaa59348, 0x3fef72b83c7d517b,
0xbc8f1ff055de323d, 0x3fef6af9388c8dea,
0x3c8b898c3f1353bf, 0x3fef635beb6fcb75,
0xbc96d99c7611eb26, 0x3fef5be084045cd4,
0x3c9aecf73e3a2f60, 0x3fef54873168b9aa,
0xbc8fe782cb86389d, 0x3fef4d5022fcd91d,
0x3c8a6f4144a6c38d, 0x3fef463b88628cd6,
0x3c807a05b0e4047d, 0x3fef3f49917ddc96,
0x3c968efde3a8a894, 0x3fef387a6e756238,
0x3c875e18f274487d, 0x3fef31ce4fb2a63f,
0x3c80472b981fe7f2, 0x3fef2b4565e27cdd,
0xbc96b87b3f71085e, 0x3fef24dfe1f56381,
0x3c82f7e16d09ab31, 0x3fef1e9df51fdee1,
0xbc3d219b1a6fbffa, 0x3fef187fd0dad990,
0x3c8b3782720c0ab4, 0x3fef1285a6e4030b,
0x3c6e149289cecb8f, 0x3fef0cafa93e2f56,
0x3c834d754db0abb6, 0x3fef06fe0a31b715,
0x3c864201e2ac744c, 0x3fef0170fc4cd831,
0x3c8fdd395dd3f84a, 0x3feefc08b26416ff,
0xbc86a3803b8e5b04, 0x3feef6c55f929ff1,
0xbc924aedcc4b5068, 0x3feef1a7373aa9cb,
0xbc9907f81b512d8e, 0x3feeecae6d05d866,
0xbc71d1e83e9436d2, 0x3feee7db34e59ff7,
0xbc991919b3ce1b15, 0x3feee32dc313a8e5,
0x3c859f48a72a4c6d, 0x3feedea64c123422,
0xbc9312607a28698a, 0x3feeda4504ac801c,
0xbc58a78f4817895b, 0x3feed60a21f72e2a,
0xbc7c2c9b67499a1b, 0x3feed1f5d950a897,
0x3c4363ed60c2ac11, 0x3feece086061892d,
0x3c9666093b0664ef, 0x3feeca41ed1d0057,
0x3c6ecce1daa10379, 0x3feec6a2b5c13cd0,
0x3c93ff8e3f0f1230, 0x3feec32af0d7d3de,
0x3c7690cebb7aafb0, 0x3feebfdad5362a27,
0x3c931dbdeb54e077, 0x3feebcb299fddd0d,
0xbc8f94340071a38e, 0x3feeb9b2769d2ca7,
0xbc87deccdc93a349, 0x3feeb6daa2cf6642,
0xbc78dec6bd0f385f, 0x3feeb42b569d4f82,
0xbc861246ec7b5cf6, 0x3feeb1a4ca5d920f,
0x3c93350518fdd78e, 0x3feeaf4736b527da,
0x3c7b98b72f8a9b05, 0x3feead12d497c7fd,
0x3c9063e1e21c5409, 0x3feeab07dd485429,
0x3c34c7855019c6ea, 0x3feea9268a5946b7,
0x3c9432e62b64c035, 0x3feea76f15ad2148,
0xbc8ce44a6199769f, 0x3feea5e1b976dc09,
0xbc8c33c53bef4da8, 0x3feea47eb03a5585,
0xbc845378892be9ae, 0x3feea34634ccc320,
0xbc93cedd78565858, 0x3feea23882552225,
0x3c5710aa807e1964, 0x3feea155d44ca973,
0xbc93b3efbf5e2228, 0x3feea09e667f3bcd,
0xbc6a12ad8734b982, 0x3feea012750bdabf,
0xbc6367efb86da9ee, 0x3fee9fb23c651a2f,
0xbc80dc3d54e08851, 0x3fee9f7df9519484,
0xbc781f647e5a3ecf, 0x3fee9f75e8ec5f74,
0xbc86ee4ac08b7db0, 0x3fee9f9a48a58174,
0xbc8619321e55e68a, 0x3fee9feb564267c9,
0x3c909ccb5e09d4d3, 0x3feea0694fde5d3f,
0xbc7b32dcb94da51d, 0x3feea11473eb0187,
0x3c94ecfd5467c06b, 0x3feea1ed0130c132,
0x3c65ebe1abd66c55, 0x3feea2f336cf4e62,
0xbc88a1c52fb3cf42, 0x3feea427543e1a12,
0xbc9369b6f13b3734, 0x3feea589994cce13,
0xbc805e843a19ff1e, 0x3feea71a4623c7ad,
0xbc94d450d872576e, 0x3feea8d99b4492ed,
0x3c90ad675b0e8a00, 0x3feeaac7d98a6699,
0x3c8db72fc1f0eab4, 0x3feeace5422aa0db,
0xbc65b6609cc5e7ff, 0x3feeaf3216b5448c,
0x3c7bf68359f35f44, 0x3feeb1ae99157736,
0xbc93091fa71e3d83, 0x3feeb45b0b91ffc6,
0xbc5da9b88b6c1e29, 0x3feeb737b0cdc5e5,
0xbc6c23f97c90b959, 0x3feeba44cbc8520f,
0xbc92434322f4f9aa, 0x3feebd829fde4e50,
0xbc85ca6cd7668e4b, 0x3feec0f170ca07ba,
0x3c71affc2b91ce27, 0x3feec49182a3f090,
0x3c6dd235e10a73bb, 0x3feec86319e32323,
0xbc87c50422622263, 0x3feecc667b5de565,
0x3c8b1c86e3e231d5, 0x3feed09bec4a2d33,
0xbc91bbd1d3bcbb15, 0x3feed503b23e255d,
0x3c90cc319cee31d2, 0x3feed99e1330b358,
0x3c8469846e735ab3, 0x3feede6b5579fdbf,
0xbc82dfcd978e9db4, 0x3feee36bbfd3f37a,
0x3c8c1a7792cb3387, 0x3feee89f995ad3ad,
0xbc907b8f4ad1d9fa, 0x3feeee07298db666,
0xbc55c3d956dcaeba, 0x3feef3a2b84f15fb,
0xbc90a40e3da6f640, 0x3feef9728de5593a,
0xbc68d6f438ad9334, 0x3feeff76f2fb5e47,
0xbc91eee26b588a35, 0x3fef05b030a1064a,
0x3c74ffd70a5fddcd, 0x3fef0c1e904bc1d2,
0xbc91bdfbfa9298ac, 0x3fef12c25bd71e09,
0x3c736eae30af0cb3, 0x3fef199bdd85529c,
0x3c8ee3325c9ffd94, 0x3fef20ab5fffd07a,
0x3c84e08fd10959ac, 0x3fef27f12e57d14b,
0x3c63cdaf384e1a67, 0x3fef2f6d9406e7b5,
0x3c676b2c6c921968, 0x3fef3720dcef9069,
0xbc808a1883ccb5d2, 0x3fef3f0b555dc3fa,
0xbc8fad5d3ffffa6f, 0x3fef472d4a07897c,
0xbc900dae3875a949, 0x3fef4f87080d89f2,
0x3c74a385a63d07a7, 0x3fef5818dcfba487,
0xbc82919e2040220f, 0x3fef60e316c98398,
0x3c8e5a50d5c192ac, 0x3fef69e603db3285,
0x3c843a59ac016b4b, 0x3fef7321f301b460,
0xbc82d52107b43e1f, 0x3fef7c97337b9b5f,
0xbc892ab93b470dc9, 0x3fef864614f5a129,
0x3c74b604603a88d3, 0x3fef902ee78b3ff6,
0x3c83c5ec519d7271, 0x3fef9a51fbc74c83,
0xbc8ff7128fd391f0, 0x3fefa4afa2a490da,
0xbc8dae98e223747d, 0x3fefaf482d8e67f1,
0x3c8ec3bc41aa2008, 0x3fefba1bee615a27,
0x3c842b94c3a9eb32, 0x3fefc52b376bba97,
0x3c8a64a931d185ee, 0x3fefd0765b6e4540,
0xbc8e37bae43be3ed, 0x3fefdbfdad9cbe14,
0x3c77893b4d91cd9d, 0x3fefe7c1819e90d8,
0x3c5305c14160cc89, 0x3feff3c22b8f71f1,
}
};
const bool WANT_ROUNDING = true;
macro float __math_uflowf(uint sign) => __math_xflow(sign, 0x1p97f);
macro double __math_uflow(ulong sign) => __math_xflow(sign, 0x1p769);
macro float __math_oflowf(uint sign) => __math_xflow(sign, 0x1p-95f);
macro double __math_oflow(ulong sign) => __math_xflow(sign, 0x1p-767);
macro __math_xflow(sign, v)
{
$typeof(v) temp;
@volatile_store(temp, (sign ? -v : v) * v);
return temp;
}
macro force_eval_add(x, v)
{
$typeof(x) temp;
@volatile_store(temp, x + v);
}

View File

@@ -0,0 +1,15 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
fn float powf_broken(float x, float f) @extern("powf") @weak @nostrip
{
unreachable("'powf' not supported");
}
fn double pow_broken(double x, double y) @extern("pow") @weak @nostrip
{
unreachable("'pow' not supported");
}
$endif

Some files were not shown because too many files have changed in this diff Show More