Compare commits

..

2165 Commits

Author SHA1 Message Date
Christoffer Lerno
dd8e280835 Release 0.6.8 2025-03-01 18:47:08 +01:00
Christoffer Lerno
34c2d8ce77 Fix regression with scripts. 2025-03-01 17:24:38 +01:00
Christoffer Lerno
28b9ff8016 Fixed some additional errors due to the BuildParseContext refactoring. 2025-03-01 12:48:22 +01:00
Christoffer Lerno
76f226f536 Fixed error and poor error message when using an invalid target name. 2025-03-01 12:30:36 +01:00
Christoffer Lerno
1b0ac13d76 Deprecation of operator(@construct) 2025-02-28 10:39:08 +01:00
Christoffer Lerno
f134b8b67a Swizzling an inline vector in a struct would cause a crash. 2025-02-27 21:49:20 +01:00
Christoffer Lerno
33b05bcfeb More deprecations in lib6, and updates to lib7 2025-02-27 11:10:41 +01:00
Christoffer Lerno
6d3c1f5d2f Fix final? issues with -o. 2025-02-26 02:51:42 +01:00
Christoffer Lerno
96943ca66f Check exe and lib output so -o works with directories. Removed construct forms from Maybe. 2025-02-26 02:35:28 +01:00
Christoffer Lerno
374d73af12 Cleanup. 2025-02-26 01:49:19 +01:00
Adversing
81397f0726 Add --print-env option to c3c (#1880)
* Add `--build-env` for build environment information.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-02-26 01:48:03 +01:00
Aleksandr Vedeneev
0c33b78a2f Test runner args #1967 (#1988)
* harmonized testrun arguments with c3c --test-* naming
added --test-quiet option
added --test-noleak to disable tracking allocator mem leak detection
added --test-nocapture - tests can print out everything as they run
* Move changes to lib7

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-02-26 00:49:21 +01:00
Christoffer Lerno
ee5b9e5826 @if declarations were missing from -P output #1973. 2025-02-25 17:00:47 +01:00
Christoffer Lerno
5d3c3781e4 Update msys CI 2025-02-25 16:09:23 +01:00
Christoffer Lerno
c13c0d04b1 Run MSVC with "no-terminal" 2025-02-25 16:02:51 +01:00
Christoffer Lerno
88f44f1eac Fix test. 2025-02-25 15:48:44 +01:00
Christoffer Lerno
50680d6893 Fix bug casting bool to int to other int #1995. Use test_suite7 in CI. 2025-02-25 15:36:06 +01:00
Christoffer Lerno
31096531e1 Several fixes for .o files and -o output, improving handling and naming, checking for existence of sub-folders before output. Warning on using .o and -o output when not single module etc. Fix to static lib regression. Info about asm / llvm / obj output. 2025-02-25 14:05:00 +01:00
Christoffer Lerno
062a67fe75 Updates to file:: and path::, Path is now passed an allocator. path::traverse function. mkdir / rmdir / chdir works directly with strings. Strings get file_basepath, path_dirname. Test suite runner now uses lib7. Bug when printing a parameter declaration error. Fix optional jumps in expression lists, #1942. 2025-02-25 02:18:33 +01:00
Christoffer Lerno
1dfc24822e Update pool test. 2025-02-24 22:37:20 +01:00
Christoffer Lerno
e35c7f0b90 Update pool test. 2025-02-24 18:07:36 +01:00
Christoffer Lerno
7083b2b8e5 Added --suppress-run from #1931. 2025-02-24 11:25:07 +01:00
Christoffer Lerno
135213388d Add bigint fixes to lib7 2025-02-24 11:15:50 +01:00
Jonas Quinten
ed62268997 std::math::bigint: Fixed init_with_array with empty array 2025-02-24 11:12:55 +01:00
Jonas Quinten
38110b0269 fix 2025-02-24 11:12:55 +01:00
Jonas Quinten
e34d56327a std::math::bigint: Fixed init_with_u128 and init_with_array 2025-02-24 11:12:55 +01:00
Jonas Quinten
b50e6bd0e4 std::math::bigint: Added unit tests for init_with_u128 and init_with_array 2025-02-24 11:12:55 +01:00
Christoffer Lerno
3ba68f85fe Fix bug checking for @builtin 2025-02-24 10:28:50 +01:00
Christoffer Lerno
9f5c5a9acf Update some examples. 2025-02-24 02:20:02 +01:00
Christoffer Lerno
b6f5938eda Change to {} generics in lib7. Update qoi and other encodings and multiple other small changes. 2025-02-24 01:44:57 +01:00
Christoffer Lerno
87725a3a9e Create a unit7 for all unit tests. 2025-02-24 01:05:45 +01:00
Christoffer Lerno
70029cc4b8 Updated stdlib to experimental allocator first. Updates to DString, String and anything using new_* syntax. 2025-02-23 22:54:48 +01:00
Christoffer Lerno
4f72bc4be9 Fixes to lib7, added parallel test structure. 2025-02-23 13:53:04 +01:00
Christoffer Lerno
3a1aa8bdf0 Fix precedence of braces. Updated to lib2. 2025-02-23 01:30:34 +01:00
Christoffer Lerno
a986d053c0 Add lib2 to allow hacking the stdlib more for 0.7.x 2025-02-23 00:24:01 +01:00
Christoffer Lerno
43943c1f33 Update MSVC paths in CI 2025-02-22 23:02:07 +01:00
Christoffer Lerno
3da9f73338 - Output into /.build/obj/<platform> by default.
- Output llvm/asm into llvm/<platform> and asm/<platform> by default.
- Don't delete .o files not produced by the compiler.
- Correctly handle in/out when interacting with inout.
2025-02-22 22:34:26 +01:00
Christoffer Lerno
855be92881 Regression with .gitkeep in project init. List.init incorrectly didn't have the first argument the allocator. Added .init to priority queue. Created mem thread allocator alias. Correctly handle ident aliases. Allow ident on builtin aliases. 2025-02-21 21:34:48 +01:00
Christoffer Lerno
e674deb486 Fix generics. 2025-02-21 16:12:35 +01:00
Christoffer Lerno
3ef094a3d3 Add some conveniences to Clock and thread. Allow atomic load on booleans. 2025-02-21 15:56:32 +01:00
Christoffer Lerno
9c60c2cb33 Change to avoid thread races during compilation. 2025-02-21 13:15:19 +01:00
Christoffer Lerno
b54d994475 Fix memcmp misuse in parsing asm args. 2025-02-21 09:46:56 +01:00
Christoffer Lerno
80e360d8dd Dispose of copied module. 2025-02-20 23:25:25 +01:00
Christoffer Lerno
9f165342e2 Revert disposal 2025-02-20 22:41:44 +01:00
Christoffer Lerno
a2bfeb156d Dispose of the LLVMModule 2025-02-20 22:36:00 +01:00
Christoffer Lerno
bb8c03777d Fix address overread 2025-02-20 21:56:28 +01:00
Christoffer Lerno
8338888976 Do not use optimization for C++ wrapper. 2025-02-20 18:36:22 +01:00
Christoffer Lerno
79db06ecd1 Crash when trying to define a method macro that isn't @construct but has no arguments. 2025-02-20 15:51:21 +01:00
Christoffer Lerno
341a70bd5d Implicitly unwrapped optional value in defer incorrectly copied #1982. 2025-02-20 03:44:22 +01:00
Christoffer Lerno
8bf9ca89a1 Add a separate job to just run the test suite runner for Mac. 2025-02-20 02:30:43 +01:00
Alex Veden
5046608d1f added io::stdout().flush() - to force printing test name before possible deadlock
mem::scoped() and long jump resilience fixed #1963
fixed --test-nosort argument + extra test for teardown_fn memory leak
Some renaming. Simplify robust test allocator handling. Pop temp allocators in test runner.
`Thread` no longer allocates memory on posix.
Update unprintable struct output.
Correctly give an error if a character literal contains a line break.
2025-02-20 01:15:48 +01:00
Christoffer Lerno
535151a2a5 Fix character literal regex. 2025-02-20 00:59:18 +01:00
Christoffer Lerno
b45cb22950 Some improvements to the test_suite_runner 2025-02-19 20:59:12 +01:00
Christoffer Lerno
d6485ca08b Test new tester script. 2025-02-19 18:01:44 +01:00
Christoffer Lerno
d9e5926d57 Fix error when boolean combined with ??. First checkin of C3 tester (unfinished) 2025-02-19 01:02:58 +01:00
Christoffer Lerno
cbacd64987 Update tests to (Foo) { ... } syntax. 2025-02-18 18:53:30 +01:00
Christoffer Lerno
168c11e006 {| |} expression blocks deprecated. 2025-02-18 12:50:34 +01:00
Mateo Acuña
26362d5068 Improve crtbegin.o Lookup for Linux Targets (#1975)
* refactor linux crtbegin lookup to use architecture-specific glob paths
* Fixed incorrect function call to `get_linux_crt_begin_glob_path` (`get_linux_crt_begin_arch_glob`)
* Formatting

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-02-18 00:30:54 +01:00
Christoffer Lerno
e77d1fb646 - Increase precedence of (Foo) { 1, 2 }
- Add `--enable-new-generics` to enable `Foo{int}` generic syntax.
2025-02-18 00:26:22 +01:00
Christoffer Lerno
c41d551ead Create 0.6.8 2025-02-18 00:26:22 +01:00
Christoffer Lerno
0d7697280c Fix of test (again) 2025-02-17 00:22:21 +01:00
Christoffer Lerno
0a93581695 Fix test. 2025-02-17 00:03:44 +01:00
Christoffer Lerno
0509b40b21 - Fix issue when dereferencing a constant string.
- Fix problem where a line break in a literal was allowed.
2025-02-16 23:55:55 +01:00
Christoffer Lerno
6e11bdbd35 Fix issue where target was ignored for projects. 2025-02-16 23:31:03 +01:00
Christoffer Lerno
8a6e996442 Create release candidate 0.6.7 2025-02-16 01:24:51 +01:00
pekochan069
be00fdb253 Add scoop installation 2025-02-14 20:13:50 +01:00
Christoffer Lerno
0dd1a93d0d Regression String! a; char* b = a.ptr; would incorrectly be allowed. 2025-02-14 16:11:31 +01:00
Christoffer Lerno
7ca70b20be Allow (Foo) { 1, 2 } syntax for compound literals. 2025-02-14 12:51:58 +01:00
Bruno Dias
e0cfe56121 Fixed nix build for macos. (#1914)
* Fixed nix build for macos.
* Fix test.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-02-13 22:44:21 +01:00
Christoffer Lerno
6ca77065d8 Fix issue when parsing bitstructs, preventing them from implementing interfaces. 2025-02-13 21:51:22 +01:00
Christoffer Lerno
e96dce92cd Issue when scalar expanding a boolean from a conditional to a bool vector #1954. 2025-02-13 21:36:28 +01:00
Christoffer Lerno
cec9b21707 Missing end padding when including a packed struct #1966. 2025-02-13 21:15:27 +01:00
Christoffer Lerno
8c58b31bbd Remove <[]> experimental generic syntax. 2025-02-13 12:53:46 +01:00
Christoffer Lerno
c785572467 Add allocator::wrap. 2025-02-13 03:10:53 +01:00
Jamie Wales
a297470887 Add further tests to vector library 2025-02-12 23:31:10 +01:00
Velikiy Kirill
f0682422c0 Add more Windows API types and structs (#1956)
* Add more Windows API types and structs
* Add more Windows API types and structs (merged sock.c3 to wsa.c3)
* Some formatting.

---------

Co-authored-by: Kirill Velikiy <velikoss@vk.com>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-02-12 23:30:07 +01:00
Christoffer Lerno
1f856cacf5 HashMap is now Printable. Fix access inlining for enums. #1958 2025-02-12 23:11:46 +01:00
Ygor Pontelo
c9ecb09cd7 duration fix (#1950)
* duration fix
* Update release notes, explicitly send -1.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-02-12 22:13:07 +01:00
Christoffer Lerno
4961d0433f - Circumvent Aarch64 miscompilations of atomics.
- Fixes to ByteBuffer allocation/free.
- Fix issue where compiling both for asm and object file would corrupt the obj file output.
2025-02-12 12:50:30 +01:00
Christoffer Lerno
ba48627ca0 Fix address out of bounds access in test. 2025-02-11 00:22:01 +01:00
Christoffer Lerno
45eb3acffe Remove unused parameter in mem::clear. 2025-02-11 00:15:33 +01:00
Christoffer Lerno
d3ad533dd6 Fix issue in List with sanitizers. 2025-02-11 00:13:04 +01:00
Christoffer Lerno
9e54014848 Fix issue in GrowableBitSet with sanitizers. 2025-02-10 23:55:02 +01:00
Christoffer Lerno
f8e3ffd267 Fix test 2025-02-10 22:19:16 +01:00
Christoffer Lerno
8b8a2beb0d Fix threading test. 2025-02-10 22:08:54 +01:00
Christoffer Lerno
79a4b6855b - Detect unaligned loads #1951.
- Fix issue where aligned bitstructs did not store/load with the given alignment.
2025-02-10 22:07:15 +01:00
Christoffer Lerno
86680279fa Improve inference on ?? #1943. 2025-02-10 16:20:33 +01:00
Christoffer Lerno
b46d3947dd Postpone Xtensa addition. 2025-02-10 12:00:50 +01:00
Christoffer Lerno
c4212c4649 - Test runner will also check for leaks.
- `write` of qoi would leak memory.
- Issue when having an empty `Path` or just "."
- `set_env` would leak memory.
2025-02-10 00:39:02 +01:00
Christoffer Lerno
63f619e5b6 Add tracking allocator to test runner. #1809 2025-02-09 03:10:35 +01:00
Christoffer Lerno
ce06de4b18 Updates to grammar. 2025-02-09 02:19:27 +01:00
Christoffer Lerno
e1d546225f Update stdlib with new syntax for short function decl. 2025-02-08 23:04:59 +01:00
Christoffer Lerno
c4f9efc8f5 Allow fn int test() => @pool() { return 1; } short function syntax usage #1906. 2025-02-08 22:45:14 +01:00
Christoffer Lerno
69e30c19f8 Distinct inline void causes unexpected error if used in slice #1946. 2025-02-08 20:33:08 +01:00
Christoffer Lerno
940874e349 Cleaner error message when missing comma in struct initializer #1941. 2025-02-08 19:54:44 +01:00
Christoffer Lerno
d3f2180330 bigint::from_int(0) throws assertion #1944. 2025-02-08 19:15:14 +01:00
Christoffer Lerno
68b5c1e1f1 Fix bigint hex parsing #1945. 2025-02-08 19:06:06 +01:00
Christoffer Lerno
c8e671d34b Assert when using optional as init or inc part in a for loop #1942. 2025-02-08 18:58:44 +01:00
Christoffer Lerno
46c7e9aefa Cleanup QOI 2025-02-08 00:32:48 +01:00
Christoffer Lerno
2126be2222 Fix issue number. 2025-02-08 00:15:47 +01:00
Christoffer Lerno
fa4fb44779 Issue with defer copying when triggered by break or continue. 2025-02-08 00:14:01 +01:00
Christoffer Lerno
07e8779d4e Fix fixup ordering in defer. 2025-02-07 23:12:34 +01:00
Christoffer Lerno
77db50bce8 Allow function types to have a calling convention. #1938 2025-02-07 22:03:15 +01:00
Jooris Hadeler
ea4c864d4b Add Socket.peek to allow peeking at the receiving queue. (#1933)
* Add `Socket.peek` to allow peeking at the receiving queue.

This uses the `MSG_PEEK` flag to peek at the beginning of the
receiving queue without removing data from said queue.
2025-02-07 21:00:54 +01:00
rexim
e6ec09f2c5 Remove the need for the socket helper function, and use the new inline enum functionality. 2025-02-07 20:55:40 +01:00
rexim
122179980c Introduce Socket.shutdown() 2025-02-07 20:52:36 +01:00
Christoffer Lerno
27e76fe59e Project view refactoring. 2025-02-07 20:49:51 +01:00
Christoffer Lerno
d13f302ac8 Build options refactoring. 2025-02-07 16:04:44 +01:00
Christoffer Lerno
3e1e3e3e29 Incorrect error message when providing too many associated values for enum #1934. 2025-02-07 10:44:53 +01:00
Christoffer Lerno
0388910c17 Cleanup. 2025-02-07 01:08:28 +01:00
Christoffer Lerno
4b984e12a5 Refactor build options. 2025-02-07 00:11:04 +01:00
Christoffer Lerno
4e717657bd Remove not-yet-supported docs tool. 2025-02-06 23:23:24 +01:00
Fangrui Song
78dcda0bb2 Clean up some linker/C compiler options
-fno-pic/-fno-pie/-fpic/-fPIC/-fpie/-fPIE options belong to the same
famility where the last option wins. These options have no effect in the
link phase.

Clang and GCC usually pass `--eh-frame-hdr` to ld, with the exception
that `gcc -static` does not pass `--eh-frame-hdr`. The difference is a
historical choice related to `__register_frame_info`. We can behavle
like Clang and always pass `--eh-frame-hdr`.

Remove a `-L` that does not specify a directory.
2025-02-06 22:57:49 +01:00
Christoffer Lerno
bc63c16c93 Add @select to perform the equivalent of a ? x : y at compile time. 2025-02-06 22:21:26 +01:00
Christoffer Lerno
e3851f3723 return (any)&foo would not be reported as an escaping variable if foo was a pointer or slice. 2025-02-06 16:33:42 +01:00
Aleksandr Vedeneev
3a502feb1d test::eq/ne/gt syntax (#1928)
* test::eq/ne/gt syntax

* renamed @almost to test::eq_approx
2025-02-05 22:50:25 +01:00
Christoffer Lerno
ef72e19bf0 Remove 20 from docker. 2025-02-05 01:03:07 +01:00
Christoffer Lerno
8b794e8cea Updated test (again!) 2025-02-04 23:36:27 +01:00
Christoffer Lerno
549e27a800 Fix test compatibility with LLVM 21 2025-02-04 23:11:09 +01:00
Christoffer Lerno
d05cc991f5 Support new LLVM, fix max version. 2025-02-04 23:03:02 +01:00
Christoffer Lerno
07be4b0e06 Support new LLVM 2025-02-04 23:01:32 +01:00
Christoffer Lerno
6fcda240b8 Fixes to enum conversions. 2025-02-04 22:26:51 +01:00
Christoffer Lerno
fff3cf33c7 Issue where inlined expr enums weren't properly const folded. 2025-02-04 21:46:23 +01:00
Christoffer Lerno
a862437bac Rephrased the text. 2025-02-04 11:27:44 +01:00
Christoffer Lerno
7a6df10b39 Update error message on casting between distinct types. 2025-02-04 11:24:08 +01:00
Christoffer Lerno
c54c400291 Allow inline enum values to define sizes. 2025-02-04 00:23:59 +01:00
Christoffer Lerno
aaa5c0f743 Fix bug in parsing inline enums. 2025-02-03 23:43:34 +01:00
Christoffer Lerno
9d2f4e72c2 Add inline to enums #1819. 2025-02-03 22:51:50 +01:00
Danyella Strikann
70a849cbb5 Removed the unused command headersfrom the usage
Signed-off-by: Danyella Strikann <danyellastrikann@duck.com>
2025-02-03 00:40:41 +01:00
Christoffer Lerno
ecb25a0010 Remove accidental include. 2025-02-03 00:39:19 +01:00
Christoffer Lerno
300983f831 Compile time array assign ops, e.g. $c[1] += 3 #1890. 2025-02-03 00:35:20 +01:00
Christoffer Lerno
f2df4855ff Improve error message when using ',' in struct declarations. #1920 2025-02-02 22:44:30 +01:00
rexim
50c590bb5f Suggest alternative to reference macro arguments 2025-02-02 22:22:34 +01:00
Radek Micek
4a99ebef51 clock_gettime returns CInt, not void 2025-02-02 22:21:31 +01:00
Christoffer Lerno
20d93ede0c Fix test compatibility with LLVM 20 2025-02-02 02:47:51 +01:00
Christoffer Lerno
f8b2f7f268 Refactor casts and make untyped list conversions not dependent on context. 2025-02-01 23:37:32 +01:00
Christoffer Lerno
dc6d994480 Fixing various issues around shifts, like z <<= { 1, 2 }. 2025-02-01 15:43:42 +01:00
Christoffer Lerno
2b3b7e32b8 Fix bug indexing into a constant array at compile time. 2025-02-01 00:36:30 +01:00
Christoffer Lerno
03e2b30ede Compile time array inc/dec #1890. 2025-01-31 23:58:44 +01:00
Adversing
f3afec61bb Added fmodf function #1875 2025-01-31 22:20:45 +01:00
Christoffer Lerno
bda33ca3f9 Update defer 2025-01-31 16:59:14 +01:00
Christoffer Lerno
50c1aac9bb Usage of @noreturn macro is type-checked as if it returns #1913. 2025-01-31 16:19:12 +01:00
Christoffer Lerno
9092defd46 defer is broken when placed before a $foreach #1912 2025-01-31 14:39:51 +01:00
Christoffer Lerno
7dd9256e2d Update temp path append function. 2025-01-30 23:20:38 +01:00
Christoffer Lerno
a056efce04 Additional cleanup. 2025-01-30 19:32:20 +01:00
Christoffer Lerno
0bad8f92b0 More conservative use of getcwd and some cleanup. 2025-01-30 18:13:12 +01:00
Christoffer Lerno
b040736f7f Additional cleanup. 2025-01-30 14:38:25 +01:00
Christoffer Lerno
778260213e Some minor cleanup and updates. Should improve build issue with Gentoo #1907. 2025-01-30 10:45:41 +01:00
Christoffer Lerno
50385be614 Some minor cleanup and updates. 2025-01-30 01:58:51 +01:00
Christoffer Lerno
3c50376175 New test runner 2025-01-30 01:09:48 +01:00
Christoffer Lerno
6848753a10 Warn on if-catch with just a default case #1904. 2025-01-29 15:29:09 +01:00
Christoffer Lerno
13771cc536 Truncate output from execute and print to stdout on error. 2025-01-29 13:51:33 +01:00
Christoffer Lerno
ac3b2f0fea - Fix bug where in dead code, only the first statement would be turned into a nop.
- Remove unused $inline argument to mem::copy.
2025-01-29 13:07:19 +01:00
Christoffer Lerno
4b61ac7dae Project / build refactoring. 2025-01-29 00:25:22 +01:00
Christoffer Lerno
70d0ad1fcc Missing error when placing a single statement for-body on a new row #1892. 2025-01-28 17:13:37 +01:00
rexim
af2a0ffd3f Fix missing r15 register in inline assembly 2025-01-28 15:10:48 +01:00
Christoffer Lerno
02c3d5419b Add with_padding convenience functions. 2025-01-28 00:18:08 +01:00
Christoffer Lerno
55fba09b3b Fixed STB_WEAK errors when using consts in macros in the stdlib #1871. 2025-01-27 23:51:23 +01:00
Christoffer Lerno
d2a7dc4a9a Fix test take two. 2025-01-27 20:51:41 +01:00
Rene Hangstrup Møller
d8ca0f69f6 fix allocator::new_aligned #1898 2025-01-27 20:38:11 +01:00
Christoffer Lerno
7d0e143224 Remove debug code. 2025-01-27 20:36:41 +01:00
Christoffer Lerno
bd139f73ac Fix test. 2025-01-27 15:25:46 +01:00
Christoffer Lerno
f23dda8d50 Fix regression for exec #1881. 2025-01-27 14:34:05 +01:00
Christoffer Lerno
a88364aaad Fixes miscompilation of nested @jump #1896. 2025-01-27 11:35:55 +01:00
Christoffer Lerno
9530fe8fcd Fix regression for parsing types and switch to the "new" generic syntax that's being tested. 2025-01-26 22:51:27 +01:00
Christoffer Lerno
26dc88e096 Fix issues with @jump on empty default or only default #1893 #1894 2025-01-26 15:38:24 +01:00
Christoffer Lerno
1f1c445a76 Issue where trailing body argument was allowed without type even though the definition specified it #1879. 2025-01-25 23:52:13 +01:00
Christoffer Lerno
3e4f9e875f Add test for casts and append. 2025-01-25 23:18:35 +01:00
BWindey
dab4844195 [FIX] Let c3c project subcommands use same logic as others to get project.json (#1885)
* Extract project JSON loading into its own function
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-25 22:52:31 +01:00
Christoffer Lerno
e40bab2d30 Allow (int[*]) { 1, 2 } cast style initialization. Experimental change from [*] to [?]. Fix issue where compile time declarations in expression list would not be handled properly. 2025-01-25 22:10:12 +01:00
Christoffer Lerno
ca91ad4097 Fix bug where .min/.max would fail on a distinct int #1888. 2025-01-25 01:01:52 +01:00
Christoffer Lerno
e2b11c17bc - Compile time array assignment #1806.
- Allow `+++` to work on all types of arrays.
2025-01-25 00:48:06 +01:00
Christoffer Lerno
eda997545a Fix issue in optimized if lowering. 2025-01-24 17:14:34 +01:00
Christoffer Lerno
92b3490210 Fix issue where an if statement would not execute at all. 2025-01-24 16:42:59 +01:00
Christoffer Lerno
ba545b44f0 Change const checking in if lowering. 2025-01-24 16:31:27 +01:00
Christoffer Lerno
4f130cfe56 Further lvalue refactoring. This completely removes CHECK_LVALUE. 2025-01-24 00:19:22 +01:00
Christoffer Lerno
145b76ec75 Cleanup. 2025-01-23 11:03:43 +01:00
Christoffer Lerno
e30952b484 INLINE to static inline for refactored function. 2025-01-23 11:00:35 +01:00
Christoffer Lerno
b145c073f0 VERY experimental <[ ]> syntax for generics. Continue lvalue refactoring. 2025-01-23 01:29:35 +01:00
Christoffer Lerno
948d56b321 Refactor $ct lvalue handling. 2025-01-23 00:00:22 +01:00
Christoffer Lerno
69d0fa8c44 Correctly check jump table size and be generous when compiling it #1877. 2025-01-22 22:33:35 +01:00
Christoffer Lerno
a845a932f5 Change _MSC_VER to PLATFORM_WINDOWS for some exec. Fix to nix. 2025-01-22 00:44:50 +01:00
Christoffer Lerno
3221180315 Fixes to `"exec" use. 2025-01-22 00:26:40 +01:00
BWindey
c326c525be [FEAT] Add CLI flags to filter 'c3c project view' results (#1863)
* Project view feature
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-21 23:46:41 +01:00
Christoffer Lerno
16aadae9bd Remove allocator argument 2025-01-21 22:25:42 +01:00
Snikimonkd
b7ffa3b17c [feat] add test tmp files to gitignore 2025-01-21 12:44:13 +01:00
Christoffer Lerno
772b20c26b Fix find_msvc 2025-01-21 01:26:55 +01:00
Christoffer Lerno
c7eb0024c7 Error on switch case fallthough if there is more than one newline #1849. 2025-01-21 00:38:24 +01:00
Christoffer Lerno
1a2dcd07ee Add win-debug setting to be able to pick dwarf for output #1855. 2025-01-21 00:13:11 +01:00
Christoffer Lerno
ab32231cd1 Added releasenotes. 2025-01-20 23:51:54 +01:00
Snikimonkd
a0192a0116 [FEAT] add golang like channel (#1843)
* [feat] add golang like channels
* Updated new_init/init. Some fixes for init.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-20 23:51:00 +01:00
Christoffer Lerno
13e3ecbde2 Tab and style 2025-01-20 23:31:49 +01:00
Christoffer Lerno
fefe6d1342 Filter $exec output from \r, which otherwise would cause a compiler assert #1867. 2025-01-20 22:32:53 +01:00
Bram Windey
bbef5656a5 Fix 'strtul' to 'strtoul' 2025-01-20 21:45:15 +01:00
Christoffer Lerno
ad3cd88350 Fix dues to crash when converting a const vector to another vector #1864. 2025-01-20 16:26:26 +01:00
Christoffer Lerno
5183370773 Update mingw llvm/lld 2025-01-20 14:53:59 +01:00
cd-n0
f74891d214 Fix linux-crt and linux-crtbegin not getting recognized as a project parameter (#1865)
* Fix `linux-crt` and `linux-crtbegin` not getting recognized as a project parameter

* Update releasenotes.md
2025-01-20 14:48:41 +01:00
Christoffer Lerno
d2885faa79 Further cleanup. 2025-01-20 04:09:47 +01:00
Christoffer Lerno
c59d47f652 Keep the old behaviour which made the script detect bugs (although indirectly!) 2025-01-20 03:43:12 +01:00
Christoffer Lerno
f863c4ae84 Fix incorrect arg type failing MSVC compilation. Missing ASSERT updated. Update python script. Fix bug printing error duplicate generic module. 2025-01-20 03:35:49 +01:00
Christoffer Lerno
bb2a2526e4 Refactoring access + some macro renaming. 2025-01-20 02:44:39 +01:00
Christoffer Lerno
f9b86226a8 Refactoring identifier and catch unwrap into two different nodes. 2025-01-19 13:23:21 +01:00
Christoffer Lerno
a4f5c97150 Fix typo 2025-01-18 23:53:32 +01:00
Christoffer Lerno
5de03abe0d Concatenating an const empty slice with another array caused a null pointer access. 2025-01-18 23:50:31 +01:00
Christoffer Lerno
c3f5806aa3 Const strings and bytes were not properly converted to compile time bools.
Contracts @require/@ensure are no longer treated as conditionals, but must be explicitly bool.
2025-01-18 23:24:55 +01:00
Christoffer Lerno
5a36f0bc16 Fix issue with @const where the statement $foo = 1; was not considered constant. 2025-01-18 22:40:58 +01:00
Christoffer Lerno
c5dbbf9ff7 Compiler allows a generic module to be declared with different parameters #1856. 2025-01-17 23:24:42 +01:00
Christoffer Lerno
304b604652 Added weakly linked __powidf2 2025-01-17 17:42:39 +01:00
BWindey
b787985bf7 Improve c3c --help/-hh and c3c project (#1851)
* Fix missing newline and incorrect indentation for 'c3c project' help on fetch

* Use a ternary trick :D

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-17 12:23:15 +01:00
Christoffer Lerno
d72ec09cee Fix lack of location for reporting lambdas with missing return statement #1857. 2025-01-17 11:55:56 +01:00
Christoffer Lerno
d4bd68c188 Fix bug in SHA1 for longer blocks #1854. 2025-01-17 01:10:40 +01:00
Christoffer Lerno
f51bfa5a44 Update latest version. 2025-01-16 22:45:48 +01:00
Christoffer Lerno
3e4d1de70e Fix issue requiring prefix on a generic interface declaration. 2025-01-16 22:09:53 +01:00
Christoffer Lerno
721aaa28aa Added '%h' and '%H' for printing out binary data in hexadecimal using the formatter. 2025-01-16 01:06:57 +01:00
Christoffer Lerno
15503a9054 Release candidate 0.6.6 2025-01-15 22:10:48 +01:00
Taylor W
660654f9e0 math_tests: pow test (#1842)
* math::nolibc: replaced code with word macros

* math_tests: pow test

Added test for pow and added more test points for the exp and log tests.
2025-01-15 13:35:18 +01:00
Christoffer Lerno
2f7d18bfb8 Quicksort and insertsort incorrectly allowing arrays and vectors by value. #1845. 2025-01-15 13:31:29 +01:00
Christoffer Lerno
29a6a0db32 Fix unavailable LLVM int128 alignment. 2025-01-15 11:49:33 +01:00
Christoffer Lerno
7b2fe92241 Improve error message on incorrect inner struct/union name #1847. 2025-01-15 10:54:50 +01:00
Christoffer Lerno
70da1f748a Enum associated declarations accidentally allowed declaration in function style. #1841 2025-01-14 23:06:17 +01:00
Christoffer Lerno
3033295884 Fix bug with enums with jump tables #1840 also affecting ranged enums entries. 2025-01-14 22:47:12 +01:00
Christoffer Lerno
8c12f92aff Make stringify to recursively enter #hash expressions #1834. 2025-01-14 12:40:42 +01:00
Christoffer Lerno
76da7936e5 Fix issue with inferred vector output to JSON. #1839 2025-01-14 12:25:49 +01:00
Max
2a924ae3b0 fix the link order to support LLVM_20 change (#1838)
* fix the link order to support LLVM_20 change

* Update CI to use LLVM 20

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-14 11:23:40 +01:00
Christoffer Lerno
5ba9acad5d Fix bug where &i[0] = null was not detected to be an error. #1833 2025-01-14 01:43:59 +01:00
Christoffer Lerno
4cb984e56d Prevent temp arena scribbling from causing an asan warning. #1825 2025-01-13 16:58:55 +01:00
Christoffer Lerno
70606a2bbe Report the correct type as not having a method when access fails #1828. 2025-01-13 14:11:51 +01:00
Taylor W
259112e178 math: macros to set floating-point numbers with uint (#1826)
* math: Setting the bits of floating-point numbers

Added macros which set all 32 bits of a float, the lower 32 bits of
a double, and the upper 32 bits of a double. Some changes were made to
older code to use these macros.

* Replaced code with bitsetting macros in __tan.c3 and tan.c3

* math: tests for word macros and release notes

Tests were written for the word macros, which include getting and
setting a float with a uint and getting and setting the high or low word
of a double with a uint.

Release notes were updated to include the word setter macros.
2025-01-13 13:37:49 +01:00
Christoffer Lerno
a2cde1e072 Correctly handle known length slices with index checks... and now works with $defined too. 2025-01-13 13:20:59 +01:00
Christoffer Lerno
de04c52379 Correctly handle known length slices with index checks. 2025-01-13 02:30:35 +01:00
Christian Buttner
27970085e5 Fix formatter output length calculation. 2025-01-12 22:54:43 +01:00
Jefferson Amstutz
e0afc0f9ea use WORKING_DIRECTORY to ensure stability of finding the git hash 2025-01-12 22:53:16 +01:00
konimarti
0e44e63fa8 net/url: implement url encoding (RFC 3986) (#1795)
* net/url: implement url encoding (RFC 3986)

Implement url percent-encoding and -decoding functions according to RFC
3986. Add unit tests.

Link: https://datatracker.ietf.org/doc/html/rfc3986

* net/url: ensure correct encoding of URL components

Add encoding and decoding methods to the Url struct components according
to RFC 3986.

An Url can be parsed from a String with `new_parse()` or `temp_parse()`.
The parsed fields are decoded. The only field that is not decoded is
`raw_query`. To access the decoded query values, use
`Url.query_values()`.

`Url.to_string()` will re-assemble the fields into a valid Url string
with proper percent-encoded values.

If the Url struct fields are filled in manually, use the actual
(un-encoded) values. To create a raw query string, initialize an
`UrlQueryValues` map, use `UrlQueryValues.add()` to add the query
parameters and, finally, call `UrlQueryValues.to_string()`.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-12 22:52:25 +01:00
Adversing
2623d7d525 Feat: Added exp, log and pow functions as requested in #1632 (#1781)
* Feat: Added exp, log and pow functions as requested in #1632
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-12 22:50:10 +01:00
Christoffer Lerno
4e78e32ced Fix regression with contract docs and generics #1821 2025-01-12 14:19:19 +01:00
Christoffer Lerno
f65ca07b62 Fix bug when multiple $else clauses followed an $if #1824. 2025-01-12 13:31:35 +01:00
welrox
7a805340c5 Add more aarch64 instructions for inline asm 2025-01-12 02:49:24 +01:00
Christoffer Lerno
a863d7fe9e Prevent #hash arguments from taking code that modifies ct variables. #1794 2025-01-12 02:20:18 +01:00
Christoffer Lerno
f60bfa8442 Assert concatenating constant slices #1805. Do not link "ld" on Linux with no libc. 2025-01-11 23:46:08 +01:00
Christoffer Lerno
50fdf9900d Regression: Broken type of constant initialized with cast from bitstruct #1811 2025-01-11 23:17:38 +01:00
Christoffer Lerno
8785c2c46f Assert when partially initializing a constant struct containing a slice #1812. 2025-01-11 22:42:33 +01:00
Christoffer Lerno
c8fa7b0cb3 Fix regression with swizzle references for vectors #1810. 2025-01-11 21:36:17 +01:00
Christoffer Lerno
f2e69f8fdc Fix bug with defer assignment in macro #1807. 2025-01-11 20:48:53 +01:00
Christoffer Lerno
8a9edc02b6 Fix bug preventing compile time slices from being iterated over with $foreach. 2025-01-11 03:23:03 +01:00
Christoffer Lerno
d173ba0377 More tests. 2025-01-11 01:51:34 +01:00
Christoffer Lerno
fbb4ae056a Bug when using +++ on value build a slice or array: the rhs cast was not done, corrupting data. 2025-01-11 01:02:13 +01:00
Christoffer Lerno
ae10ae6847 Fix regression when checking a macro constness. 2025-01-11 00:37:34 +01:00
Christoffer Lerno
64ab67bb58 Fixes to JSON output, making it valid. 2025-01-11 00:17:06 +01:00
Christoffer Lerno
dd650bc334 Fixes to JSON output. 2025-01-11 00:07:39 +01:00
Christoffer Lerno
48923a2237 Function comments are stored and displayed with -P. 2025-01-10 23:39:57 +01:00
Christoffer Lerno
1f29110271 Handle bytes and strings the same way in terms of zero termination. 2025-01-10 19:58:00 +01:00
Christoffer Lerno
e133f4406a Extend embedded files to zero terminate. 2025-01-10 19:25:53 +01:00
pekochan069
2fa258a066 Change vswhere command to find to find msvc toolchain correctly 2025-01-10 15:46:16 +01:00
Alex Veden
87d29a62e5 allowing c3c test -- <args1> 2025-01-10 15:45:05 +01:00
konimarti
190dc246b3 mem: add macro to assert on memory leak in scope (#1792)
* mem: add macro to assert on memory leak in scope

Implement `mem::@assert_leak` to assert on a memory leak in the scope of
the macro body. Memory report for the leak can be disabled by setting
the boolean argument to false.

* fix: add conditional compilation flags

* Moved the code into `mem.c3` and made it a builtin.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-10 13:33:33 +01:00
Christoffer Lerno
6ae84aac78 Updated releasenotes. 2025-01-10 13:17:45 +01:00
Louis Brauer
c8c58f946c Date/Time formatters (#1782)
* Add .DS_Store to .gitignore
* Allow <= 999_999 as usec on DateTime (was < 999_999)
* Move [Tz]DateTime .format() to std::time::datetime and import only with libc
* Changed name to DateTimeFormat, prefer function over method. Move names to enum.
* Updated tests to the latest standard.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-10 13:16:51 +01:00
Christoffer Lerno
0d1fb2843e Improve the error message when running out of memory. 2025-01-10 00:29:08 +01:00
Christoffer Lerno
d67fcb3956 Fix to the successful --lsp output. 2025-01-10 00:09:27 +01:00
Christoffer Lerno
f1325f6539 Added --lsp output. 2025-01-10 00:06:42 +01:00
Christoffer Lerno
3a1bba19af Allow test runners to take String[] arguments. 2025-01-09 22:32:59 +01:00
Christoffer Lerno
0857363470 Fix '\\' in -P output. 2025-01-09 20:46:36 +01:00
Christoffer Lerno
713199d7be Fix -P output. 2025-01-09 20:45:42 +01:00
Christoffer Lerno
b941f93416 Deprecate old void! @benchmark and @test functions. 2025-01-09 20:33:53 +01:00
Christoffer Lerno
c22b7d45c1 Update test failing on LLVM 17 2025-01-09 01:35:16 +01:00
Christoffer Lerno
c78bb45f2f Fix sample. 2025-01-09 01:33:58 +01:00
Christoffer Lerno
11f9365eb0 Remove all TODOs and make them strings to not crash things for -P output. 2025-01-09 01:32:21 +01:00
Christoffer Lerno
cdc1656f3a #foo style arguments were not type checked when given a type. #1790 2025-01-09 01:28:30 +01:00
Christoffer Lerno
8fb3ec73ff Deprecate $varef. 2025-01-08 23:56:10 +01:00
Christoffer Lerno
214e806a33 Deprecate `fn void! main() type main functions. 2025-01-08 23:17:50 +01:00
Christoffer Lerno
8e0d6d11b9 Deprecated '&' macro arguments. 2025-01-08 22:13:49 +01:00
Christoffer Lerno
9412b58d80 Remove trailing comma in project creation. 2025-01-08 13:44:09 +01:00
Christoffer Lerno
dad97fc2d9 Improved #foo resolution inside of the compiler.
Deprecation of several `&` macros.
2025-01-08 12:55:20 +01:00
vssukharev
ff33cc4dad Add Nix build and checks to CI/CD 2025-01-06 23:11:04 +01:00
Christoffer Lerno
51e0e5e66d Change ordering to simplify adding methods to type in conditional modules. 2025-01-06 22:36:29 +01:00
Christoffer Lerno
5fa6ecf9ae Enforce utf-8 on windows. 2025-01-06 20:02:57 +01:00
Christoffer Lerno
34c7f4e6b7 Deref subscripts as needed for macro ref method arguments. #1789 2025-01-06 13:54:02 +01:00
Christoffer Lerno
737559d3f8 Fix typo, minor changes. 2025-01-06 12:36:59 +01:00
Christoffer Lerno
f801372074 Optimize temp variables in LLVM. 2025-01-06 04:25:47 +01:00
Christoffer Lerno
ea2dce0ab4 Make "?:" lower in the frontend. 2025-01-06 03:01:13 +01:00
Christoffer Lerno
314c6f94f0 Remove the last "cast" operations. 2025-01-06 01:51:03 +01:00
Christoffer Lerno
8fd119e546 Refactor vector->array scalar->vector and slice->array casts to expressions. 2025-01-06 00:22:26 +01:00
Christoffer Lerno
8612476103 Refactor float<->float ptr->int and int->enum casts to expressions. 2025-01-05 23:16:48 +01:00
Louis Brauer
35812bd7ba Add String.trim_left() / right() (#1773)
* Add String.trim_left() / right()

* Fix formatting
2025-01-05 21:53:18 +01:00
Christoffer Lerno
f1ef2e8138 Improve @param parse errors #1777 2025-01-05 18:14:30 +01:00
robin
c47cb512ab Additional convenient functions and enums (#1767)
Added some missing API calls and enums when working with the Objective-C
Runtime.
2025-01-05 16:22:46 +01:00
KillerxDBr
fe7d4230d8 Fix 'clean' command on Windows MinGW
'c3c clean' command try to use 'rm' on Windows MinGW compiled executable, since it does not define "_MSC_VER", but define "_WIN32"
There are other uses of "_MSC_VER" in the same file, they probably can be changed to "_WIN32" too
2025-01-05 16:19:12 +01:00
Christoffer Lerno
b6e166f44d Include @name when searching for possible matches to name in the error message. #1779 2025-01-05 16:12:13 +01:00
Christoffer Lerno
ab2d223e71 Macros with trailing bodys aren't allowed as the single statement after a while loop with no body #1772. 2025-01-05 16:00:39 +01:00
Christoffer Lerno
c6c7baa3b4 Refactor casts, removing SLBOOL and PTRBOOL. 2025-01-05 15:45:39 +01:00
Christoffer Lerno
218f293cd4 Introducing int_to_ptr expr. 2025-01-05 15:26:20 +01:00
Christoffer Lerno
4d641d193c Refactoring removing cast types. 2025-01-05 15:09:30 +01:00
Christoffer Lerno
67ff78f1ca Fix not freeing a zero length String 2025-01-05 14:30:00 +01:00
vssukharev
4f0716ab13 Fix issue when building on NixOS, firstly occured under commit 819a85ee 2025-01-05 14:18:56 +01:00
Christoffer Lerno
07c59e6a6c Fix +a = 1 erronously being accepted. Refactorings. 2025-01-05 02:24:11 +01:00
Christoffer Lerno
86a674b87e Remove unused cast. 2025-01-04 23:25:55 +01:00
Christoffer Lerno
9957ab259c Fix vector float -> bool conversion. 2025-01-04 23:16:34 +01:00
Christoffer Lerno
4c3944f626 Refactor vec comparisons. 2025-01-04 23:06:21 +01:00
Christoffer Lerno
6f9b466d7c Optimize recast. 2025-01-04 21:58:04 +01:00
Thomas Adam
61badb6af7 libc: add isatty() 2025-01-04 15:41:51 +01:00
Christoffer Lerno
e31e57c7e7 Improved error message when accessing @private from other modules. Added convenience functions to Maybe. 2025-01-04 14:58:06 +01:00
Christoffer Lerno
469188044d Assert on certain slice to slice casts. #1768. 2025-01-04 13:31:47 +01:00
Christoffer Lerno
38063e5602 Refactor casts. 2025-01-04 13:04:08 +01:00
Bernd
ba5e2b7fa6 socket.c3: optimize poll() duration wrapping (#1762)
* socket.c3: optimize poll() duration wrapping
2025-01-04 00:32:17 +01:00
Christoffer Lerno
eed806962d Allow compile time $foreach iteration over constant Strings and bytes. 2025-01-04 00:26:21 +01:00
Christoffer Lerno
a1ce5e15ce Fix tests. 2025-01-04 00:09:24 +01:00
Christoffer Lerno
f0735c945a Update name to "validation" 2025-01-03 23:48:25 +01:00
Christoffer Lerno
d84e131b73 Add 'warnings' setting. 2025-01-03 23:37:37 +01:00
Christoffer Lerno
ad1511e69c Prohibit raw vaargs in regular functions with a function body. 2025-01-03 15:36:42 +01:00
Christoffer Lerno
db4dc114f2 $vasplat was allowed inside of a function when passed as an argument to a function. 2025-01-03 15:01:24 +01:00
Christoffer Lerno
a7f363ea43 Dynamic function lookup fails after changing type without dummy anycast due to poor tracing of typeid. #1761 2025-01-03 14:39:01 +01:00
Christoffer Lerno
0ccbba61ce Improve error message. 2025-01-03 12:07:54 +01:00
Christoffer Lerno
d921a4e168 Fix case when construct is using vaarg. 2025-01-03 12:05:10 +01:00
Thomas Adam
819a85ee06 build: add /usr/lib to LLVM_LIB search paths
It seems that on Alpine Linux (Edge), the LLVM_LIBRARY_DIRS cmake
variable doesn't look in /usr/lib, which is where the relevant files are
on Alpine.

Only do this for !WIN32 systems.
2025-01-03 11:47:00 +01:00
Christoffer Lerno
a3d15fe16c Fix issue with zero arg @operator(construct). Assert on add to uninitialized ct variable #1765 2025-01-03 11:45:46 +01:00
Christoffer Lerno
56d25cdeeb Remove array->vector casts 2025-01-03 01:35:51 +01:00
Louis Brauer
d027a15b4a add std::net::url - with fixes (#1748)
* add std::net::url for parsing/generating URLs

* Move String.index_of_chars into std

* Fix param contract

* Idiomatic type naming, Allman formatting, slicing, document functions

* Use String.tokenize

* Don't return str_view() from freed dstring

* Change indentation to tabs

* Variable casing according to guidlelines

* Updated API and added line to the releasenotes.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-02 21:13:42 +01:00
Francesco Alemanno
a16316d7b4 enhance default hashing strategy for basic types (#1758)
* enhance default hashing strategy for basic types

* fix

* `$defined` in a global scope should accept testing normal macros.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-01-02 20:44:33 +01:00
Christoffer Lerno
14d8e93004 Return type inference bugs with macros #1757 2025-01-02 17:34:37 +01:00
Christoffer Lerno
37c62bf9b7 Update releasenotes. 2025-01-02 16:29:16 +01:00
Koni Marti
72839d7654 fix: net::poll() with negative timeout
If the timeout to net::poll() is -1, poll() will block until a requested
event occurs. However, in the poll() function, the Duration timeout is
converted to milliseconds (Duration is in microseconds). The conversion
involves integer arithmetics which results in a zero timeout (= -1 /
1000), i.e. poll() will return immediately and the following accept will
fail.

To fix this, only convert to milliseconds if timeout paramter is
non-negative.

Example to reproduce the error:

```
module echo;
import std::io, std::net;
fn void main()
{
	TcpServerSocket server = tcp::listen("localhost", 6969, 69, REUSEADDR)!!;
	server.sock.set_non_blocking(true)!!;
	Poll[*] polls = {{ .socket = server.sock, .events = net::SUBSCRIBE_READ }};
	if (catch net::poll(polls[..], net::POLL_FOREVER)) io::printn("poll error");
	TcpSocket client = tcp::accept(&server)!!;
	io::printn("OK");
}
```

Poll() will return immediately and the following tcp::accept() will fail
with an "ACCEPT_FAILED" error.

Reported-by: Alexey Kutepov <reximkut@gmail.com>
2025-01-02 16:28:26 +01:00
Christoffer Lerno
1994cba73e Fix default #foo args. 2025-01-02 15:23:21 +01:00
vssukharev
55cdcbb39b Fix typo in libc: SIGABTR -> SIGABRT 2025-01-01 21:36:33 +01:00
Christoffer Lerno
c7ce6230db Update test with target. 2025-01-01 21:02:16 +01:00
Christoffer Lerno
7c45ae24ae Macros with default arguments to &, # and type parameters didn't work as expected. #1754. 2025-01-01 17:52:32 +01:00
Christoffer Lerno
99c350fc43 Fix error where panic would not properly stop the program when stacktrace couldn't be printed #1751. 2025-01-01 13:00:04 +01:00
Christoffer Lerno
faf1c5cb64 Introduce EXPR_RVALUE for some additional refactoring. 2025-01-01 12:19:00 +01:00
Radek Micek
ece6efc75e Fix fputc 2025-01-01 12:17:58 +01:00
Christoffer Lerno
0a809ab5f0 Allow using 'var' to declare lambdas in functions. 2025-01-01 01:02:35 +01:00
Christoffer Lerno
78ff1a4af5 Support experimental @operator(construct) operator overload. 2025-01-01 00:45:42 +01:00
Christoffer Lerno
c0dcae4f1d Improve ordering of method registration to support adding methods to generic modules with method constraints #1746 2024-12-31 18:15:38 +01:00
Christoffer Lerno
5e32c8a828 Improve posix thread error handling. 2024-12-31 17:27:13 +01:00
Christoffer Lerno
4d15a2f45e Improve error reporting when using type names as the function argument #1750. 2024-12-31 16:59:51 +01:00
Christoffer Lerno
a913f21c45 Fix issue with compiling a constant struct containing a string array in a local context. 2024-12-31 16:45:10 +01:00
Christoffer Lerno
322c70433b Refactoring, stop using int to bool as cast. Merge make any. 2024-12-31 16:32:37 +01:00
Christoffer Lerno
ad9cfcdcc7 Fix typo. 2024-12-30 18:24:01 +01:00
Christoffer Lerno
4232c9d2b0 Fix bug when including compile time parameters in trailing body more than once. 2024-12-30 17:57:36 +01:00
antek-bizon
9edd59d280 Add compilation guide for Fedora 2024-12-30 13:56:29 +01:00
Christoffer Lerno
df74cbf06f Fix bug where !! and ! was not recognized to jump out of the current scope. Remove more casts. 2024-12-30 01:43:02 +01:00
Christoffer Lerno
5af224ab16 Remove 3 casts and replace them with a single new normal expression node. 2024-12-30 00:55:40 +01:00
vssukharev
7b734df09e Several nix fixes (#1737)
* Fixed nix c3c development shell

* Fix unknown git hash on nix build
2024-12-29 21:12:26 +01:00
Christoffer Lerno
1340a47bc2 - any_to_int checks value to be int and no longer works with enum.
- Add check in formatter printing "%c".
2024-12-29 17:07:00 +01:00
Book-reader
4f4476ba75 fix typo in @require statement in builtin.c3 2024-12-29 12:24:36 +01:00
Christoffer Lerno
5c7a183f8a Change CBool to be 1 byte. 2024-12-28 22:46:26 +01:00
Taylor W
53bada2a1e math::nolibc: atanh (#1730)
* math::nolibc: log1p

* math::no_libc: atanh

Added atanh nolibc definition and more test points in the math_tests
module.
2024-12-28 21:13:44 +01:00
Totto16
43efb7df2f fix: use helper printing macros, to use the correct printing width (e.g. %llu vs %lu) 2024-12-28 19:21:39 +01:00
Christoffer Lerno
f5cea221a6 Miscompile when indexing an array with small unsigned types. 2024-12-28 17:39:25 +01:00
Christoffer Lerno
b7082f34a1 C backend work. 2024-12-28 17:09:43 +01:00
Christoffer Lerno
d20d957881 Add @enum_from_value 2024-12-27 23:21:36 +01:00
Christoffer Lerno
008274cda5 Update the release notes. 2024-12-27 22:35:28 +01:00
Alexey Kutepov
e07ab7547f Fix Segfault and UX of the c3c project add-target subcommand (#1729)
* Fix UX of the `c3c project add-target` subcommand

- Fix segfault on `project.json` containing empty map `{}`
- Enable `add-target` to operate on non-existing project files
- Extend `add-target` syntax to accept source through CLI args

This enables the following workflow without friction and needless
crashes:

```console
$ cat > main.c3 <<END
import std::io;

fn void main() {
    io::printfn("Hello, World");
}
END
$ c3c project add-target main executable main.c3
$ c3c run
```

* Fix read_project() call in fetch_project()

* Keep the style of curlies consistent
2024-12-27 22:33:57 +01:00
Christoffer Lerno
cf10837eb8 Add static lib for MSVC 2024-12-27 21:46:08 +01:00
Christoffer Lerno
291b26f230 Add static lib. 2024-12-27 02:05:40 +01:00
Christoffer Lerno
08e8c9bf57 Use weak on dyn-symbols on Linux. 2024-12-27 02:05:40 +01:00
Christoffer Lerno
625152440c Use weak_odr rather than weak on Windows which seems to prevent issues such as #1704. Fix regression. 2024-12-26 21:35:41 +01:00
Aaron
fbd51821d1 Fixed -P JSON syntax, string conversion. (#1714)
* Fix JSON formatting in parse command, strings now "JSON-legal"

* Clean up

* Implemented JSON for CONST_BYTES

* Handle empty byte string, slightly more concise

* of course msvc is the *only* compiler that complains about this
2024-12-26 02:18:49 +01:00
Christoffer Lerno
c3ebf51295 Add "name" project property to override the name of the resulting binary. #1719 2024-12-26 02:15:45 +01:00
Christoffer Lerno
9a9ff7f32c Fix bug in OnStackAllocator when freeing overallocated data. # #1720 2024-12-25 23:57:47 +01:00
Christoffer Lerno
7b73eec82b Prevent DString from being initialized with "". 2024-12-25 23:40:58 +01:00
Christoffer Lerno
75ba4a1cdb Incorrectly handles distinct enums and pointers with '+=' and '-=' #1717. 2024-12-25 23:09:10 +01:00
Christoffer Lerno
e5ca9065bd Deprecate cast conversion from enum -> integer. 2024-12-25 20:34:28 +01:00
Taylor Wampler
1042d0825f Fixed typo in atan.c3 2024-12-25 19:15:59 +01:00
Christoffer Lerno
17942925f5 Fix bug in temp allocator when temp memory is exhausted and allocation needs overaligned mem. #1715 2024-12-25 17:56:37 +01:00
Christoffer Lerno
7424317d03 Fix call to copy. 2024-12-25 00:16:35 +01:00
Christoffer Lerno
dbf1d91961 Add --win-vs-dirs to override VS detection dirs. 2024-12-25 00:07:57 +01:00
Christoffer Lerno
eb1644b302 Change included common options. 2024-12-24 20:43:52 +01:00
Christoffer Lerno
cde5bc3263 Change included common options. 2024-12-24 20:39:24 +01:00
Taylor Wampler
e995e289db math::nolibc: acos and asin
There are now nolibc definitions for the inverse cosine and inverse
sine.

More test points were added for acos, asin, and atan in the math_tests module.
This was done becuase the nolibc inverse trigonometric functions have
various branching conditions depending on the provided input value. Several
branches in these functions were neglected.
2024-12-24 11:29:32 +01:00
Taylor W
5020caa9c3 C3_MATH feature (#1709)
* C3_MATH feature

This feature allows the usage of noclib math files even when libc is in use.
If a nolibc symbol exists, it will be used in place of libc, otherwise
it will default to libc.

* Added MIT License notices to atan.c3
2024-12-23 23:42:57 +01:00
Aaron
f34eb7d9f3 Prevent trailing _ for all numbers (#1706)
* moved _ check to scan_number_suffix
* Skipping underscore on list-operators command
* Disallow # from operator, update release notes
2024-12-23 21:23:46 +01:00
Guillaume M.
bf74ef0e5e Fix crt detection for Arch Linux (#1705)
* Fix crt detection for arch linux

On archlinux, the crt is located directly in /usr/lib. Thus, the globbing done in find_linux_crt fails since it expects the crt to be in a subdirectory of /usr/lib. This patch fixes this issue.
2024-12-23 20:03:53 +01:00
Christoffer Lerno
0ff52311c3 - Fix problem where crt1 was linked for dynamic libraries on Linux and BSD. #1710 2024-12-23 20:02:17 +01:00
Christoffer Lerno
e453e6f9ca - Add enum.from_ordinal and fault.from_ordinal
- Deprecate cast-style conversion from integer to enum.
- Make deprecation an error in test mode.
2024-12-23 15:27:59 +01:00
Christoffer Lerno
6078598aff - static-lib and dynamic-lib options from the command line now produces headers.
- Fix bug outputting exported functions without predefined extname.
- Removed 'headers' command line option.
2024-12-22 11:49:11 +01:00
Taylor W
9fdb3b3b4a Adding trigonometric and hyperbolic trigonometric tests (#1699)
* math_tests: rewrote test_atan()

Rewrote the atan test so that analagous checks are symmetrically
performed for all possible inputs: int, float, and double. The total
number of tests has increased while reducing the total amount of
code.

* math_tests: inverse trig and inverse hyperbolic trig

Tests were written for acos, acosh, asin, asinh, and atanh.

* math: cos macro missing values::promote_int

The cosine macro can't take an integer input without this fix. You can
see that some of the other macros, like the sine macro, have this.

* math_tests: trig and exponential

Wrote tests for the trigonometric macros as well as the
exponential macro. The hyperbolic trig macros use the exponential macro
rather than any LLVM instrinsics (for now at least, LLVM 19 has the proper
compiler intrinsics).

* math: float comparison

In the math module two macros were defined to assist in comparing
floating-point numbers for a given tolerance.

The trig, hyperbolic trig, and exponential tests in the math_tests module
were updated to use these macros instead of direct comparison.
2024-12-21 22:25:23 +01:00
Christoffer Lerno
1362aa655f Split help into normal and "full" help, #1703 2024-12-21 16:08:07 +01:00
Christoffer Lerno
9c22ab8925 Add "skip_empty" to split methods. Add split_to_buffer method. 2024-12-21 15:43:37 +01:00
Christoffer Lerno
ca2dbb2f4b Update Mingw build. 2024-12-20 21:56:28 +01:00
Christoffer Lerno
16bbc5a026 Add "tokenizer" to String. 2024-12-20 13:18:40 +01:00
Christoffer Lerno
627f10cd18 Fix bug when a macro calling an extern function was called in another module also declaring and calling the same function. #1690 2024-12-19 20:39:23 +01:00
Christoffer Lerno
13509b9231 Remove LLVM 20 from testing due to broken build. 2024-12-19 19:51:50 +01:00
Tomas Kallup
ca88afbf5b Fix hashmap put all for create + test case
Closes: #1695
2024-12-19 16:15:18 +01:00
vssukharev
42c9c9894b Slight updates in nix 2024-12-18 21:04:42 +01:00
Christoffer Lerno
b4de62cfc2 Added iter() value_iter() and key_iter() to HashMap. 2024-12-18 21:02:28 +01:00
Christoffer Lerno
226fbc191b Update to Slice2d 2024-12-17 23:12:32 +01:00
Denis Palashevskii
4839d8861d increase BitWriter.write_bits nbits limit, add tests (#1692)
* increase BitWriter.write_bits nbits limit, add tests

* update releasenotes.md
2024-12-17 21:21:58 +01:00
Brecht Sanders
789b47d565 Support detection of shared MinGW-w64 LLVM libraries
Support detection of shared MinGW-w64 LLVM libraries by also looking for liblld*.dll.a files.
2024-12-17 11:29:40 +01:00
Walther Chen
c13cdcdd36 in new_struct_to_str, fix uaf 2024-12-17 11:29:18 +01:00
Christoffer Lerno
4ae3d0150f Fix case trying to initialize a char[*]* from a String. 2024-12-15 20:42:42 +01:00
Christoffer Lerno
7d153a162a Prepare 0.6.6 2024-12-14 23:06:08 +01:00
Christoffer Lerno
7bc3e94ff3 Build 0.6.5 release. Fixed. 2024-12-14 17:38:02 +01:00
Christoffer Lerno
9c1fb26660 Build 0.6.5 release. 2024-12-14 17:33:00 +01:00
Christoffer Lerno
3dd725a0f0 Crash when a constant null typeid is checked for properties. #1679 2024-12-14 15:13:43 +01:00
Christoffer Lerno
a8aad53038 It was possible to create 0 length arrays using byte literals. #1678 2024-12-14 02:49:45 +01:00
konimarti
68c60f58c0 math: fix adjoint of Matrix2 (#1676)
* math: fix adjoint of Matrix2

Fix the adjoint of the Matrix2x2 implementation in the math module. This
also fixes the calculation of the inverse which depends on the adjoint.

* update release notes
2024-12-13 11:16:47 +01:00
Radek Micek
c9c3f33acc Fix weekday 2024-12-12 22:30:23 +01:00
Christoffer Lerno
5ffc5187eb Update Win LLVM library. 2024-12-12 21:52:13 +01:00
Christoffer Lerno
5d31cdfa16 Incorrect no-libc definition of cos, making it unavailable for wasm. 2024-12-12 21:50:56 +01:00
Christoffer Lerno
e8ff4af5b9 Fix test for LLVM 20 2024-12-11 23:05:57 +01:00
Christoffer Lerno
723e1dd9a6 Fix issue with overloaded *= etc 2024-12-11 20:56:11 +01:00
Christoffer Lerno
369a4558a3 Remove mention of install_win_reqs.bat 2024-12-11 13:57:03 +01:00
Christoffer Lerno
5e6a3d9d8e Fix tabs in readme. 2024-12-10 23:14:20 +01:00
Koni Marti
62dca4f1c5 math: add gcd and lcm
Add gcd and lcm functions to calculate the greatest common divisor (gcd)
and the least common multiple (lcm) to the math module. This will also
work for BigInts that implements its own gcd/lcm.
2024-12-10 15:44:52 +01:00
Christoffer Lerno
061c02306f Cast removing arbitrary array indices and converting them to pointers should always be fine #1664 2024-12-08 18:58:43 +01:00
Christoffer Lerno
f006b05010 Fix issue with accessing arrays in access-overloaded types, e.g. list[1][2] #1665. 2024-12-08 18:04:29 +01:00
Koni Marti
c5a727aa9b math: update complex numbers
Add inverse, conjugate, and equals functions to the Complex numbers. Add
an IMAGINARY constant to represent the imaginary unit. Also, add unit
tests for different types.
2024-12-08 16:51:44 +01:00
konimarti
e67e9d3bbf Fix fnv a hashes (#1667)
* fix fnv32a

* fix fnv64a

* Simplify code

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2024-12-07 15:39:45 +01:00
Christoffer Lerno
ea86c9d37a Fix of issue where multiple methods were accepted for the same type. Fix of issue where a method was linked to a type alias instead of the underlying type. #1661 2024-12-06 13:09:47 +01:00
Christoffer Lerno
d1a2e6e5bd Update Windows debug output. 2024-12-06 02:18:36 +01:00
konimarti
c96985f1db sort: add is_sorted (#1660)
* sort: add is_sorted
Add is_sorted function to check whether a list is sorted or not. Sort
order (ascending or descending) will be detected by looking at the data.
Add tests.
* update the release notes
* refactor: use lambda
2024-12-05 22:37:13 +01:00
Christoffer Lerno
b7010c83e0 Fix windows emit loc. 2024-12-04 21:33:03 +01:00
Christoffer Lerno
20a3d19ac7 Update Windows build to use LLVM 19.1.4 2024-12-04 14:00:18 +01:00
Christoffer Lerno
0ded93ab9b Do not produce expression locations for windows. 2024-12-04 12:21:12 +01:00
Christoffer Lerno
ec82ec0426 Fix CI. 2024-12-04 00:02:36 +01:00
Christoffer Lerno
6281f8ff89 Add -q option, make --run-once implicitly -q.
Add `-v`, `-vv` and `-vvv` options for increasing verbosity, replacing debug-log and debug-stats options. #1601
2024-12-03 23:37:31 +01:00
Koni Marti
2c9d2d4fd7 update the release notes 2024-12-03 21:21:38 +01:00
Christoffer Lerno
8569239bc1 Crash when using --no-obj without compile-only. #1653 2024-12-03 19:48:24 +01:00
konimarti
5463c398cb Add quickselect (#1654)
* sort: extract partition from quicksort

Extract the partition logic from quicksort into a macro. This allows to
reuse the partition logic for, e.g., the quickselect algorithm.

* sort: implement quickselect

implement Hoare's selection algorithm (quickselect) on the basis of the
already implemented quicksort. Quickselect allows to find the kth
smallest element in a unordered list with an average time complexity of
O(N) (worst case: O(N^2)).

* add quicksort benchmark

Create a top-level benchmarks folder. Add the benchmark implementation
for the quicksort algorithm.

Benchmarks can then be run in the same way as unit tests from the
root folder with:

	c3c compile-benchmarks benchmarks/stdlib/sort
2024-12-03 19:27:26 +01:00
Nexus
7381734913 fix: prevent infinite read-loop by updating left_to_read after write (#1652)
* fix not updated `left_to_read` after the buffer has been written
2024-12-02 14:34:57 +01:00
Christoffer Lerno
462322026f Fix bug with missing target in test and crash in benchmark. Note that this doesn't resolve the issues with these yet. 2024-11-30 20:26:04 +01:00
Christoffer Lerno
b5e5c719ed Enforce single module compilation for static libraries to make constructors run properly. 2024-11-30 15:34:54 +01:00
neokeld
a0f4976b07 Add char_at method in DString 2024-11-30 13:30:20 +01:00
Christoffer Lerno
44c2486a74 Update test for LLVM 20 2024-11-30 12:53:58 +01:00
Christoffer Lerno
5fc6672784 Crash compiling for arm64 when returning 16 byte and smaller structs by value not a power of 2 #1649. 2024-11-30 11:47:49 +01:00
Christoffer Lerno
bcb1edba90 Update tests. 2024-11-28 23:32:34 +01:00
Christoffer Lerno
8099e7a75d Update LLVM debug info. 2024-11-28 21:59:20 +01:00
Christoffer Lerno
cc9a501351 Fix bug preventing optionals from being used in ranges or as indices. 2024-11-28 00:48:58 +01:00
Christoffer Lerno
b536a23124 Updated release notes. 2024-11-27 13:46:39 +01:00
Christoffer Lerno
6ca5bcc6b8 Add simple memcpy, memcmp and memset functions for nolibc. 2024-11-27 13:45:41 +01:00
Christoffer Lerno
ac966f118a Updated base32 / base64 API. 2024-11-27 11:58:28 +01:00
Christoffer Lerno
f13472a8c3 Contracts on generic modules would evaluate too late, sometimes not catching the error until it already occurred elsewhere. Add file::save. 2024-11-27 00:02:43 +01:00
Christoffer Lerno
0e213ae777 Disable report heap allocs using parameter. 2024-11-26 03:11:10 +01:00
Christoffer Lerno
a0c82a6a47 Updated base32 API. 2024-11-26 03:01:45 +01:00
Christoffer Lerno
a087ba608b Begin unifying baseXX encodings. b64 / hex data strings can now be used with \` as well. 2024-11-25 16:20:10 +01:00
Tim Jurcka
9112d63655 Fix args passed to __asan_region_is_poisoned 2024-11-25 11:44:39 +01:00
Koni Marti
3f7f7a0aa7 base64: use url encoding with updated api
Ensure that the URL alphabet for base64 is used with the urlencode
functions (urlencode, urlencode_buffer, urlencode_temp and
urlencode_new) are used. Add a new test.
2024-11-25 11:44:24 +01:00
Koni Marti
8d03aafe72 base32: update base32 api
Update the base32 api to be consistent with the recent changes to the
base64 api introduced by commit 60101830 ("Updated base64 encoding
api").
2024-11-25 11:43:40 +01:00
Koni Marti
b0c0fd7dc8 encoding: implement hex encoding (base16)
Implement hex encoding and decoding (base16) according to RFC 4648.
Add unit tests.

Link: https://www.rfc-editor.org/rfc/rfc4648
2024-11-25 11:41:22 +01:00
Nexus
c273f26cb3 Add "sources" option support for library. (#1631)
* Add "sources" support for library manifest
* Add "sources" to library manifest creation
* Add "sources" key to target manifest
* Added fallback for already made libraries
* Remove src/ in library creation
* add changes to releasenotes.md
2024-11-24 15:37:15 +01:00
Christoffer Lerno
60101830cc Updated base64 encoding api. 2024-11-24 00:14:31 +01:00
Ellipse12
a58d782704 added check for to_string in is_struct_with_default_print
The function wasn't checking if the struct had the method `to_string` which made it segfault when trying to override the default to_string on a struct
2024-11-23 23:08:45 +01:00
Koni Marti
9b94c1dda9 fix: base64 decoding
Fix the base64 decoding. If there's an 'A' character in the encoded
text, the base64 decode function returns an INVALID_PADDING error. The
reason lies in the way Base64Decoder.init tries to find a suitable
invalid character. Fix this by defining the invalid character as 0xff
(which is already the case for a decoding without padding).

This error has not been caught by the test harness, because no test
contains an 'A' character in the the encoded text yet. Add a new test.
2024-11-23 23:06:44 +01:00
Christoffer Lerno
201a6b350e Support MSVCRT and OLDNAMES.lib in python script. 2024-11-23 18:54:27 +01:00
Christoffer Lerno
b2724caeda Begin work on asm label support. 2024-11-23 17:10:42 +01:00
Sander van den Bosch
9d99d556a1 Add .tlb file extention from msvc files to .gitignore 2024-11-22 22:37:38 +01:00
Christoffer Lerno
a1a6511e26 Remove "Timespec" 2024-11-22 16:50:29 +01:00
Christoffer Lerno
652456646f Prevent methods from using names of properties or fields. #1638 2024-11-22 16:40:33 +01:00
Christoffer Lerno
ca0dc49f64 Improve support for Windows cross compilation on targets with case sensitive file systems. 2024-11-21 23:28:58 +01:00
Christoffer Lerno
ae1b39eb60 Not possible to alias or take reference for extension methods on non-user defined types. #1637 2024-11-21 14:48:13 +01:00
Christoffer Lerno
22f7faf60e SimpleHeapAllocator bug when splitting blocks allowed memory overrun. 2024-11-21 13:36:24 +01:00
Christoffer Lerno
f3bf9eb14d Update mingw packages. 2024-11-21 11:31:55 +01:00
Christoffer Lerno
347a1a48d4 Indexing an Optional slice would crash in codegen #1636. 2024-11-21 11:30:53 +01:00
Christoffer Lerno
c9793457f3 Fix issue with properties in different targets not being respected. #1633 2024-11-21 01:24:44 +01:00
Christoffer Lerno
50d31ba398 Fix issue with overloaded subscript and ++/--. 2024-11-20 23:44:42 +01:00
Sander van den Bosch
2788c4cc00 Add new AMX and other feature flags 2024-11-20 23:43:30 +01:00
Christoffer Lerno
ba54232b8d Fix issue with overloaded subscript and ++/--. 2024-11-20 00:23:08 +01:00
Christoffer Lerno
489bb70901 Updated cast rules 2024-11-19 00:04:10 +01:00
Christoffer Lerno
dd06dfa5ba Fix issue with resolved try-unwrap in defer. 2024-11-18 15:53:27 +01:00
Walther Chen
f39e339726 Fix error when HashMap.remove on uninitialized HashMap (#1629)
* HashMap: test removal on uninitialized

* HashMap.remove_entry_for_key: return false on unintialized

* test: switch to temp_init

* release note
2024-11-18 14:20:32 +01:00
Christoffer Lerno
295b374b48 Support &a[0] returning the distinct type when applying it to a distinct of a pointer. 2024-11-17 22:25:57 +01:00
Christoffer Lerno
8ed390c394 A distinct inline pointer type can now participate in pointer arithmetics. 2024-11-16 23:08:54 +01:00
Christoffer Lerno
f9e9cac6e8 Cleanup and better contract error messages. 2024-11-16 00:02:03 +01:00
konimarti
f3304acc93 Add io stream primitives (#1626)
* io: implement MultiReader struct

Implement a MultiReader (InStream) which sequentially read from the
provided readers (InStreams). Return IoError.EOF when all of the readers
are read.

* io: implement MultiWriter struct

Implement a MultiWriter (OutStream). The MultiWriter duplicates its
writes to all the provided writers (OutStream).

* io: implement TeeReader struct

Implement a TeeReader (InStream) which reads from a wrapped reader
(InStream) and writes data to the provided writer (OutStream).
2024-11-15 23:18:29 +01:00
Walther Chen
a233771433 Fix WriteBuffer.write_bytes off-by-one (#1625)
* fix WriteBuffer.write_bytes off-by-one

* test for WriteBuffer.write_bytes off-by-one
2024-11-14 14:58:09 +01:00
Christoffer Lerno
ea9a871d90 Fix incorrect doc contracts on interfaces. 2024-11-14 11:47:00 +01:00
Christoffer Lerno
84d010bb2f Remove accidental doc comment. 2024-11-14 11:14:02 +01:00
Christoffer Lerno
e0ba468b7e Update mingw libs. 2024-11-14 01:26:29 +01:00
Christoffer Lerno
f88c0dd645 Tweak the error message on unexpectedly getting a non-type identifier. #1622 2024-11-14 00:19:17 +01:00
Walther Chen
758918c077 fix WriteBuffer.write_byte 2024-11-14 00:06:54 +01:00
Christoffer Lerno
7b516e6113 @builtin was not respected for generic modules #1617. 2024-11-13 23:34:34 +01:00
Matteo Cardinaletti
61a76bb834 Init command will now add test-sources to project.json #1520 2024-11-12 15:17:09 +01:00
vssukharev
e6b6edefaf Add support for nix flakes (#1614)
* Add support for nix flakes
* Added debug build type for flake.nix; Got rid of redundant version check in nix/default.nix
* Added dev shell with compile_commands.json into flake.nix
* Fixed issue with generated compile_commands.json while creating c3c derivation with nix. Deduced devShells in flake.nix to nix/shell.nix
2024-11-12 12:33:39 +01:00
Christoffer Lerno
a228eb020d Allow splat in initializers. 2024-11-11 23:54:35 +01:00
Christoffer Lerno
c46933a81a Refactor "splat" parsing. 2024-11-11 15:43:17 +01:00
Christoffer Lerno
746046c8c0 Fix bug where a > 0 ? f() : g() could cause a compiler crash if both returned void!. 2024-11-10 01:18:56 +01:00
Christoffer Lerno
acab95792f Improve error message when incorrectly using Type as an rvalue. 2024-11-10 01:18:56 +01:00
Christoffer Lerno
b882265e52 Start work on 0.6.5 2024-11-10 01:18:56 +01:00
Christoffer Lerno
547f2ef189 Tighten up conversion rules for arrays and slices. 2024-11-10 01:16:01 +01:00
Christoffer Lerno
69004943a7 Update to 0.6.4. 2024-11-09 17:09:54 +01:00
Christoffer Lerno
658fb4b1f9 Updated for 0.6.4 release. 2024-11-08 12:38:23 +01:00
Christoffer Lerno
e675b0c00a Fix last test for LLVM20 2024-11-07 13:49:03 +01:00
Christoffer Lerno
95ac29559c Fix some tests for LLVM20 2024-11-07 12:43:26 +01:00
konimarti
b7a095b4b4 Fix Formatter.floatformat and Object.to_format (#1602)
* fix: change float format specifier in Object.to_format

Fix the float format specifier in Object.to_format. If there is a float
stored in a Object such as 3.14, it would be printed out as 3 because
the format specifier is %d but should be %g.

* fix: print nan in floatformat

Fix floatformat to print 'nan' if float is nan. Currently,
io::printn(float.nan) will produce 'inf' instead of 'nan'.
2024-11-07 00:01:14 +01:00
Christoffer Lerno
08d1b29301 Add contracts on @catch / @ok. Taking the $typeof of a wildcard optional returns void! 2024-11-05 10:50:38 +01:00
Christoffer Lerno
741707273d Add "prepare" target in project.json #1577 2024-11-04 22:46:19 +01:00
Tomas Kallup
bdd6ed0e83 Fix: Unify termios types for actions & flags
The type for `Tcflags` was used instead of the CInt (now `Tcactions`)
and vice versa.
2024-11-04 17:15:40 +01:00
konimarti
8154e275fa encoding: implement RFC4648 base32 encoding (#1596)
Implement base32 encoding and decoding according to RFC 4648 with the
standard and extended hex alphabets. Add unit tests.

Link: https://www.rfc-editor.org/rfc/rfc4648
Signed-off-by: Koni Marti <koni.marti@gmail.com>
---------

Signed-off-by: Koni Marti <koni.marti@gmail.com>
2024-11-04 12:19:28 +01:00
Christoffer Lerno
6258cba79a @tag on macros cannot be retrieved with tagof #1582 2024-11-04 02:51:58 +01:00
Waqar Ahmed
213831289a Fix compile with gcc 14.2.1 (#1594)
* Fix compile with gcc 14.2.1 Fixes -Werror=maybe-uninitialized warnings
* Updated to use INVALID_PTR rather than NULL.
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2024-11-04 00:20:14 +01:00
Christoffer Lerno
ba34b45651 Fix LLVM 20 compilation 2024-11-03 23:52:48 +01:00
rexim
4be5c74798 Relative DESTINATION path when installing man page
This makes the install() function install the file with respect to
`CMAKE_INSTALL_PREFIX` which may not always be `/usr/local/` especially
when some people install the compiler locally in their `$HOME` folder.

https://cmake.org/cmake/help/latest/command/install.html
2024-11-03 23:20:15 +01:00
Christoffer Lerno
b06a611e69 Improve error message when using void aliases as variable storage type. 2024-11-01 14:56:29 +01:00
Christoffer Lerno
fd5b8d1374 Add bounds checking on List access. 2024-11-01 14:18:29 +01:00
Christoffer Lerno
4d84811629 Fixed test. 2024-10-31 21:41:50 +01:00
Christoffer Lerno
cd4fd02ee3 Sometimes generating introspection info would not be in the global scope causing a crash #1586. 2024-10-31 21:27:46 +01:00
Ygor Pontelo
475972aecd allow current directory as project (#1580)
* allow current directory as project
* avoid multiple underscores in module name

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2024-10-30 21:23:51 +01:00
Christoffer Lerno
b7a23e558a Fixes with error handling recursive @tag #1583. 2024-10-30 20:57:17 +01:00
Christoffer Lerno
827440686f $define would occasionally not properly evaluate declarations it encountered. 2024-10-30 11:59:18 +01:00
aiko
7f70145f55 wrote manpage in c3c.1 and added manpage install to CMakeLists.txt 2024-10-28 22:37:56 +01:00
MohMaGen
0639659270 fix: remove requirements for pop_first. 2024-10-27 20:35:09 +01:00
Christoffer Lerno
4be08ee0bd string::new_struct_to_str and io::struct_to_format to dump struct data. io::print will now print structs. 2024-10-26 21:00:07 +02:00
Christoffer Lerno
cc6a24cf80 Null-check function pointer invocation #1573. 2024-10-26 18:41:47 +02:00
Christoffer Lerno
b187d5a3fc Named vector component access would not fold at compile time. #1574 2024-10-26 13:22:35 +02:00
Christoffer Lerno
83f8d24892 Update sponsors. More comments in path.c3 2024-10-26 13:00:54 +02:00
Walther Chen
fd1898b70a copy out keys also in HashMap.copy_keys (#1569)
* copy out keys also in HashMap.copy_keys
* test for copying keys out
2024-10-26 01:57:00 +02:00
Walther Chen
8ce63106b9 Update .editorconfig to reflect current style (#1571)
* Update .editorconfig to reflect current style
* .editorconfig use tabs for python
* update msvc_build_libraries.py from two-space to tab indent
* update test/src/tester.py from 4-space to tab indent
2024-10-25 17:44:32 +02:00
Christoffer Lerno
c0c571ffe0 Incorrect error message when $eval is provided an invalid string. #1570 2024-10-25 10:31:45 +02:00
Christoffer Lerno
d8f4da4d90 Incorporating change from #1568 and the other fix needed. 2024-10-23 23:48:27 +02:00
Matteo Cardinaletti
f02387073d Json AST: Adding 'constants' and Improving 'globals' #1540, #1541 2024-10-23 23:31:28 +02:00
Christoffer Lerno
d344cc6020 (uptr)&((Foo*)null).a incorrectly inserts a null check. #1544.
Fix regression handling typedefs from generic modules.
2024-10-22 12:15:39 +02:00
Christoffer Lerno
9100638400 &self argument not implicitly null checked. #1556. 2024-10-21 00:45:40 +02:00
Christoffer Lerno
d2085654a7 if (try foo) was handled incorrectly inside a defer. 2024-10-20 18:30:03 +02:00
Christoffer Lerno
78d5939d9d Show error when declarations do not start with fn in interfaces. #1565. Some added functionality to lists and time. 2024-10-18 17:10:00 +02:00
Christoffer Lerno
c013006671 Improve infer conversions on constants, e.g. ZString a = foo ? "a" : "b"; #1561 2024-10-17 01:20:52 +02:00
Fernando López Guevara
e09a9f0d80 chore(cmake): improve lld resolution on apple 2024-10-17 00:46:16 +02:00
Christoffer Lerno
705856d51a - Disallow casting a void* to any or an interface, unless it is null.
- Defer resolution of declarations when looked up in `def` aliased #1559.
2024-10-16 12:50:47 +02:00
Christoffer Lerno
4445b6c054 Improve error messages on expressions like var $type = int; #1553. 2024-10-15 11:50:15 +02:00
Christoffer Lerno
cf03bc0a0a Formatting fixes 2024-10-14 16:13:51 +02:00
Christoffer Lerno
f37f1769ae c3.l grammar fixed. 2024-10-14 12:37:53 +02:00
Christoffer Lerno
31cd839063 Switch to <* *> docs. Fix issue with dynamically loaded C3 libs with other C3 code. 2024-10-14 02:14:34 +02:00
Christoffer Lerno
9f6a4eb300 Empty expression block would crash compiler with debug on #1554. 2024-10-13 02:19:26 +02:00
PavelBlinnikov
6a2957faf7 fix: segfault in panic when statically compiled
Co-authored-by: RoadToLP <ilya.titoff2013@yandex.ru>
2024-10-12 22:18:38 +02:00
Walther Chen
e6c9cfed42 Small fixes to AST json (#1550)
* fix ast json

* INERT_COMMA -> INSERT_COMMA

* fix struct field types
2024-10-12 19:28:47 +02:00
Christoffer Lerno
0da7f1b4de Wrong error message for interface methods with body #1536. 2024-10-12 02:51:07 +02:00
Christoffer Lerno
8ce171877e Also weaken libc and generic templates. 2024-10-12 02:13:35 +02:00
Christoffer Lerno
a38c7f6e49 Fix tests. 2024-10-12 01:19:15 +02:00
Christoffer Lerno
d19f628c73 Return missing MSVC fix. 2024-10-12 00:05:50 +02:00
Christoffer Lerno
526d15b804 Stop using linkonce and use weak instead. 2024-10-11 23:56:29 +02:00
Christoffer Lerno
c308397ed6 Fix weak linking for windows. 2024-10-11 23:35:12 +02:00
Christoffer Lerno
0cf93a8c32 Fix test. 2024-10-11 22:13:53 +02:00
Christoffer Lerno
cba25710fe Refactor and unify linker visibility and fix issues with weak stdlib. 2024-10-11 21:45:44 +02:00
Christoffer Lerno
1b471283c9 - Don't weakly link in exe. 2024-10-11 18:11:27 +02:00
Christoffer Lerno
09fee2aa4b - Standard library is now correctly weakly linked, fixing the use of C3 .so together with executable. #1549, #1107. 2024-10-11 17:14:02 +02:00
Koni Marti
2739c86881 Add csv unit tests
Add unit tests for std::encoding::csv.

Signed-off-by: Koni Marti <koni.marti@gmail.com>
2024-10-11 16:11:45 +02:00
Christoffer Lerno
cdf67684cc Add releasenote for latest fix. 2024-10-11 13:53:29 +02:00
Koni Marti
4e5ba327fe Fix mem leak in HashMap.keys_new_list
Fix a memory leak in HashMap.key_new_list(). The custom memory allocator
will not be used, since key_new_list() will call HashMap.copy_keys()
without passing the memory allocator along. Hence, HashMap.copy_keys()
will allocate on the heap and these memory blocks will not be freed.

To fix this, pass the custom allocator to HashMap.copy_keys(). Also,
since HashMap.key_new_list() is deprecated anyways, replace it by
HashMap.copy_keys().

Affected from this leak is Object.to_format() from
std::collection::object (for an ObjectInternalMap) which is used in the
JSON parser.

The tests for the JSON parser show the memory leak:

$ c3c compile-test test/unit/stdlib/encoding
$ valgrind --leak-check=yes ./testrun
==1454708==
==1454708== HEAP SUMMARY:
==1454708==     in use at exit: 384 bytes in 8 blocks
==1454708==   total heap usage: 69 allocs, 61 frees, 528,672 bytes allocated
==1454708==
==1454708== 48 bytes in 1 blocks are definitely lost in loss record 1 of 8
==1454708==    at 0x48447A8: malloc (vg_replace_malloc.c:446)
==1454708==    by 0x12CDBF: std.core.mem.allocator.LibcAllocator.acquire (libc_allocator.c3:42)
==1454708==    by 0x1790FD: malloc_try (mem_allocator.c3:64)
==1454708==    by 0x1790FD: alloc_array_try (mem_allocator.c3:286)
==1454708==    by 0x1790FD: alloc_array (mem_allocator.c3:269)
==1454708==    by 0x1790FD: copy_keys (hashmap.c3:310)
==1454708==    by 0x1790FD: std_collections_map$String$p$std.collections.object.Object$.HashMap.key
==1454708==    by 0x14D593: std.collections.object.Object.to_format (object.c3:53)
==1454708==    by 0x164556: std.io.Formatter.print_with_function (formatter.c3:86)
==1454708==    by 0x165B49: std.io.Formatter.out_str (formatter.c3:152)
==1454708==    by 0x16E2B0: std.io.Formatter.vprintf (formatter.c3:456)
==1454708==    by 0x12696B: std.core.dstring.DString.appendf (dstring.c3:532)
==1454708==    by 0x124EA9: std.core.string.tformat (string.c3:79)
==1454708==    by 0x113C79: json_test.test_string (json.c3:34)
==1454708==    by 0x118AA1: std.core.runtime.run_tests (runtime.c3:227)
==1454708==    by 0x1190B1: std.core.runtime.default_test_runner (runtime.c3:246)
==1454708==

[..snip..]

==1454708==
==1454708== LEAK SUMMARY:
==1454708==    definitely lost: 384 bytes in 8 blocks
==1454708==    indirectly lost: 0 bytes in 0 blocks
==1454708==      possibly lost: 0 bytes in 0 blocks
==1454708==    still reachable: 0 bytes in 0 blocks
==1454708==         suppressed: 0 bytes in 0 blocks
==1454708==
==1454708== For lists of detected and suppressed errors, rerun with: -s
==1454708== ERROR SUMMARY: 8 errors from 8 contexts (suppressed: 0 from 0)

Signed-off-by: Koni Marti <koni.marti@gmail.com>
2024-10-11 13:52:34 +02:00
Christoffer Lerno
efeb9e627e Interfaces not correctly copied with generics #1545. 2024-10-11 13:26:22 +02:00
Christoffer Lerno
8e24f15d58 Cannot use void as a generic parameter #1546. Interfaces now support .ptr and .type directly without casting to any. 2024-10-11 12:10:35 +02:00
Christoffer Lerno
1adad860f4 Update AST output. 2024-10-11 11:24:01 +02:00
Christoffer Lerno
cf07570871 Improved error message when declaring a variable void!. 2024-10-11 00:15:49 +02:00
Christoffer Lerno
bf30e52993 Improved error message on invalid subscript index type #1535. 2024-10-10 14:59:16 +02:00
Christoffer Lerno
a91ddd40dd Infer now works across ternary. Crash returning struct or vector from function using ternary expression #1537. 2024-10-10 14:44:40 +02:00
Christoffer Lerno
57ecadd23e Updated CSV API 2024-10-10 14:14:24 +02:00
Christoffer Lerno
967f14148f Bug when defers and $if were combined in a macro, which would cause miscompilation. 2024-10-10 13:42:12 +02:00
Christoffer Lerno
7a6544b17c Constant bytes <=> char[] conversion should work #1514 2024-10-09 19:38:06 +02:00
Christoffer Lerno
bf59efd3f4 Fix sincos to be different on Darwin compared to other. 2024-10-09 16:14:24 +02:00
Christoffer Lerno
c1266e9d06 Compiler error when any/interface initialized using {} #1533. 2024-10-09 13:12:40 +02:00
Christoffer Lerno
557adb6ed9 Update the init function of the arena allocator. 2024-10-09 12:45:50 +02:00
Christoffer Lerno
9f10996ac7 Bug when a continue is copied in a defer. 2024-10-08 22:13:30 +02:00
Christoffer Lerno
31f48829b0 Added CBool #1530. 2024-10-08 22:00:06 +02:00
Christoffer Lerno
39d4a97e24 Fix broken sincos function. 2024-10-08 20:34:41 +02:00
Christoffer Lerno
a665978b64 Fixing some whitespace issues. 2024-10-08 19:38:31 +02:00
Koni Marti
0cc62058a9 fix(string): use heap allocator for ZString.copy
Use the heap allocator for ZString.copy() instead of the temp allocator
to stay consistent across the code base.

The temp allocator is used for ZString.tcopy().
2024-10-08 17:33:29 +02:00
Christoffer Lerno
7dfdb9d061 Fix to ad hoc generic types. 2024-10-08 11:48:20 +02:00
Christoffer Lerno
e3ea1d5049 Deprecate @adhoc, allow non-nested ad hoc generic types. 2024-10-08 11:02:10 +02:00
Christoffer Lerno
19a96acac8 Improve error message in the case of MyInterface x = foo; #1522 2024-10-07 21:00:17 +02:00
Christoffer Lerno
80d016e076 Unintended deref of pointers with methods caused regression with hash function. 2024-10-07 20:43:37 +02:00
Fernando López Guevara
ac214b97df chore(cmake): switch from LLVM_LIBRARY_DIRS (list) to LLVM_LIBRARY_DIR for single path usage 2024-10-07 19:12:05 +02:00
Fernando López Guevara
1a948e4341 fix(time): update month 2024-10-07 19:11:14 +02:00
Christoffer Lerno
6bbc77a69c Segfault with passing a program with - using stdin. Using no module with - would reject the program. #1523 2024-10-06 12:19:13 +02:00
Denis Palashevskii
217151be8d fix int formatting in std::collections::object 2024-10-05 14:03:27 +02:00
Christoffer Lerno
2ef1465244 Improved ObjC support. 2024-10-04 23:25:51 +02:00
Christoffer Lerno
cfc1d0d8f8 Incorrect subscript resolution #1519 2024-10-04 20:50:48 +02:00
Christoffer Lerno
6fabecac1a Better error for int Foo(int a) declarations #1516 2024-10-04 18:15:06 +02:00
Christoffer Lerno
77ac864995 Unexpected compile error using a typed constant with copysign #1517 2024-10-04 18:01:57 +02:00
Christoffer Lerno
f95769541d Better error for int a[4] = .... #1518 2024-10-04 17:52:32 +02:00
Christoffer Lerno
fa4ca7944f - Add read/write to stream with big endian ints.
- Move accidently hidden "wrap_bytes".
2024-10-03 20:42:25 +02:00
Christoffer Lerno
02e9bfaf31 Separate const slice. Fix #1489. Fix const slice appending. Remove unintended print of char[] as String. Support const conversion of array -> slice. 2024-10-03 15:04:33 +02:00
Christoffer Lerno
7f66d5992f Update latest version. 2024-10-03 13:10:25 +02:00
Christoffer Lerno
84e10cf635 0.6.3 Release version. 2024-10-03 10:11:31 +02:00
Christoffer Lerno
1b8f8c5f5a Compiler crash when compiling c code in a library without --obj-out #1503. 2024-10-03 00:56:01 +02:00
Fernando López Guevara
131a783e89 feat(hash): added test for sha256 2024-10-02 16:55:17 +02:00
chri-k
2233f24c8f Add variants of DString.insert_at to match .append (#1510) 2024-10-02 10:22:59 +02:00
Christoffer Lerno
607a625641 Updated bigint. 2024-10-02 01:13:34 +02:00
Christoffer Lerno
9b49d19224 DString reverse and an initial BigInt implementation (untested), 2024-10-01 22:51:48 +02:00
Christoffer Lerno
46ae4353e0 Assume XTensa in 21+ instead. 2024-10-01 16:06:23 +02:00
Christoffer Lerno
44fcba2e3a Remove LLVM 20 2024-10-01 15:58:23 +02:00
Christoffer Lerno
f434795ee5 Test enable 19 and 20 2024-10-01 15:51:44 +02:00
Christoffer Lerno
0ea423d022 Foreach over distinct iterable would ignore operator(len). 2024-10-01 13:00:54 +02:00
Fernando López Guevara
c9b9de2838 fix(string): remove allocator argument on temp_ascii_to_lower 2024-09-30 22:01:44 +02:00
Christoffer Lerno
5918d5120f Foreach over distinct pointer failed to be caught as error #1506. 2024-09-30 21:32:33 +02:00
DanyDollaro
0d73f2fffa Added mutex tests (#1501)
* Added mutex tests. Add errorcheck in safe mode for Posix threads. Make non-recursive locks fail when used recursively on Windows. Fix thread pool tests. Simple locking count.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2024-09-30 20:57:16 +02:00
Christoffer Lerno
c8018c5543 Added generic PBKDF2 implementation. 2024-09-30 00:07:49 +02:00
Christoffer Lerno
071bd0ebf2 Fix bug when reading zip manifest, that would not return a zero terminated string. #1490 2024-09-29 21:14:03 +02:00
Christoffer Lerno
a00fce516e Added test and releasenotes for #1498. 2024-09-29 11:23:40 +02:00
chri-k
94abb3bd0c Fix escape sequence handling in encoding::json 2024-09-29 11:22:15 +02:00
Christoffer Lerno
2e94ea1a0d Improved error messages on Foo a = foo { 1 }; #1496 2024-09-28 23:53:31 +02:00
Christoffer Lerno
3b009e0b50 Added generic HMAC. 2024-09-28 23:28:11 +02:00
Christoffer Lerno
cc130e04dd Added MD5 and crypto::safe_compare. 2024-09-28 22:16:25 +02:00
Christoffer Lerno
2eca868540 Add UUID generation. 2024-09-28 20:58:03 +02:00
Christoffer Lerno
eeba5f020a Bad error on parameterized type without parameter #1495. 2024-09-28 19:44:57 +02:00
Christoffer Lerno
ded8bce8e6 Change no recursive import to use attribute. 2024-09-28 13:48:43 +02:00
Christoffer Lerno
8e7efaae99 ThreadPool is now adhoc available. 2024-09-28 13:28:39 +02:00
Alex Ling
fe9e434020 Fix incorrect to_gmt_offset result 2024-09-28 13:23:48 +02:00
Christoffer Lerno
8a0b0f5cf5 Disable fixed_pool where no threads are available. 2024-09-28 03:50:22 +02:00
Christoffer Lerno
5df321816b Unintended commit reverse. 2024-09-28 01:42:06 +02:00
Christoffer Lerno
7ff645c423 Free if broadcast fails. 2024-09-28 01:33:12 +02:00
Christoffer Lerno
93f290d57c Added a simple fixed threadpool which allocates. 2024-09-28 01:25:08 +02:00
Rachad ADEKAMBI
2146a76795 fix typo#1492 (#1493)
* fix typo#1492 
* Fix missing update in sema_decls

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2024-09-28 00:22:15 +02:00
chopsticks-user
b071e24d7e Minimal GL and GLFW bindings to render a triangle 2024-09-27 13:12:32 +02:00
Christoffer Lerno
a99e4b602a Error when slicing a struct with an inline array #1488. 2024-09-27 13:10:48 +02:00
Alex Ling
4cdea865f0 TzDateTime enhancements #1473 2024-09-26 12:32:05 +02:00
Christoffer Lerno
bf9ae2f0d3 Bad error message aliasing an ident with a path. #1481. 2024-09-25 21:41:40 +02:00
alex_s168
da2f958614 add x86 APX features (#1482)
add apx features Update cpu_detect.c3
2024-09-25 21:11:01 +02:00
Christoffer Lerno
b47201fe61 Fix handling of default target with libc. 2024-09-25 21:09:33 +02:00
Christoffer Lerno
a8932910d9 wasm32 / wasm64 targets are use-libc=no by default. 2024-09-25 21:01:00 +02:00
Christoffer Lerno
413877b59d Allow ^ suffix for non-recursive imports #1480. 2024-09-25 15:23:21 +02:00
Christoffer Lerno
da47588502 Make methods be available in earlier stages of analysis. Add @adhoc attribute to allow types with ad hoc generic declarations. 2024-09-25 14:26:49 +02:00
Christoffer Lerno
6f7ffbeb3c Add rand_in_range random function. Fix methodsof to apply to more types. Prevent methodsof in the wrong stage. 2024-09-25 00:18:11 +02:00
Christoffer Lerno
a258f2084f Allow specifying an import module using @wasm #1305. 2024-09-24 21:31:48 +02:00
Christoffer Lerno
d067a31ce6 Fix entropy 2024-09-24 20:15:10 +02:00
Christoffer Lerno
c6e4eee789 Added rnd function. 2024-09-24 19:02:03 +02:00
Christoffer Lerno
07c49f832e Safer seed of rand with WASM. 2024-09-24 18:38:11 +02:00
Christoffer Lerno
01b087238a Const initializer refactoring. Improve error on "Foo![]" #1477 2024-09-24 18:04:39 +02:00
Christoffer Lerno
d4832812ef Fix regression. 2024-09-23 14:13:55 +02:00
Christoffer Lerno
9c098fd79f Always flatten cont initializer inner type. 2024-09-23 02:51:53 +02:00
Christoffer Lerno
029d5e9068 Const initializer further cleanup. 2024-09-23 00:29:43 +02:00
Christoffer Lerno
f30486adf9 Const initializer cleanup. 2024-09-22 22:31:33 +02:00
Brian Sinquin
e21c337d3d Project fetch missing libraries (#1469)
Project fetch missing libs
2024-09-21 23:49:26 +02:00
Christoffer Lerno
f66f324e0e Suppor slicing of bytes. 2024-09-21 22:56:27 +02:00
Christoffer Lerno
885acdac24 Support compile time slicing of untyped lists. 2024-09-21 20:20:56 +02:00
Christoffer Lerno
ccb04c317c Fix bug due to enum associated values not being checked for liveness. 2024-09-21 18:05:20 +02:00
Christoffer Lerno
abbedeec4f Allow the "self" parameter to be $/# for macro methods. Fix bug when passing a type as a compile time value. 2024-09-21 15:55:39 +02:00
Christoffer Lerno
cdae3ec936 Some refactoring of the bitstruct representation. Correctly represent inner types. #1471 2024-09-21 13:43:52 +02:00
Christoffer Lerno
d727696830 Segfault using ternary with no assignment #1468. 2024-09-21 00:57:36 +02:00
Christoffer Lerno
2a9078a3b4 Also depend on Ubuntu20 2024-09-20 23:14:19 +02:00
Christoffer Lerno
ac479c7e40 llvm issue with try when bool is combined #1467 2024-09-20 20:15:44 +02:00
Christoffer Lerno
cda6ffea1e Slicing constant strings at compile time works. 2024-09-20 19:34:00 +02:00
Caleb-o
0900f401c0 Add Enum constraint to enummap
Fix typo in enumset require
2024-09-20 19:17:09 +02:00
Christoffer Lerno
d5a96ed637 Disabling LLVM 19 again. 2024-09-20 19:16:33 +02:00
Christoffer Lerno
8d9eff5297 Re-enable LLVM 19 2024-09-20 16:22:04 +02:00
Christoffer Lerno
19c1511901 Support linking .o files in compilation command. #1417 2024-09-20 16:21:29 +02:00
Christoffer Lerno
8e37e54645 Add env::COMPILER_BUILD_HASH and env::COMPILER_BUILD_DATE 2024-09-20 12:29:23 +02:00
Christoffer Lerno
c25645eab1 Add .gitkeep files to project subfolders. 2024-09-20 10:47:13 +02:00
Chandler
b9f7711f21 Update Arch Linux install instructions
c3c was recently added to the official Arch 'extra' repo. Updated the README to reflect this.
2024-09-20 00:40:58 +02:00
Christoffer Lerno
9447913de6 Use arena with JSON parser. Slightly altered output for json printing. 2024-09-20 00:39:10 +02:00
Brian Sinquin
8a9834cac0 Vendor-fetch download to lib directory specified in project.json (#1422) (#1441)
Vendor-fetch download default destination set to dependency-search-path in project.json Add fetched libraries in the project configuration file dependency entry.
2024-09-19 23:44:05 +02:00
Christoffer Lerno
2fec1c83a4 Enum attributes would be overwritten by enum value attributes. 2024-09-19 23:02:06 +02:00
Christoffer Lerno
ff36380ddf Allow user-defined attributes to have typed parameters. Folding a constant array of structs at compile time would cause an assert. 2024-09-19 22:21:29 +02:00
Christoffer Lerno
f2cfa61a39 User defined attributes could not have more than 1 parameter due to bug. 2024-09-19 21:14:08 +02:00
Christoffer Lerno
41156cc45d Temp allocator overwrites data when doing reset on extra allocated pages. #1462 2024-09-19 20:50:14 +02:00
Christoffer Lerno
9f51bfcc10 Support casting bitstructs to bool. 2024-09-19 01:03:06 +02:00
Christoffer Lerno
9426e813be Add test. 2024-09-18 23:16:07 +02:00
Christoffer Lerno
20fd7aba9b Regression when passing types as #expr arguments. #1461 2024-09-18 22:16:26 +02:00
Christoffer Lerno
3bada4560e Correctly print interfaces. 2024-09-18 14:53:55 +02:00
Christoffer Lerno
9719abe99a Better slice error message, and enable slice on non-vaarg. 2024-09-18 10:55:18 +02:00
Christoffer Lerno
5540519e52 Cleanup. 2024-09-18 10:20:57 +02:00
Christoffer Lerno
0b94e73c0b Regression fix arguments needed when presenting error on a method. 2024-09-18 10:07:39 +02:00
Christoffer Lerno
5e2a06bfd6 Update shell argument escape. 2024-09-18 00:48:39 +02:00
Christoffer Lerno
ac95e411bc Make str_eq safe to use with NULL. 2024-09-17 23:48:36 +02:00
Christoffer Lerno
08219fc57e Fix reordering semantics in struct assignment. 2024-09-17 22:41:49 +02:00
Josh Ring
09643f3c8b fix install instructions on ubuntu's latest LTS (#1449)
* fix install instructions on ubuntu's latest LTS
* cleanup arch linux and fixed some typos
2024-09-17 01:38:50 +02:00
Christoffer Lerno
08a575fa82 Crash invoking a @body argument with the wrong number of parameters. 2024-09-17 00:33:38 +02:00
Christoffer Lerno
62887a6ce8 Temporarily disable building with LLVM 19 and 20 2024-09-17 00:28:53 +02:00
Christoffer Lerno
297a6c9348 Support C3C_LIB and C3C_CC environment variables. Enable compiling against 20 to see if it works. 2024-09-17 00:12:50 +02:00
Christoffer Lerno
1181edc48e Fix error message when not finding a particular function 2024-09-16 22:43:30 +02:00
Christoffer Lerno
81f1930349 Code cleanup. Correct deprecation notice on '$or'. Allow "self" param on macro method to be constant. 2024-09-16 10:40:34 +02:00
RealPacket
54a1819d46 chore: Remove overrides for C3 files (#1450) 2024-09-16 10:21:41 +02:00
Christoffer Lerno
1b5472cc94 Add paramsof. 2024-09-15 23:43:09 +02:00
Christoffer Lerno
06a083bafc Lambda / function type would accidentally be processed as a method. 2024-09-15 22:12:03 +02:00
Christoffer Lerno
9bb45cb6a3 Add missing concat. Fix error message location on not enough arguments. 2024-09-15 15:56:13 +02:00
Real-Packet
1bfe9c568e chore(ci): Update actions/*-artifact to v4 (simple find & replace)
Because v3 is being deprecated and uses Node 16 instead of Node 20.
v4 is also a drop in replacement so we can just find & replace the usages without any changes to easily upgrade to v4.
2024-09-15 02:11:00 +02:00
Christoffer Lerno
4f5e5fcdba Remove mem tap. 2024-09-15 02:10:19 +02:00
Christoffer Lerno
bca44d1c14 Bug with casting anyfault to error. 2024-09-15 00:55:22 +02:00
Christoffer Lerno
e6d1d66c8f Updated grammar script and fix concat op 2024-09-14 23:13:06 +02:00
Christoffer Lerno
c3a5f5c0f0 Build macos version with LLVM 17 2024-09-14 22:18:33 +02:00
Christoffer Lerno
c0875d7987 Fix macos release. 2024-09-14 22:15:16 +02:00
Christoffer Lerno
6b6ac2bcb3 Rename release files. 2024-09-14 20:19:12 +02:00
Christoffer Lerno
f78466452a Updated grammar.y 2024-09-14 19:24:21 +02:00
Christoffer Lerno
f51230e793 Fix bugs in "trap-on-wrap" #1434. 2024-09-14 16:19:40 +02:00
Christoffer Lerno
3ab201ce10 Use atexit to fix finalizers on Windows #1361. 2024-09-14 16:17:57 +02:00
Christoffer Lerno
45a94cfe86 Asserts are now correctly included and traced in when running tests. Removed accidental debug trace. 2024-09-14 13:48:27 +02:00
Christoffer Lerno
f16cc999bd Fix bug where inline index access to array in a struct would crash the compiler. 2024-09-14 12:58:37 +02:00
Christoffer Lerno
d39f25efd3 Support inline struct designated init as if inline was anonymous. 2024-09-13 20:31:21 +02:00
Christoffer Lerno
6b2ce6de6f Fix unreachable. 2024-09-13 15:32:26 +02:00
Christoffer Lerno
3f1738e0fe Unified constant handling. 2024-09-13 15:11:15 +02:00
Real-Packet
3ceaf2ab81 chore: migrate to softprops/action-gh-release
Hopefully this works. Had to generate a new GPG key because GPG requires a password for signing stuff with GPG and I forgot to use it to sign something so I could save it in the password manager.
Fixes c3lang/c3c#1437
2024-09-13 15:07:45 +02:00
wilsonk
4c7d61ae82 Bsd family fixes (#1435)
Some small fixes for the BSD's
Try fcntl for NetBSD
Fixes for stdin, etc. and setjmp/longjmp
2024-09-13 14:49:51 +02:00
Alexey Kutepov
d53dd57b84 Introduce os::native_fputc() abstraction layer for File.write_byte() (#1440)
Introduce os::native_fputc() abstraction layer for File.write_byte()
2024-09-13 12:25:41 +02:00
Christoffer Lerno
6ff5ac5592 Removed unused functions. 2024-09-12 15:28:19 +02:00
Christoffer Lerno
9ce1bbe3cd Update README.md
Fix link
2024-09-12 14:02:10 +02:00
Christoffer Lerno
65c48419d0 Minor refactorings. Added "Thank you" section to readme. Some fixes to hostinfo. 2024-09-12 13:47:49 +02:00
alex_s168
d376ee6671 ability to disable llvm at compile time (#1433)
ability to disable llvm at compile time
2024-09-12 13:36:00 +02:00
Christoffer Lerno
eaa419a48d Correct '.so' suffix on dynamic libraries on Linux. 2024-09-12 09:21:23 +02:00
Christoffer Lerno
1b6ec34c61 Refactor alignment code. Change deprecated function in test. 2024-09-12 08:30:01 +02:00
Christoffer Lerno
9f4da339c3 Support int[*] { 1, 2, 3 } expressions. 2024-09-12 00:11:09 +02:00
wilsonk
1b54a99f6a Add initial FreeBSD support (#1430)
Add initial FreeBSD support
2024-09-11 22:38:53 +02:00
ElaDeCode
2b0d2892af move macro matrix_look_at to matrix module 2024-09-11 14:58:42 +02:00
Fernando López Guevara
27f2d201ed fix: cast native thread 2024-09-11 10:12:33 +02:00
Christoffer Lerno
d6cf622e49 Make subscript use its own "index" type rather than reuse Range. 2024-09-10 22:11:19 +02:00
Christoffer Lerno
2092e2167e Add io::read_new_fully for reading to the end of a stream. Add io::wrap_bytes for reading bytes with io functions. 2024-09-10 13:21:07 +02:00
Christoffer Lerno
503032cbcf Update range checking. 2024-09-10 13:21:07 +02:00
Christoffer Lerno
6f90e13502 Fix regression for $include. 2024-09-10 12:15:45 +02:00
Christoffer Lerno
b22bd459dd Fix regression for splat. 2024-09-10 00:21:01 +02:00
Christoffer Lerno
f67147a405 Fix bug in new splat code, fixes #1423. 2024-09-09 22:12:30 +02:00
Christoffer Lerno
df4eb3d0f0 Allow var in lambdas in macros. Allow ad hoc generic declaration in lambdas and type definitions. Fix deprecation flag. 2024-09-09 21:46:06 +02:00
Christoffer Lerno
32cc4bcd03 Fix issues for compiling on 32-bit. 2024-09-09 00:55:50 +02:00
Christoffer Lerno
1502c6d660 Limit object filename lengths. #1415 2024-09-07 23:38:20 +02:00
Christoffer Lerno
d4fb5b747b Update QOI type names. 2024-09-07 16:10:15 +02:00
Hema2
7581651011 Add QOI to the standard library (#1409)
Add QOI to the standard library
2024-09-07 15:55:26 +02:00
Christoffer Lerno
4f54e273ab Asserts are retained regardless of optimization when running tests. 2024-09-07 15:40:32 +02:00
Christoffer Lerno
1cc1b83b6f format functions are now functions and work better with splat. 2024-09-07 14:34:30 +02:00
Christoffer Lerno
8e9199f453 Untyped splat. 2024-09-07 14:26:42 +02:00
Christoffer Lerno
223501eeca Support splat for varargs #1352. 2024-09-07 05:26:43 +02:00
Christoffer Lerno
7649738618 Improve lvalue handling in the compiler. #1357 2024-09-07 03:19:35 +02:00
Christoffer Lerno
78c60ae695 Increase stack size for msys. 2024-09-07 01:37:45 +02:00
Christoffer Lerno
f5f122d5a5 Reduce recursion depth. Improve error message. 2024-09-07 00:48:16 +02:00
Christoffer Lerno
4b27a33a10 Refactor vasplat. 2024-09-07 00:29:41 +02:00
id3nom
15aca2eb84 Add CMake option C3_ENABLE_CLANGD_LSP (#1414)
* CMake option C3_ENABLE_CLANGD_LSP

The CMake option enables the generation of compile_commands.json in
the build directory and the creation of a symlink in the root
directory targeting the new file,
this will allow the Clangd Language Server Protocol (LSP) to
function properly.

* Added .editorconfig

EditorConfig helps maintain consistent coding styles:
https://editorconfig.org/
2024-09-06 23:06:09 +02:00
Christoffer Lerno
1cb91c0ac9 Fold default args in non-debug. 2024-09-06 23:04:09 +02:00
Christoffer Lerno
840b3b3161 "optsize" did not work correctly in project.json. 2024-09-06 22:55:15 +02:00
Christoffer Lerno
a5cf3ce2f1 Update releasenotes. 2024-09-06 20:56:38 +02:00
Lars Nilsson
04c85eb9ce Adding hashmap and map initialization functions with data (#1402)
Adding hashmap and map initialization functions with data to populate them with
2024-09-06 20:55:42 +02:00
Christoffer Lerno
82364d2e3c Function vasplat refactoring. 2024-09-06 20:54:28 +02:00
Christoffer Lerno
3db7bf5dfd Crash when reading an empty 'manifest.json'. 2024-09-06 18:05:43 +02:00
Christoffer Lerno
de13023981 Converting a slice to a vector/array would copy too little data. 2024-09-06 15:36:43 +02:00
Christoffer Lerno
35b825c78a Function vasplat refactoring. 2024-09-06 11:44:48 +02:00
Christoffer Lerno
28428fcf30 Handle "splice splat" in the vararg slot as an expression. 2024-09-06 10:43:03 +02:00
Christoffer Lerno
1e570bf506 Rename vec_erase_ptr_at to vec_erase_at. 2024-09-06 00:41:07 +02:00
Christoffer Lerno
ad0e97ab7b Deprecated inline generic types, deprecated tuple / triple types. 2024-09-05 23:42:20 +02:00
Christoffer Lerno
ed5d338a39 Added new style named arguments. 2024-09-05 22:13:22 +02:00
Christoffer Lerno
e795745e43 lvalue refactoring. 2024-09-05 22:09:35 +02:00
Christoffer Lerno
5e4d790fc3 Fixing incorrectly solved generic module name collision bug. 2024-09-04 21:51:03 +02:00
Christoffer Lerno
7e47f4ed08 Generic methods were incorrectly registered as functions, leading to naming collisions. #1402 2024-09-04 15:13:29 +02:00
Christoffer Lerno
63fc77a861 Move of const to separate file and removal of old concat code. 2024-09-04 09:34:51 +02:00
Christoffer Lerno
59ff94c005 Issue where a lambda wasn't correctly registered as external. #1408 2024-09-03 23:25:47 +02:00
Christoffer Lerno
bbc199cda3 Some cleanup of asm and assert 2024-09-03 13:53:15 +02:00
Christoffer Lerno
df91ee3d2a Update version. 2024-09-03 11:48:43 +02:00
Christoffer Lerno
528fecef4d Create release. 2024-09-02 23:21:01 +02:00
Christoffer Lerno
d39f1a6af0 Fix to test. 2024-09-02 22:53:54 +02:00
Christoffer Lerno
4367ef11fa Further fixing storeload. 2024-09-02 22:37:50 +02:00
Ikko Eltociear Ashimine
b8d77d2490 chore: update linux.c3
Recieve -> Receive
2024-09-02 11:07:34 +02:00
Christoffer Lerno
2600c3116c Do not add the libc allocator if it isn't available. 2024-09-02 01:44:35 +02:00
Christoffer Lerno
2506c2579b Prevent loading / storing large structs with LLVM. 2024-09-02 01:16:48 +02:00
Lars Nilsson
fe7392a656 Adding check of HTTP response code so that Windows will remove up the opened file if the download was not successful 2024-09-01 21:44:09 +02:00
Christoffer Lerno
8b7a3f1835 Optimize the value after foo()!! 2024-09-01 21:14:30 +02:00
Christoffer Lerno
e6acc56c1f Bug where if try would work incorrectly in a macro. 2024-08-31 23:09:36 +02:00
Christoffer Lerno
d635cfb90f printf will now show errors in the output when there are errors. 2024-08-31 19:36:18 +02:00
Christoffer Lerno
6cb6113c57 - Memory leak in Object when not using temp allocators.
- Tracking allocator would double the allocations in the report.
2024-08-31 03:35:39 +02:00
Alexey Kutepov
6aea0f12cd Properly persist git hash on each build (#1391)
Properly persist git hash on each build

Rebuild `git_hash.h` only when `.git` folder changes

`add_custom_target()` always considers its target out-of-date which
leads to rebuilding of `git_hash.h` on every build (which is
ironically what we wanted) and consequently rebuilding of
build_options.c and relinking of c3c even when no changes are made,
which is mildly annoying.

We are replacing `add_custom_target()` with `add_custom_command()`
which depends on `.git`, so `git_hash.h` is only rebuilt if any git
commands are performed. Which is less annoying.

In case of no `.git` we simply do not depend on it which leads to
`git_hash.h` being rebuilt only once.
2024-08-30 15:01:08 +02:00
Christoffer Lerno
99ace59b45 Create a build library on --test. 2024-08-30 14:55:35 +02:00
Christoffer Lerno
31f9ed3e6b Methods can now properly be aliased using def #1393. 2024-08-30 12:50:39 +02:00
Christoffer Lerno
573c0881e9 Correctly use wincrt setting for in libraries. 2024-08-29 23:53:14 +02:00
Christoffer Lerno
7134b3ba35 Update Raylib examples to use Raylib5. 2024-08-29 23:34:31 +02:00
Christoffer Lerno
bc267e22bd Add fmod implementation for nolibc. 2024-08-29 20:04:59 +02:00
Christoffer Lerno
3d83316b03 Regression: backtrace accidentally turned off by default. 2024-08-29 19:15:50 +02:00
Christoffer Lerno
dfe80eb050 Improve the error message when the compilation does not produce any files #1390. 2024-08-28 11:16:39 +02:00
Christoffer Lerno
22151a0a03 Fix bug with defer (catch err) when used together with regular defer. 2024-08-28 10:41:59 +02:00
rexim
484a9acc6f Print Git Hash on --version 2024-08-27 04:41:39 +02:00
Christoffer Lerno
26acce246d Fixed int128 div/mod. Fix WASM memory init priority. 2024-08-27 04:31:14 +02:00
Christoffer Lerno
388578c209 Too restrictive compile time checks for @const. Fixes to wasm nolibc in the standard library. 2024-08-26 13:33:15 +02:00
Christoffer Lerno
b33cce385c Fix of bug in defer (catch err) with a direct return error. 2024-08-26 11:49:41 +02:00
Christoffer Lerno
4b2019cf20 Add "allocator-required" functions. 2024-08-25 21:53:54 +02:00
Christoffer Lerno
61246d713d Print linking in CI and fix win linking. 2024-08-25 21:10:24 +02:00
Christoffer Lerno
40455f5260 Print linking in CI 2024-08-25 20:30:21 +02:00
Christoffer Lerno
78ce03bd62 Prefer \ to concat windows paths. 2024-08-25 20:14:36 +02:00
Christoffer Lerno
61645d14fa Only output -pie to the linker for executables. Fix issue assembling paths using concat_file_arg 2024-08-25 19:54:38 +02:00
Christoffer Lerno
d465ba5356 --test will now provide the full filename and the column. 2024-08-25 18:26:44 +02:00
Christoffer Lerno
8fde7cd6f5 --path is now properly respected. 2024-08-25 18:15:33 +02:00
Itzerr
734e0f350a Fixed gencontext_begin_module using wrong reloc_model. (#1384)
Fixed gencontext_begin_module using wrong reloc_model.
2024-08-25 18:08:33 +02:00
Chuck Benedict
e1bbab3831 RISCV: Correct auipc imm; clarify signed imm error; add imm negative t… (#1378)
RISCV: Correct auipc imm; claify signed imm error; add imm negative tests. Allow fitted int asm imm const in uints; add rv regs
2024-08-25 11:19:30 +02:00
Christoffer Lerno
a870881fff Allow "project.json5" to be used. 2024-08-25 00:19:08 +02:00
Christoffer Lerno
1ed3eab010 Assigning a const zero to an aliased distinct caused an error. 2024-08-24 14:37:27 +02:00
theunixer
dd16ecf63a Essential dependency to build(llvm17-devel) on void linux was not mentioned. 2024-08-24 11:10:59 +02:00
Christoffer Lerno
d1a0ec5a35 Compiler didn't detect when a module name was used both as a generic and regular module. 2024-08-23 19:31:49 +02:00
Nikita Pivkin
cb790b4672 Remove unused parameters from check_col and check_row
Signed-off-by: Nikita Pivkin <nikita.pivkin@smartforce.io>
2024-08-23 19:03:03 +02:00
Christian Buttner
19d37ef641 Add types::is_signed, is_unsigned and inner_type. (#1365)
Add `types::is_signed`, `is_unsigned` and `inner_type`.
2024-08-23 19:01:05 +02:00
Christoffer Lerno
0722011385 Fix of compile arguments. 2024-08-23 16:47:19 +02:00
Christian Buttner
59ed118e66 Address/memory/thread sanitizer. 2024-08-23 16:06:22 +02:00
Christoffer Lerno
d54468d7ed Simplify some asm code and update releasenotes for RISCV 2024-08-23 10:23:17 +02:00
Christoffer Lerno
218f1a6ead Add support for vendor specific extensions in project.json and manifest.json. 2024-08-22 12:49:14 +02:00
Christoffer Lerno
abbd94e89b Add iOS and Android targets. 2024-08-22 00:31:03 +02:00
Christoffer Lerno
b46463563e Add path test windows and escape in double quote. 2024-08-21 10:37:50 +02:00
Christoffer Lerno
33ce8e8a75 Add path test windows. 2024-08-21 10:15:45 +02:00
Chuck Benedict
05ab0707fc Add RISC-V block asm support 2024-08-20 22:42:38 +02:00
Christoffer Lerno
d32861193b DynamicArenaAllocator would not correctly free. 2024-08-20 22:42:01 +02:00
Christoffer Lerno
fb4a231703 Add $member.get(value) to replace value.$eval($member.nameof) 2024-08-20 14:24:12 +02:00
Christoffer Lerno
0963ab4cc0 Update readme. 2024-08-19 23:52:39 +02:00
Christoffer Lerno
a248511d7b Added ElasticArray 2024-08-19 23:20:14 +02:00
Christoffer Lerno
79a1639f8a Fix aligned alloc for Win32 targets. 2024-08-19 15:25:00 +02:00
Christoffer Lerno
476a6424ee insert_at incorrectly prevented inserts at the end of a list. 2024-08-19 11:44:06 +02:00
Christoffer Lerno
6de17b9ae9 Fix use of deprecated function. Fix bug when compile time subtracting a distinct type. Fix test/benchmark debug info use. 2024-08-19 09:36:45 +02:00
Christoffer Lerno
cb7116f08b New linker options handling 2024-08-19 01:28:57 +02:00
Christoffer Lerno
15a4e23b22 Benchmark / test no longer suppresses debug info. #1364 2024-08-18 22:29:41 +02:00
Christoffer Lerno
2b0857baf9 Add connection reset error. 2024-08-18 20:01:54 +02:00
Christoffer Lerno
20b0bf43ad Fix of dstring. 2024-08-18 09:50:54 +02:00
Christoffer Lerno
17d6f03bae New hashmap type, Map 2024-08-18 00:37:24 +02:00
Owen Shepherd
4edaf603c9 fix: Guard against uninitialized hashmap in key removal
Removing non-present keys is a supported operation on HashMaps,
and most other operations are well-defined on uninitialized
HashMaps.

Currently, removing any key on an uninitialized HashMap will
result in an 'Array index out of bounds' error.

This change guards against such a case.
2024-08-17 02:35:04 +02:00
Christoffer Lerno
74b8da1e15 Avoid any constants that have the "untyped list" type but isn't a CONST_UNTYPED_LIST. 2024-08-16 21:49:21 +02:00
Christoffer Lerno
16cb756d3f Bug converting untyped list #1360 2024-08-16 18:33:30 +02:00
Christoffer Lerno
f1efdf3d98 Incorrect zero analysis on foo["test"] = {} #1360 2024-08-16 16:50:58 +02:00
Christoffer Lerno
edfea639cf - Introduce $vaarg[...] syntax and deprecate the old $vaarg(...).
- Similar change to `$vasplat`: `$vasplat` and `$vasplat[1..]`.
2024-08-16 09:28:28 +02:00
Christoffer Lerno
9fd9280132 Fix incorrect parsing of $exec. 2024-08-16 00:07:09 +02:00
Christoffer Lerno
d0bb69516a Missing check on optional left hand side for s.x. #1360 2024-08-15 21:15:59 +02:00
Christoffer Lerno
dc44254ba1 Debug info with recursive canonical type usage could cause segfault. 2024-08-15 20:22:56 +02:00
Christian Buttner
85c682f7e6 Escape arguments to platform linker/compiler. (#1358)
* Escape arguments to platform linker/compiler.
2024-08-15 15:01:53 +02:00
Christoffer Lerno
2a69f93605 Issues with wincrt linking. 2024-08-15 13:33:08 +02:00
Christoffer Lerno
ad4950130c Remove use of tappend in rmtree on windows. 2024-08-15 00:40:01 +02:00
Christoffer Lerno
3ccb4b9ec3 $exec may now provide a stdin parameter. Deprecated path.append, path.tappend, getcwd, tgetcwd, path.absolute, ls. Deprecated env::get_config_dir, replaced by env::new_get_config_dir. Added path.has_extension, path.new_append, path.temp_append, new_cwd, temp_cwd, path.new_absolute, new_ls, temp_ls. Added dstring.replace Updated win escapes for exec. 2024-08-15 00:31:47 +02:00
Christoffer Lerno
6bc486400c Add globals to -P output. 2024-08-13 22:50:45 +02:00
Christoffer Lerno
9228dbb8b8 Fix ordering issues with $include / $exec by adding a pass #1302. 2024-08-13 22:19:53 +02:00
Christoffer Lerno
e68b453218 Do not bundle output with docker. 2024-08-13 15:23:56 +02:00
Christoffer Lerno
1dd2b0ec19 Add a few newlines to maybe keep some compilers happy. 2024-08-13 14:33:33 +02:00
Tom Clesius
e7e9d3b8c7 Adapt Docker script and Dockerfile (#1347)
Adapt Docker script and Dockerfile
2024-08-13 13:37:16 +02:00
kostyavechkanov
800ad9e898 Feature/add-target (#1) (#1350)
Feature/add target (#1) project add-target command
2024-08-13 13:34:53 +02:00
Christoffer Lerno
ddecf2d5f0 Correctly show macOS version settings for project.json 2024-08-13 10:25:15 +02:00
Mikhail Shimanov
09da17dab7 Update README.md
Duplicate package removed
2024-08-12 23:26:38 +02:00
Christoffer Lerno
1678e2a939 Assert not properly traced #1354. Update interface fix. 2024-08-12 21:01:04 +02:00
Christoffer Lerno
9aab962ebc Interface resolution when part of generics #1348. 2024-08-12 10:25:53 +02:00
Christoffer Lerno
baf6e71a80 Fix interface lazy resolution errors. Fix i128 change in LLVM. 2024-08-12 01:25:30 +02:00
Christoffer Lerno
412fa4b12f Use PIE/PIC on Linux 2024-08-11 23:00:38 +02:00
Christoffer Lerno
3cae557b88 Int128 alignment fixed on x64 Linux. 2024-08-11 22:48:21 +02:00
Christoffer Lerno
f7c39ae4a9 Recursively follow interfaces when looking up method. 2024-08-11 21:16:02 +02:00
Christoffer Lerno
6d93ce9d33 Update to libc::setjmp on Win32, to do no stack unwinding. 2024-08-11 18:22:14 +02:00
Sergwest
031cbae0d6 added the necessary library to build on void linux in the README.md 2024-08-11 17:07:48 +02:00
Christoffer Lerno
5fbee47c2b Update version information, 2024-08-11 17:03:21 +02:00
Christoffer Lerno
2cd25a489a Fix of global state init. 2024-08-11 16:55:40 +02:00
Christoffer Lerno
e67586b8b0 Fixes to library loading and test sources. 2024-08-11 16:46:53 +02:00
Christoffer Lerno
7d643942b4 Fix issues when checking methods and interfaces hasn't been resolved yet. 2024-08-11 16:16:16 +02:00
Christoffer Lerno
2257a7f4ec Comment out decl size. 2024-08-11 15:14:52 +02:00
Christoffer Lerno
f8ca173fd8 Refactoring a bit. 2024-08-11 15:05:36 +02:00
Christoffer Lerno
b08e6743be When resolving inherited interfaces, the interface type wasn't always resolved. 2024-08-11 10:19:20 +02:00
Christoffer Lerno
a97e4fe42d Add temp allocator scribble. Make bufferstream safer. 2024-08-11 01:17:25 +02:00
Christoffer Lerno
2706495668 Add temp allocator scribble. Make bufferstream safer. 2024-08-11 01:17:03 +02:00
Christoffer Lerno
224c3f4123 Printable values passed to the Formatter as pointers, will print as if passed by value. Pointers are rendered with "0x" prefix when passed to '%s'. 2024-08-11 00:27:06 +02:00
Christoffer Lerno
f2911be116 Assertion when has_tagof is accidentally called on fn type #1343 2024-08-10 21:59:41 +02:00
Christoffer Lerno
05c5eaed48 Add deprecation notice for $and, $or, $concat, $append. 2024-08-10 21:25:13 +02:00
Christoffer Lerno
8541e9535e Fix print when a tag is not found. 2024-08-10 21:15:19 +02:00
Christoffer Lerno
811cb2b95c Add string methods to json, and fix issue in dstring when the formatter uses temp. Remove unnecessary use of temp allocator in to_format for json. 2024-08-10 19:13:58 +02:00
Christoffer Lerno
05421223be Add --silence-deprecation 2024-08-10 09:54:59 +02:00
Christoffer Lerno
808a6b82f3 Add simple UTF16 detection. 2024-08-10 02:50:42 +02:00
Christian Buttner
30af7f1ca6 Add c-include-dirs project/manifest setting. (#1338)
Set the include directories to be used when compiling C sources.
2024-08-10 01:51:59 +02:00
Christoffer Lerno
274e5280cb Rename muldiv and update tests for LLVM 20 2024-08-09 23:56:26 +02:00
Samuel Goad
f85c4cd79f Update string_iterator.c3 to include extra convenience methods (#1327)
Update string_iterator.c3 to include extra convenience methods

Added peek: returns the next character without incrementing current
Added has_next: checks if the iterator has another element
Added get: gets the current element (the same one that was returned with the previous call to next).
2024-08-09 23:10:46 +02:00
Lexi
696d39b922 Move safe_mul_div macro and make it generic on integer types (#1334)
Move safe_mul_div macro and make it generic on integer types
2024-08-09 22:54:26 +02:00
Christoffer Lerno
d997445284 The compiler now skips UTF8 BOM. 2024-08-09 22:39:24 +02:00
Christoffer Lerno
f3e5268083 % analysis was incorrect for int vectors. 2024-08-09 15:23:40 +02:00
Christoffer Lerno
44db4a21fc Add @tag and .tagof .has_tagof. Allow bitstructs to have attributes. 2024-08-09 15:03:44 +02:00
Christoffer Lerno
c8a113384c Better precision with Clock on Win32 2024-08-08 23:03:04 +02:00
Lexi Allen
07e7bc0a94 Fix win32 native_clock() by converting native performance counter value to nanoseconds using previously gotten frequency value 2024-08-08 21:14:58 +02:00
Prithviraj Renjella Rajendra Prasad
7c8acbe485 Implemented arg passing to clean-run and run commands (#1328)
* added clean-run and run commands to list of commands that pass args

* Updated compiler usage message to reflect that commands run and clean-run accept args
2024-08-08 16:24:00 +02:00
Christoffer Lerno
65c2126202 Removing tb codegen info, because it's sure to have code-rotted by now. 2024-08-08 12:55:40 +02:00
Christoffer Lerno
0ef0f62b69 Only destroy temp allocators on env::LIBC. 2024-08-08 12:48:01 +02:00
Christoffer Lerno
921422a189 Fix Vec2.angle 2024-08-08 01:48:39 +02:00
Christoffer Lerno
56b771a7ad Support destroying temp allocators, and destroy temp allocators on exit. 2024-08-07 16:24:26 +02:00
Christoffer Lerno
d2988e6a88 With single module, name the .o file after what -o provides. #1306 2024-08-07 01:35:09 +02:00
Velikiy Kirill
16510d2400 Update tcp.c3 to finally (i guess) fix Windows Sockets (#1324)
Update tcp.c3
2024-08-06 21:20:59 +02:00
Christoffer Lerno
6f790598ef Update manifest.json template. #1321 2024-08-06 17:42:23 +02:00
Christoffer Lerno
63f0c7b2fe 'wincrt' in manifest.json should now be respected #1322 2024-08-06 17:37:43 +02:00
Christoffer Lerno
800f7970a7 Fixes to the socket functions. Improved output when pointer is out of range. Better error when casting to a distinct fails. 2024-08-06 17:08:03 +02:00
Velikiy Kirill
b7381fc075 Adding win32_WSACleanup() 2024-08-06 15:59:36 +02:00
Christoffer Lerno
b1785606cc LLVM codegen for constants in enums could fail. 2024-08-06 00:28:03 +02:00
Christoffer Lerno
387d7d5508 Fix defer on 19/20 2024-08-05 23:15:55 +02:00
Christoffer Lerno
6adacf8892 Fix module name regression. 2024-08-05 23:04:29 +02:00
Christoffer Lerno
f7d6f93f1b Refactoring -> ensure built in aliases have a valid unit. 2024-08-05 22:48:18 +02:00
Dodzey
9daa173ab7 Add methodsof to type info (#1303)
Add `methodsof` to type info for struct, union and bitstruct
2024-08-05 21:58:13 +02:00
Lexi
e748f72447 Project view command (#1314)
Add parsing for the project command and view subcommand. Add basic implementation of c3c project view. Move get_valid_integer into common build.

Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2024-08-05 21:45:15 +02:00
Christoffer Lerno
14358417c8 Refactoring -> ensure built in aliases have a valid unit. 2024-08-05 21:44:02 +02:00
Oloruntobi1
8aa3461bf6 [DOCS] updated readme with issue 1086 link 2024-08-05 19:55:04 +02:00
Christoffer Lerno
04c37a98b5 Formatting. 2024-08-05 19:53:39 +02:00
rexim
4850f1e94b Ignore EINTR return from waitpid
Apparently it is a thing that can happen and for example musl just
ignores such situations and tries to wait again.

dd1e63c363/src/process/system.c (L39)
2024-08-05 19:53:05 +02:00
Christoffer Lerno
60945ffe58 Fix of tests. 2024-08-05 19:52:34 +02:00
Christoffer Lerno
746016996c Variable in if-try / if-catch cannot be a reused variable name. 2024-08-05 18:43:04 +02:00
Christoffer Lerno
67a2734777 Issue where a if (catch e = ...) in a defer would be incorrectly copied. Causing codegen error. 2024-08-05 15:20:50 +02:00
Christoffer Lerno
b208fc7cf5 Add unreachable. 2024-08-04 23:40:28 +02:00
Christoffer Lerno
2748cf99b3 - Fix issue where a compile time parameter is followed by "...".
- Fix issue with some conversions to untyped list.
- Experimental change: add `+++` `&&&` `|||` as replacement for `$concat`, `$and` and `$or`.
2024-08-04 23:16:25 +02:00
Christoffer Lerno
b49b60ab5f Fix compiler timings. 2024-08-04 11:21:23 +02:00
Christoffer Lerno
620c67b04e Bug in List add_array when reserving memory. 2024-08-04 01:34:45 +02:00
Alexey Kutepov
a5b5f315d1 Implement passing arguments to program via compile-run (#1296) 2024-08-03 19:47:52 +02:00
Christoffer Lerno
43ea05aad2 Remove $expand. 2024-08-03 12:47:19 +02:00
Halen84
0ec1c80221 Fix typo in parse_global.c
In parse_def_ident()
2024-08-03 03:50:55 +02:00
Christoffer Lerno
d91c289bf6 Distinct inline can now be called if it is aliasing a function pointer. 2024-08-03 03:08:38 +02:00
Christoffer Lerno
74b9971494 Add wincrt setting to libraries. 2024-08-02 20:15:40 +02:00
Christoffer Lerno
f8f116109a Use back-off strategy when allocating virtual memory. 2024-08-02 15:45:16 +02:00
Christoffer Lerno
8498cb6258 Add @const attribute for macros, for better error messages with constant macros #1293 2024-08-02 15:01:02 +02:00
Yhya Ibrahim
7a72f44f64 Add --run-once option to delete the output file after running it (#1295)
Add `run-once` option to delete the output file after running it
2024-08-02 12:47:36 +02:00
Christoffer Lerno
a90e3c440b Distinct inline would not implement protocol if the inlined implemented it. This closes #1292 2024-08-02 11:37:38 +02:00
Christoffer Lerno
1aab8b87ec Add experimental @noalias attribute. 2024-08-01 22:57:26 +02:00
Christoffer Lerno
ebf071ac51 Fix incorrect override of optimization levels when using projects. 2024-08-01 21:40:51 +02:00
Christoffer Lerno
3d0fc33441 && doesn't work correctly with lambdas #1279. 2024-08-01 21:16:23 +02:00
Christoffer Lerno
c50df85976 Fix docs to match the update in supporting LLVM 17+ only. 2024-08-01 20:04:54 +02:00
Christoffer Lerno
db9fc20acf Wrapper RTTI now follows LLVM RTTI. 2024-08-01 19:58:52 +02:00
Christoffer Lerno
10058cf271 - Distinct func type would not accept direct function address assign. #1287 2024-08-01 16:26:50 +02:00
Christoffer Lerno
310dadef45 No type_lowering in the frontend. 2024-08-01 13:26:47 +02:00
Christoffer Lerno
3159f036a2 Update lowering for function pointers. 2024-08-01 11:37:40 +02:00
Christoffer Lerno
c3e426c82a Assertion with duplicate function pointer signatures #1286 2024-08-01 01:52:26 +02:00
Christoffer Lerno
b83d388523 Incorrect justify formatting of integers. 2024-08-01 01:20:42 +02:00
Christoffer Lerno
d8820259d2 Enable LLVM 19 2024-08-01 00:21:14 +02:00
Christoffer Lerno
354d78e893 Temporarily disable LLVM for Linux 2024-08-01 00:12:01 +02:00
Christoffer Lerno
7f00f35f4b $expand macro, to expand a string into code. opt project setting now properly documented. 2024-08-01 00:07:16 +02:00
Yhya Ibrahim
8c33b073c2 Fix a warning/error where C compilers can not predict that a variable is initialized 2024-07-31 18:05:26 +02:00
Christoffer Lerno
d6490c9bab Make it possible to set max limit for memory pages. 2024-07-31 18:03:52 +02:00
PalsFreniers
b0e104bfd0 Adding Termios library as std::libc::termios (posix libc functions) (#1272)
Adding libc's termios to lib/std/libc
2024-07-31 14:45:04 +02:00
Chuck Benedict
563e677b08 Add Riscv Example (#1268)
Add Riscv example. Risc-V CI. Install baremetal toolchain. Prevent imported crt file from messing up linker search.
2024-07-31 14:43:47 +02:00
Ikko Eltociear Ashimine
7664d0568e Update bytewriter.c3
minor fix
2024-07-31 01:37:12 +02:00
Christoffer Lerno
e1a13e433f Experimental xtensa support 2024-07-31 01:36:28 +02:00
Dodzey
d212f7d946 Remove extra item 2024-07-30 17:52:14 +02:00
Christoffer Lerno
8d6dabf65c Struct members declared in a single line declaration were not sharing attributes. #1266 2024-07-30 02:45:50 +02:00
Dmitry Atamanov
a4c5b85db8 Remove extra space. 2024-07-29 15:16:14 +02:00
Christoffer Lerno
e66001c182 Using winmain would call the wrong definition #1265. 2024-07-29 15:04:32 +02:00
Christoffer Lerno
08c7b35731 Improve the error message when typing fn void Foo(). 2024-07-28 21:08:47 +02:00
Christoffer Lerno
35cb36fcea Fix incorrect linker selection. 2024-07-28 17:55:59 +02:00
Christoffer Lerno
bf8ca989d6 Add --show-backtrace option to disable backtrace for even smaller binary. 2024-07-28 01:10:59 +02:00
Christoffer Lerno
4976ebcef4 Permit foreach values to be optional. Update matching algorithm. 2024-07-27 21:53:44 +02:00
Christoffer Lerno
51661f5c55 c3c init-lib does not create the directory with the .c3l suffix #1253 2024-07-27 11:52:56 +02:00
Christoffer Lerno
3cbb10392c Don't generate .o files on compile and compile-run if there is no main. 2024-07-27 05:00:27 +02:00
Christoffer Lerno
168ce752d1 Package Linux binaries in a folder called "c3" and not "linux" 2024-07-27 02:49:09 +02:00
Christoffer Lerno
8fcf9bc6bf Give some symbol name suggestions when the path is matched. 2024-07-27 01:21:02 +02:00
Christoffer Lerno
56f43f55f3 Add WASM test. 2024-07-26 21:39:45 +02:00
Christoffer Lerno
9386ac026d dbghelp.lib was linked even on nolibc on Windows. 2024-07-26 20:54:57 +02:00
Christoffer Lerno
e1565ccdc5 Regression: Invalid is_random implementation due to changes in 0.6. 2024-07-26 20:49:17 +02:00
Christoffer Lerno
34993a20fd Fix broken WASM std library code. 2024-07-26 19:20:58 +02:00
Christoffer Lerno
ea0124433a Remove "EXPR_GROUP" to simplify the code somewhat. 2024-07-26 14:34:08 +02:00
Christoffer Lerno
73b15c691d Deprecate *-add settings, use without -add. Updated CI. 2024-07-26 03:23:30 +02:00
Christoffer Lerno
623dd9f3b3 Added "weak" type aliases def Foo = my_foo::Foo @weak; 2024-07-26 01:13:48 +02:00
Christoffer Lerno
379637f214 Scalar -> vector not implicit in call or assign. 2024-07-24 14:00:09 +02:00
Christian Buttner
26ca8f7777 Add type property is_substruct. 2024-07-24 14:00:09 +02:00
Christoffer Lerno
237f7e7f1a Updated stats. 2024-07-24 14:00:09 +02:00
Christoffer Lerno
34fc9851bf Update wrapper 2024-07-24 14:00:09 +02:00
Christoffer Lerno
abdaca08fe Add new optimizer runner. 2024-07-24 14:00:09 +02:00
Christoffer Lerno
bdc9f339c9 The msvc_sdk script failed to work properly on windows when run in folders with spaces 2024-07-24 13:18:26 +02:00
Christoffer Lerno
3188d4d858 Reference parameter doesn't work with vector subscript #1250. 2024-07-23 21:20:40 +02:00
Christoffer Lerno
1bb76b1a49 Unsplat with named parameters was accidentally disallowed. 2024-07-22 11:44:34 +02:00
Christoffer Lerno
9584efd84c Update AVX support. 2024-07-22 02:34:33 +02:00
Christoffer Lerno
c84bc8a8f3 Add convenience function. 2024-07-21 00:43:16 +02:00
Christian Buttner
edc55a2afd Small fixes to stdlib. (#1247)
Small fixes to stdlib. Match the signature of `NativeConditionVariable.wait_timeout` and `NativeMutex.lock_timeout` of thread_win32.c3 to `ConditionVariable.wait_timeout` and `TimedMutex.lock_timeout` to avoid casting errors. Add `time::us`.
2024-07-20 19:19:16 +02:00
Christoffer Lerno
480325177c Remove accidental debug code. 2024-07-20 18:40:25 +02:00
Christoffer Lerno
03cfa42eb6 Duplicate symbols with static variable declared in macro #1248. Improved error message when trying user foreach with an untyped list. 2024-07-20 03:39:33 +02:00
Christoffer Lerno
b25c573ae3 Indexing into a constant array / struct now works at compile time. Constants defined by indexing into another constant could fail codegen. Stdlib nolibc code bugs fixed. 2024-07-20 01:20:03 +02:00
Christoffer Lerno
7f5757d66b Add .dot to integer vectors. 2024-07-19 11:34:05 +02:00
Christoffer Lerno
a3a275c3d5 Updated linux build 2024-07-19 11:10:59 +02:00
Christoffer Lerno
557f007b12 Spelling 2024-07-19 10:38:52 +02:00
Christoffer Lerno
542406c16f Exclude 18 for linux for now. 2024-07-19 01:13:49 +02:00
Christoffer Lerno
1fa870411f Separate LLVM18 compile for Linux in CI 2024-07-19 00:36:04 +02:00
Christoffer Lerno
c096487eea Test if this fixes LLVM 18 compilation. 2024-07-19 00:32:27 +02:00
Christoffer Lerno
97a8e0cdd4 Retain backwards compatibility with old manifest.json. 2024-07-19 00:23:54 +02:00
Christoffer Lerno
eb20a5c051 mainfest.json is now checked for incorrect keys. Added --list-manifest-properties to list the available properties in manifest.json. 2024-07-19 00:03:05 +02:00
Christoffer Lerno
5c6acf89da Added docs to io.c3 2024-07-18 20:44:36 +02:00
Christoffer Lerno
9dfe7ddbde Add wrapper methods, use LLVM-transforms directly. 2024-07-18 20:44:36 +02:00
Christian Buttner
8285720180 Add tests and improvements for @nopadding and @compact. 2024-07-17 17:00:36 +02:00
Christoffer Lerno
a4a1a42842 Update llvm build to use on windows. 2024-07-17 16:55:55 +02:00
Christoffer Lerno
3c3217ab2b Fix PIE. 2024-07-16 14:58:48 +02:00
Alex Anderson
17ee3887dd Use usz and fix out of bounds access in branchless loop 2024-07-16 13:22:11 +02:00
Alex Anderson
db75da65db Make countingsort.c3's recursion stage branchless
Tracks the three potential cases for each fallback, item counts ranging from [2,32], [33,128], [128, ...] and uses a loop specifically for each fallback.
2024-07-15 22:25:59 +02:00
Christoffer Lerno
cf95257c81 Fix test (again). 2024-07-15 17:30:42 +02:00
Christoffer Lerno
b40036c203 Fix test. 2024-07-15 17:04:06 +02:00
Christian Buttner
b18661a8b0 Make stdlib mem::allocator more complete. (#1238)
Make stdlib mem::allocator more complete. Fill in some gaps and docstrings. List.to_new_array. Handle overalignment smoothly in list.
2024-07-15 16:35:40 +02:00
Christoffer Lerno
bc0d52142a Added pull request #1189: Fix os::native_is_{file,dir} bug. Add tests. 2024-07-15 03:02:54 +02:00
Christoffer Lerno
24041ed80d Macro $case statements now pick the first match and does not evaluate the rest. Added countingsort tests #1234. 2024-07-15 02:01:26 +02:00
Christoffer Lerno
1a03e6b22e Prevent implicit array casts to pointers with higher alignment. #1237 2024-07-14 23:44:05 +02:00
Christoffer Lerno
68fb916195 Fix when memcmp is defined. 2024-07-14 16:51:38 +02:00
Christoffer Lerno
dfb8a1b8cb Improved bool and float array comparisons. 2024-07-14 14:16:17 +02:00
Christoffer Lerno
6c38409c57 Array comparison now uses built-in memcmp on LLVM to enable optimizations. 2024-07-14 01:35:19 +02:00
Christoffer Lerno
27fd7a9088 - Fix problem where a $$FUNC would return "<GLOBAL>" when evaluated for a static in a function #1236. 2024-07-13 19:57:04 +02:00
Christoffer Lerno
0e62423e06 Bitstruct in struct fix. 2024-07-13 01:54:45 +02:00
Christoffer Lerno
3f45ed14b9 Compare @compact structs. 2024-07-12 23:54:07 +02:00
Christoffer Lerno
ca4b782912 MemberIndex -> ArrayIndex 2024-07-12 18:27:05 +02:00
Christian Buttner
1976a11154 @nopadding and @compact attributes (#1235)
Add `@nopadding` attribute. `@compact`
2024-07-12 18:25:09 +02:00
Christoffer Lerno
e7d8f64a49 Compile c files to separate directories. Add compressed library to example test project. 2024-07-10 13:35:01 +02:00
Christoffer Lerno
5cf1f13328 Private function called from nested macro not visible to linker #1232 2024-07-09 22:01:39 +02:00
Christoffer Lerno
fba706f10b Updated sorting code. 2024-07-09 01:04:11 +02:00
Alex Anderson
c50630989e draft: add countingsort.c3 (#1230)
Draft countingsort.c3
2024-07-08 21:08:57 +02:00
Christoffer Lerno
3832be94d0 Added sort helper function. 2024-07-08 21:02:49 +02:00
Alex Anderson
900c1152d3 add insertion sort (#1225) 2024-07-08 18:53:47 +02:00
Christoffer Lerno
4ea50a8a85 Update version. 2024-07-08 17:39:31 +02:00
Christoffer Lerno
0132fd4101 Bad error message when using a generic method without generic parameters #1228 2024-07-08 17:32:39 +02:00
Christoffer Lerno
0e90ce3b8a Prevent accidental delete of lib folder when building. 2024-07-08 14:05:09 +02:00
Christoffer Lerno
9368ebfbd3 Allow using $defined(&a[1]) to check if the operation is supported. 2024-07-08 01:42:34 +02:00
Christoffer Lerno
8381dbbd8f Fix incorrect INLINE on const init function. 2024-07-07 23:29:57 +02:00
Christoffer Lerno
343ccaa2ef Support c-file compilation in libraries. 2024-07-07 11:21:31 +02:00
Christoffer Lerno
3f62775f4b Support c-file compilation in libraries. 2024-07-07 02:04:37 +02:00
Christoffer Lerno
c3ecad96b7 Update CI, add example. 2024-07-05 16:53:49 +02:00
Christoffer Lerno
2ffb0cf5f7 Fix ABI lowering for 128 bit vectors on Linux. 2024-07-05 16:07:17 +02:00
Christoffer Lerno
ef716f3a69 Pull requests to dev also have a test action. 2024-07-05 15:17:23 +02:00
Christoffer Lerno
cc935862b7 Build using LLVM 18 2024-07-05 02:06:37 +02:00
Christoffer Lerno
85a535dd0c $typeof(*x) should be valid when x is an [out] parameter #1226 2024-07-04 16:50:35 +02:00
Christoffer Lerno
ab626fe3eb Update avoid warning in FetchContent 2024-07-04 12:07:01 +02:00
Christoffer Lerno
05011df13a Update flags to mac compile 2024-07-04 02:36:17 +02:00
Christoffer Lerno
fcdb25c426 Update some comments and variable names. 2024-07-04 02:15:08 +02:00
Christian Buttner
cc9ca35e04 Add $debugtrap builtin. (#1220)
Add `$breakpoint` builtin.
2024-07-04 00:50:29 +02:00
Christoffer Lerno
4a50de8318 Use LLVM 18 by default. Update MSVC to LLVM 18.1.8. 2024-07-04 00:48:35 +02:00
Christian Buttner
12051e7544 Fix $$unaligned_store arg check and add test. (#1224)
Fix `$$unaligned_store` arg check and add test.
2024-07-04 00:44:32 +02:00
Christoffer Lerno
210508fe4f Updated test. 2024-07-03 15:59:46 +02:00
Christoffer Lerno
ba5b045351 Fix Type->$Type in allocator #1223 2024-07-03 15:57:17 +02:00
Christoffer Lerno
9a19eeacb3 Added further tests to #1219 2024-07-03 15:14:50 +02:00
Christian Buttner
10ed03d6bf Extend win32 stdlib API. 2024-07-03 11:11:34 +02:00
Christoffer Lerno
3be1bf4384 Added test and updated releasenotes for formatter changes. 2024-07-02 23:28:23 +02:00
Christian Buttner
3396b20661 Fix formatter crash for null ZString, print "(null)" for null pointers. 2024-07-02 23:24:18 +02:00
Christoffer Lerno
c9e1140189 Reorganizing the Windows OS files. 2024-07-02 17:37:45 +02:00
Christoffer Lerno
416cd30b42 Wrong size for structs containing overaligned structs #1219 2024-07-02 15:17:41 +02:00
Christoffer Lerno
d66a07cc55 Add defer catch test. 2024-07-02 13:57:48 +02:00
Christoffer Lerno
ce17dbe240 Bug fix for rethrow + defer catch. More types and functions for win32 2024-07-02 02:48:48 +02:00
Christoffer Lerno
326fc501e2 Simplified @is_comparer 2024-07-02 00:36:05 +02:00
Christoffer Lerno
91ad3ee0a2 Fix regression for math::log 2024-07-01 16:52:39 +02:00
Christoffer Lerno
2993c422c1 Fix to scalar -> vector conversions. 2024-07-01 15:03:40 +02:00
Christian Buttner
6f8cdde7e4 Added a --no-headers option. 2024-07-01 13:38:58 +02:00
Christoffer Lerno
f521a0dd77 FOREACH_BEGIN / VECEACH replaced by FOREACH / FOREACH_IDX 2024-07-01 13:31:41 +02:00
Christian Buttner
12fdb58da6 Implicitly cast distinct inline to index. (#1218)
Implicitly cast distinct inline to index.
2024-07-01 13:16:39 +02:00
Christoffer Lerno
09876cefde @unaligned_store and @unaligned_load 2024-06-30 01:05:57 +02:00
Christoffer Lerno
d1e2ea7635 Require MSVC 17.7 or higher. 2024-06-29 20:47:25 +02:00
Christoffer Lerno
7b131f2a45 Print MSVC version 2024-06-29 20:35:23 +02:00
Christoffer Lerno
f3d5e3d4c2 Set minimum LLVM version for compilation. 2024-06-29 20:30:37 +02:00
Christoffer Lerno
492f83f5e2 Bit negating const zero flags would give an incorrect result. #1213 2024-06-28 16:43:57 +02:00
Christoffer Lerno
7dcd1618d8 Fixes to header gen. 2024-06-28 11:28:05 +02:00
Christoffer Lerno
e2a39aa12e Updated mangling code. 2024-06-28 00:57:14 +02:00
Christoffer Lerno
043833be7b Fixes to casts. 2024-06-27 19:32:45 +02:00
Christoffer Lerno
ad394c19d5 Remove asserts from header gen. 2024-06-27 17:21:08 +02:00
Christoffer Lerno
05592183b1 Fixed distinct comparison behaviour. 2024-06-27 15:06:23 +02:00
Christoffer Lerno
079cbb8f68 Updated module mangling, restrict module names. 2024-06-27 13:37:37 +02:00
Christoffer Lerno
3bddde20ab Fixes to distinct inline conversions. 2024-06-26 21:48:10 +02:00
Christoffer Lerno
0a8a63bc15 Fix to headergen. Updated module name store. 2024-06-26 11:43:14 +02:00
Christoffer Lerno
fd2491446a Update mangling. 2024-06-24 21:55:49 +02:00
Christoffer Lerno
26f3fe37f4 Fix of built in aliases for headers. 2024-06-24 17:23:59 +02:00
Christoffer Lerno
4cff80ecea Header exports implicit. 2024-06-24 15:04:44 +02:00
Christian Buttner
83fe94d497 Fix posix NativeConditionVariable.wait_timeout. (#1211)
Fix posix NativeConditionVariable.wait_timeout. TimeSpec::ns may not exceed one second.
2024-06-24 11:52:21 +02:00
Christoffer Lerno
616bde2c4d Further header updates. 2024-06-24 11:34:23 +02:00
Christian Buttner
0b971c2bd0 Fix off-by-one errors for stdlib unicode conversions. 2024-06-23 23:46:19 +02:00
Christoffer Lerno
201b1b7fbc - Bitstructs, unions and flexible arrays now correctly emitted in headers.
- Require `@export` functions to have `@export` types.
2024-06-23 23:39:58 +02:00
Christoffer Lerno
b0b976ee52 Fix JSON and compile issue. 2024-06-23 17:40:56 +02:00
Christoffer Lerno
7020569f45 Cleanup. 2024-06-23 16:36:04 +02:00
Christoffer Lerno
e153c76719 Bit negate now properly does type promotion. 2024-06-23 16:13:37 +02:00
Christoffer Lerno
e7f9c11a14 "panic-msg" setting to suppress panic message output. 2024-06-23 10:42:03 +02:00
Christoffer Lerno
f2e5c5e9b9 - Fix bug with @jump miscompile
- Remove "panic" text from unreachable() when safe mode is turned off.
2024-06-22 23:20:23 +02:00
Christoffer Lerno
e02f73417c Trailing body arguments may now be &ref, #hash, $const and $Type arguments. 2024-06-22 22:04:20 +02:00
Christian Buttner
41db9c43e5 Allow omitting = true for designated initializers of bitstruct bools. 2024-06-22 15:57:41 +02:00
Christoffer Lerno
0dc2f0e923 Make function pointers comparable with null again. 2024-06-22 15:38:19 +02:00
Christoffer Lerno
5940d5ddad Removal of unused code. 2024-06-21 23:24:05 +02:00
Christoffer Lerno
684850dda1 Fixing flexible array resolution. 2024-06-21 18:36:39 +02:00
Christoffer Lerno
e8e615f4db Remove superfluous code and flags for type resolution. 2024-06-21 17:45:33 +02:00
Christoffer Lerno
559b060b6b Fix bug in header gen. 2024-06-21 12:31:50 +02:00
Christoffer Lerno
581262d736 Try LLVM 19 support. 2024-06-21 11:44:27 +02:00
Christoffer Lerno
8878a49a1d Introduction of TYPE_FUNC_PTR / TYPE_FUNC_RAW. Fixed rules for function pointers. 2024-06-21 10:46:28 +02:00
Christoffer Lerno
3a7bc4d253 Return the typekind "FUNC" for a function pointer. 2024-06-20 20:47:24 +02:00
Christoffer Lerno
316982fb8f Added test and removed todo. 2024-06-19 01:17:43 +02:00
Christoffer Lerno
cfaea34053 Some additional cleanup. 2024-06-19 00:57:38 +02:00
Christoffer Lerno
8fd1d895d6 Cleanup ct_call parsing. 2024-06-19 00:28:24 +02:00
Christoffer Lerno
b592ecf6f5 Fixed crash on certain recursive function definitions #1209. 2024-06-18 22:33:10 +02:00
Christoffer Lerno
65a8826158 Fix of missing copy of parameterized custom attributes. 2024-06-17 22:05:32 +02:00
Christoffer Lerno
c9fab898cc Improved error notes when call expressions have errors. 2024-06-16 23:33:37 +02:00
Christoffer Lerno
819049d596 @str_hash, @str_upper, @str_lower, @str_find compile time macros. 2024-06-16 21:16:03 +02:00
Christoffer Lerno
147dee6ec7 Addition of $append and $concat functions. Added $$str_hash builtin. Fix to the macho runtime. 2024-06-16 01:57:05 +02:00
Christoffer Lerno
b0b885d506 Prevent Mach-O from removing @init and @dynamic in a more reliable way #1200. 2024-06-15 15:58:12 +02:00
Christoffer Lerno
c94610f8a9 Error with unsigned compare in @ensure when early returning 0 #1207. Added remove_first_item remove_last_item and remove_item as aliases for the match functions. 2024-06-14 17:29:46 +02:00
Christoffer Lerno
21fa006850 Merge 0.5.6 changes into 0.6.0 2024-06-12 11:39:52 +02:00
Christoffer Lerno
e293c435af 0.6.0: init_new/init_temp removed. LinkedList API rewritten. List "pop" and "remove" function now return Optionals. RingBuffer API rewritten. Allocator interface changed. Deprecated Allocator, DString and mem functions removed. "identity" functions are now constants for Matrix and Complex numbers. @default implementations for interfaces removed. any* => any, same for interfaces. Emit local/private globals as "private" in LLVM, following C "static". Updated enum syntax. Add support [rgba] properties in vectors. Improved checks of aliased "void". Subarray -> slice. Fix of llvm codegen enum check. Improved alignment handling. Add --output-dir #1155. Removed List/Object append. GenericList renamed AnyList. Remove unused "unwrap". Fixes to cond. Optimize output in dead branches. Better checking of operator methods. Disallow any from implementing dynamic methods. Check for operator mismatch. Remove unnecessary bitfield. Remove numbering in --list* commands Old style enum declaration for params/type, but now the type is optional. Add note on #1086. Allow making distinct types out of "void", "typeid", "anyfault" and faults. Remove system linker build options. "Try" expressions must be simple expressions. Add optimized build to Mac tests. Register int. assert(false) only allowed in unused branches or in tests. Compile time failed asserts is a compile time error. Remove current_block_is_target. Bug when assigning an optional from an optional. Remove unused emit_zstring. Simplify phi code. Remove unnecessary unreachable blocks and remove unnecessary current_block NULL assignments. Proper handling of '.' and Win32 '//server' paths. Add "no discard" to expression blocks with a return value. Detect "unsigned >= 0" as errors. Fix issue with distinct void as a member #1147. Improve callstack debug information #1184. Fix issue with absolute output-dir paths. Lambdas were not type checked thoroughly #1185. Fix compilation warning #1187. Request jump table using @jump for switches. Path normalization - fix possible null terminator out of bounds. Improved error messages on inlined macros.
Upgrade of mingw in CI. Fix problems using reflection on interface types #1203. Improved debug information on defer. $foreach doesn't create an implicit syntactic scope.
Error if `@if` depends on `@if`. Updated Linux stacktrace. Fix of default argument stacktrace. Allow linking libraries directly by file path. Improve inlining warning messages. Added `index_of_char_from`. Compiler crash using enum nameof from different module #1205. Removed unused fields in find_msvc. Use vswhere to find msvc. Update tests for LLVM 19
2024-06-12 10:14:26 +02:00
Christoffer Lerno
321c5ec756 Update mingw version and funding. 2024-05-27 12:03:45 +02:00
Christoffer Lerno
f04d93f9aa Fix flaw in bitstruct check. 2024-05-20 22:20:33 +02:00
Christoffer Lerno
9436efe554 Compiler crash on designated initializer for structs with bitstruct. 2024-05-20 14:42:09 +02:00
Christoffer Lerno
3acbf708d3 Fix location on foreach debug output. 2024-05-19 23:27:57 +02:00
Christoffer Lerno
92979984ea Fix mutex and wait signatures for Win32. 2024-05-18 22:24:45 +02:00
Christoffer Lerno
a16d41a1e1 Do not elide memory storage on variable for debug. 2024-05-17 19:51:35 +02:00
Christoffer Lerno
ff8b78fc99 Correct debug info on parameters without storage. 2024-05-17 16:26:12 +02:00
Christoffer Lerno
97c9bd7ce0 Assertion failed when casting argument to enum #1196 2024-05-16 16:07:55 +02:00
Christoffer Lerno
c40c93340d Compile time fmod evaluates to 0 #1195 2024-05-16 14:33:11 +02:00
Christoffer Lerno
094c105464 Union is not properly zero-initialized #1194 2024-05-16 11:28:04 +02:00
Christoffer Lerno
e36c696624 Update CI. 2024-05-15 21:49:18 +02:00
Christoffer Lerno
555a4ab4c5 Casting a slice address to its pointer type should not compile #1193. 2024-05-15 21:36:53 +02:00
Christoffer Lerno
7d8cc8776d Duplicate emit of expressions on negation would incorrectly compile negated macros. 2024-05-14 23:30:39 +02:00
Christoffer Lerno
960646ac8a Patch test. 2024-05-09 09:53:48 +02:00
Christoffer Lerno
ed9f15becf Foreach uses non-wrapping add/dec. 2024-05-08 23:05:12 +02:00
Christoffer Lerno
b09aa74f2f Generic modules parameterized with constants would sometimes get the wrong parameterized module name causing conversion errors #1192. 2024-05-04 23:34:37 +02:00
Christoffer Lerno
60805fd11d Bounds checking on length for foo[1:2] slicing #1191 2024-04-28 18:55:26 +02:00
Christoffer Lerno
89ecd4b33d Default to AVX on x64. 2024-04-26 19:29:32 +02:00
Christoffer Lerno
237f142a87 Default CPU actually defaults to a value instead of picking the native CPU. 2024-04-26 19:03:25 +02:00
Christoffer Lerno
a21647a1aa Do not default to native vector capability. 2024-04-26 18:45:14 +02:00
Christoffer Lerno
e9afe4ee25 Update CI script for mac. 2024-04-26 18:09:03 +02:00
Christoffer Lerno
acd067582a Update CI script. 2024-04-26 18:07:16 +02:00
Christoffer Lerno
82227e8901 Incorrect cast of bitstructs #1186 2024-04-26 17:39:30 +02:00
Christoffer Lerno
8b6735a6aa Allow recursive function definitions as long as they are pointers #1182. Add 'zstr' variants for string::new_format / string::tformat. 2024-04-16 19:42:32 +02:00
Christoffer Lerno
9ed8831500 Updated link 2024-04-14 23:07:48 +02:00
Christoffer Lerno
e7d726cc2c Fixup of scratch buffer code. 2024-04-09 14:27:52 +02:00
Christoffer Lerno
11a3dd26c8 Update mingw version. 2024-04-09 14:10:54 +02:00
Christoffer Lerno
04738586b9 Fix bug in scratch_buffer_printf. 2024-04-09 13:26:08 +02:00
cpiernikowski
18b4fce1ca Change return type of next_bool(random) from void to bool 2024-03-28 09:44:47 +01:00
Christoffer Lerno
3b9babe745 0.5.6 Add grammar for defer (catch err). 2024-03-26 09:36:45 +01:00
Brennan Cottrell
a4a85b7bbf Added print-input command line argument (#1175)
* added print-input command line argument
2024-03-26 09:33:47 +01:00
Christoffer Lerno
e8f0275d8e 0.5.6 Add defer (catch err) feature. 2024-03-25 11:35:16 +01:00
Christoffer Lerno
3251f58d46 Change version for MinGW 2024-03-25 09:40:52 +01:00
David
204fb211ac Fix x86_64 ABI small issue (#1174)
* Fix x86_64 ABI small issue Update tests for fix.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2024-03-25 09:39:26 +01:00
Christoffer Lerno
6cade814e1 Update includes for FreeBSD. 2024-03-22 09:11:18 +01:00
Christoffer Lerno
eb2fbabbb1 Update version to 0.5.6 2024-03-19 16:15:13 +01:00
Christoffer Lerno
ee9c5db719 0.5.5 release. 2024-03-18 22:05:16 +01:00
Christoffer Lerno
d8af01dc46 Update release notes and change how versions are reported. 2024-03-18 11:51:31 +01:00
David Gonzalez Martin
5bead069f2 Fix aarch64 return type ABI bug 2024-03-17 20:06:53 +01:00
shv187
63e1345780 Add getModuleHandleA + W to win32 2024-03-16 09:47:42 +01:00
Christoffer Lerno
3df988d0b8 Allow String constants -> ichar*, and allow integer pointers to explicitly convert between unsigned signed. 2024-03-15 23:08:52 +01:00
Christoffer Lerno
656202dc0d Convert paths to backslash before running on Windows. 2024-03-15 20:09:06 +01:00
Christoffer Lerno
eec3253669 Mingw -> 18.1.1-3 2024-03-15 13:23:41 +01:00
Christoffer Lerno
d9423201b8 Change mingw version. 2024-03-15 13:20:05 +01:00
Christoffer Lerno
c6087bc369 Fix underlying type of llvm.used and update section. 2024-03-15 13:02:39 +01:00
Christoffer Lerno
b7077c7967 Fix Win32 with compile-run. 2024-03-14 12:05:01 +01:00
Christoffer Lerno
4acb07f1cb compile-run and run now returns the proper return code. 2024-03-14 11:55:55 +01:00
Christoffer Lerno
5207022a4a For MacOS, running with higher optimization would crash as initializers were removed. 2024-03-14 09:33:24 +01:00
Christoffer Lerno
1a25746343 Regression: no stacktrace. 2024-03-12 17:31:06 +01:00
Christoffer Lerno
0d7ceb625b Fixed link on msvc. 2024-03-12 10:24:48 +01:00
Christoffer Lerno
95fb5f904f New linker build option. "system-linker" deprecated and removed from project settings. 2024-03-12 10:09:02 +01:00
Christoffer Lerno
a0309855d7 Added @link attribute. 2024-03-11 18:10:40 +01:00
Christoffer Lerno
546754e803 'output' directory for projects was incorrect in templates. 2024-03-08 15:34:04 +01:00
Christoffer Lerno
86461909d3 Remove initial './' in Win32 paths when running a binary. 2024-03-04 17:04:57 +01:00
Christoffer Lerno
feebd2a733 Bug in time.add_seconds #1162. 2024-03-01 12:09:47 +01:00
Christoffer Lerno
75e7176675 Bitstruct cast to other bitstruct by way of underlying type would fail #1159. 2024-02-26 23:51:14 +01:00
Christoffer Lerno
bae5d9c7f8 Improved checks of aliased "void". 2024-02-26 18:45:55 +01:00
Christoffer Lerno
4ba033fc84 Fix of int.min incorrect behaviour #1154. 2024-02-26 18:13:40 +01:00
Christoffer Lerno
f0dd0e8f92 Fix of CT named arguments #1156. 2024-02-26 17:47:50 +01:00
Christoffer Lerno
7ea3d230bb 0.5.5 features (#1151)
0.5.5 Disallow multiple `_` in a row in digits, e.g. `1__000`. #1138. Fixed toposort example. Struct/union members now correctly rejects members without storage size #1147. `math::pow` will now correctly promote integer arguments. `math::pow` will now correctly promote integer arguments. Added `new_aligned` and `alloc_aligned` functions to prevent accidental under-alignment when allocating simd. Pointer difference would fail where alignment != size (structs etc) #1150. Add test that overalignment actually works for lists. Fixed array calculation for npot2 vectors. Use native aligned alloc on Windows and POSIX. Deprecates "offset". Simplification of the Allocator interface.
2024-02-22 17:13:51 +01:00
Christoffer Lerno
b7f4fd9074 Create FUNDING.yml 2024-02-20 09:27:21 +01:00
Christoffer Lerno
9a114b38d3 Updated retry and test.c3 examples. 2024-02-17 15:19:27 +01:00
Christoffer Lerno
bec1116f86 Fixes to scoped mem report. 2024-02-17 11:52:18 +01:00
Christoffer Lerno
d7cc37b951 Important fixes to project settings. 2024-02-17 11:39:08 +01:00
Christoffer Lerno
4ce62cf221 Fix of allocator::new. 2024-02-16 21:55:30 +01:00
Christoffer Lerno
1f052da0b9 Clock.c3 fix. 2024-02-16 21:31:49 +01:00
Christoffer Lerno
812dc0c292 Update memory test code. 2024-02-16 14:19:42 +01:00
Christoffer Lerno
798fe0dce9 Updated lex file. 2024-02-16 12:01:13 +01:00
Christoffer Lerno
3f6fe55f9a Grammar fix. 2024-02-15 23:18:11 +01:00
Christoffer Lerno
aee4aecfe7 Remove install_win_reqs.bat from releases. 2024-02-15 22:41:44 +01:00
Christoffer Lerno
748c737e8f 0.5.4: Hash variables accept designated initializers. @safemacro overrides the need for @ in macro names. Fixes to macro context evaluation. Updated allocator api. Removed install_win_reqs.bat. Deterministic @init for MacOS. Fixed temp memory issue with formatter. Support LLVM 19. Add support to compare bitstructs using == and !=. Support Windows .def files. Removed invalid grammar from grammar.y. Support compile time folding of &|^~ for bitstructs. output project setting now respected. Fix issue where constants were not properly constant folded. Add temp_push/pop. Aliased declarations caused errors when used in initializers. Fix export output. Fix of const ternary #1118. Fix of $$MODULE in nested macros #1117. Fix debug info on globals. out now correctly detects subscript[] use #1116. Lateral implicit imports removed. Default to '.' if no libdir is specified. Improved error messages for --lib. Fix raylib snake example. Overzealous local escape check corrected #1127. Improved yacc grammar #1128. --linker argument #1067. Fixes to the matrix operations #1130. Added GenericList. 2024-02-15 21:39:33 +01:00
Christoffer Lerno
c673101bbb Fix incorrect code in sample. 2024-02-14 09:16:36 +01:00
Poly2it
d66674655c Update compilation instructions for Void Linux 2024-02-13 16:45:51 +01:00
Christian Clauss
da292e41bd msvc_build_libraries.py: Remove unused import an f-strings with no placeholder
% `ruff ` # https://docs.astral.sh/ruff
```
 Error: msvc_build_libraries.py:9:8: F401 `os` imported but unused
Error: msvc_build_libraries.py:10:8: F401 `sys` imported but unused
Error: msvc_build_libraries.py:19:8: F401 `re` imported but unused
Error: msvc_build_libraries.py:179:3: F541 f-string without any placeholders
Error: msvc_build_libraries.py:180:3: F541 f-string without any placeholders
Error: msvc_build_libraries.py:182:3: F541 f-string without any placeholders
Error: Process completed with exit code 1.
```
2024-01-18 11:24:03 +01:00
Christoffer Lerno
deb4cc7c4b 0.5.3: Single-module not respected. Fix issue with compiler defined types. Fix optimization levels for projects. Use GEP i8 on offsets. Optimize foreach on len 1 arrays. Move panic blocks last. Fix generic module wildcard imports. Deprecate init_temp / init_new. Fix issue with macro vaarg and untyped lists. Fix extern const globals. 2024-01-14 15:34:54 +01:00
Christoffer Lerno
e91f6e268e 0.5.2: Allow trailing comma in calls and parameter declarations #1092. Fixes issue where single character filenames like 'a.c3' would be rejected. Improve error messages for incorrect user defined foreach. Fix bug with generics in generics. Fix to error with modified vector parameters. Crash with lhs vector inference. Fixes to priority queue. 2023-12-23 23:15:51 +01:00
Poly2it
2595ed5cc9 Add compilation instructions for Void Linux 2023-12-09 15:15:17 +01:00
Christoffer Lerno
1d61ace302 Do not link with debug libc on win32 when using cross compile libs. Add delete methods to dstring. Fixes to macOS aarch64 codegen. Use glibc backtrace when available. Add load_* methods to file. The cast (int[8])int_slice[:8] now works. 2023-12-08 18:59:57 +01:00
Christoffer Lerno
a50c5f4f7c Fixes to the grammar. 2023-11-26 20:06:38 +01:00
Christoffer Lerno
a46bf4fbe0 Improve "const" error message #1079. 2023-11-22 19:12:04 +01:00
Christoffer Lerno
0d1eab5c15 Fix of incorrect error recovery leading to confusing errors #1080 2023-11-22 19:12:04 +01:00
Christoffer Lerno
3255183ee4 0.5 release. 2023-11-20 23:48:18 +01:00
Tiago Teixeira
66b65a042e Add CMake option to link c3 dynamically to LLVM/LLD (#1077)
* Add Cmake option to link c3 dynamically to LLVM/LLD

To link dynamically, use
`cmake -DC3_LINK_DYNAMIC=ON <other-options> ../`

* formatting
2023-11-20 18:10:19 +01:00
Christoffer Lerno
b8db118e64 Use signals to create the stack trace. 2023-11-20 17:17:39 +01:00
Christoffer Lerno
7c15cf2788 Hacky update to stacktrace. 2023-11-20 14:10:12 +01:00
Christoffer Lerno
31538d5955 Fix to backtrace 2023-11-20 12:39:20 +01:00
Christoffer Lerno
337eac6d2f Only fallback on native backtrace if there is no backtrace() 2023-11-20 11:36:38 +01:00
Pierre-Nicolas Clauss
e826f02da5 feat(lib/std): get backtrace for static binaries. 2023-11-20 09:46:25 +01:00
Christoffer Lerno
d5281b10dd Cleanup use of macro inspection to use @typekind and @typeid macros. 2023-11-18 23:35:18 +01:00
Christoffer Lerno
87fdb5956e Improved backtrace on platforms without glibc. Added $$frameaddress and $$returnaddress properly. 2023-11-18 20:13:11 +01:00
Christoffer Lerno
00019f9d76 Small update 2023-11-17 22:10:31 +01:00
Christoffer Lerno
f257befd86 Add compatibility matrix. 2023-11-17 22:08:59 +01:00
Christoffer Lerno
07c27f3292 Remove emulated stack trace. 2023-11-17 07:27:17 +01:00
Christoffer Lerno
ffb0021d04 Use backtrace on windows. Updated backtrace API 2023-11-17 00:36:42 +01:00
Christoffer Lerno
81c93e3488 Use backtrace on windows. Updated backtrace API 2023-11-16 21:39:27 +01:00
Christoffer Lerno
587d5578ab Fix emit location. 2023-11-13 21:42:38 +01:00
Christoffer Lerno
9345e4270a Update panic emit. 2023-11-13 19:49:25 +01:00
Christoffer Lerno
1dde6092e5 Version update, remove unused code. 2023-11-13 18:25:06 +01:00
Christoffer Lerno
5e8816e6df Fixes to native backtrace. 2023-11-13 17:20:46 +01:00
Christoffer Lerno
dc0aa35522 Native Linux backtrace. 2023-11-12 15:20:24 +01:00
Christoffer Lerno
f39aa1a41e Add location tracking for memory allocations. 2023-11-09 22:03:25 +01:00
Christoffer Lerno
e31f2a03ba Add location tracking for memory allocations. 2023-11-09 11:20:29 +01:00
Christoffer Lerno
1e38ccdd2b Fix missing free on GrowableBitSet. init_new/init_temp for GrowableBitSet, LinkedList, List, HashMap, DString, ByteBuffer. Interface to_string renamed to_new_string. Change in allocator usage, malloc is now heap. Added new_array, new_zero_array, new, new_clear, clone. Concat => concat_new. string::printf => string::new_format, string::tprintf => string::tformat. "to_*" are now "to_new_*" and "to_temp_*". "from_*" is "new_from*" 2023-11-09 01:59:49 +01:00
Christoffer Lerno
69470b8738 Improved errors on optional return. Fixes to @nodiscard erroring. Addresses #1062 2023-11-03 23:50:15 +01:00
Christoffer Lerno
5dedaa8e67 Improve error message when creating an exe and the name is already used by a directory. 2023-11-02 21:24:44 +01:00
Christoffer Lerno
eab0b417de Fix to print. 2023-11-01 14:08:29 +01:00
Christoffer Lerno
5e4cfacfcb Rename "main" variable in ct_defined analysis. 2023-11-01 12:53:32 +01:00
Christoffer Lerno
120e21b80b Convencience function for random + entropy function. 2023-10-31 22:21:38 +01:00
Christoffer Lerno
cd7a03c2cf Interface based streams. Fix for initializing with a force unwrap inside. Allow $define to take a list. Allow $define to return error on argument type mismatch in call. Fixed broken bit operations on boolean vectors. 2023-10-31 01:06:59 +01:00
Christoffer Lerno
1aa038c92f Interface based streams. Fix for initializing with a force unwrap inside. Allow $define to take a list. Allow $define to return error on argument type mismatch in call. Fixed broken bit operations on boolean vectors. 2023-10-31 01:06:59 +01:00
Christoffer Lerno
e4c1328ef2 Better checks for missing @dynamic. Addresses #1055. 2023-10-28 04:12:43 +02:00
Christoffer Lerno
e17bb5f321 Void* should never deref and should allow methods to be attached to it. 2023-10-27 00:10:59 +02:00
Christoffer Lerno
a0bc03a9f5 Fix uses of @convertible. 2023-10-26 22:21:16 +02:00
Christoffer Lerno
70e7e4b1d2 Enable mingw 2023-10-26 18:54:12 +02:00
Pierre-Nicolas Clauss
7d16d9acaf fix(build_options): detect -z flag 2023-10-26 14:23:13 +02:00
thecalculus
a7d032df21 fix: argument parsing error 'sdk_path-version' instead of 'sdk-version' 2023-10-26 13:29:39 +02:00
Christoffer Lerno
9af37fe427 $and, $or, $is_const, $assignable, .is_eq, .is_ordered, $defined($vatype(2)) works looking if we can create a type, $defined(foo[0]) $defined(foo()). Remove $checks and @checked. Improvide casting checks to always work without destructive changes. 2023-10-24 22:06:04 +02:00
Christoffer Lerno
8a12dc5bd4 Fix issue where inferred vectors where incorrectly handled in unions and structs. 2023-10-21 22:10:00 +02:00
Christoffer Lerno
d01d8d3663 "protocol" => "interface" 2023-10-20 14:12:08 +02:00
Pierre-Nicolas Clauss
e380075852 fix: standard library search paths
Path construction for locating the standard library expects ending
slashes.
2023-10-19 13:04:43 +02:00
Pierre-Nicolas Clauss
7df5bc0017 Add more paths to search for the standard library
Module `std` is searched first in a `c3` subdirectory.
Search directories are, in order and relative to the compiler executable
location:
- `lib/c3` relative to the parent directory
- `lib` relative to the parent directory
- `/lib/c3`
- `/lib`
- `/c3`
- `/`
- `c3` relative to the parent directory
- the parent directory
- `lib/c3` relative to the grand-parent directory
- `lib` relative to the grand-parent directory
2023-10-17 13:55:51 +02:00
Christoffer Lerno
9b61ddb876 Add @pure to asm. Allow regular statements in naked function. 2023-10-15 19:11:11 +02:00
pini
76fa404b89 Feat/asm x86 (#1046)
* fix(asm): consider asm blocks as volatile
When asm blocks are not marked as volatile, they may be (wrongly)
discarded by LLVM optimization passes.
* fix(asm): mark syscall as clobbering return register
The `syscall` instruction returns the system call result in the `rax`
register.
* feat(asm): add push instructions.
* feat(asm): add pop instructions
2023-10-14 20:50:45 +02:00
Christoffer Lerno
682dfd0e47 Update default asm dialect on asm strings. Fix naked function analysis. 2023-10-14 13:56:53 +02:00
Christoffer Lerno
80a9842a25 Fix incorrect check for naked functions. 2023-10-14 12:46:31 +02:00
Christoffer Lerno
89d4c2cab7 Allocator uses protocols. Fix bug where it was not possible to pass a ref variable as a ref variable. Correct codegen for !anyptr. 2023-10-14 12:39:46 +02:00
Christoffer Lerno
54f32ed71b Fix alignment for remaining bitstruct binary ops. Turn off broken Mingw LLVM in CI. 2023-10-13 14:43:04 +02:00
Christoffer Lerno
fed343e3bb Fix alignment for negating bitstructs. Update mingw LLVM versions in CI. 2023-10-13 13:57:44 +02:00
Christoffer Lerno
fd21b057eb Missing target directive in test. 2023-10-13 13:37:27 +02:00
Christoffer Lerno
e81e91be93 Fix void* <=> protocol casts. Fix of tests. 2023-10-13 12:44:58 +02:00
Christoffer Lerno
9b714e1dbb Remove TB. 2023-10-12 22:00:49 +02:00
Christoffer Lerno
e67e17ef1e Fix MacOS SDK. 2023-10-12 21:59:40 +02:00
Christoffer Lerno
942d53a678 Fix tests. Refactoring MacOS SDK. 2023-10-12 14:10:46 +02:00
Christoffer Lerno
806d7e965f Update MacOS output to include platform version in target triple, fixing the linker warnings metioned here: #1028 2023-10-12 12:36:12 +02:00
Christoffer Lerno
db3e9c7ec7 Add delete testing for windows and update "clean" 2023-10-11 15:49:05 +02:00
Christoffer Lerno
b657724d9b Add delete testing in for windows. 2023-10-11 12:48:38 +02:00
Pierre Curto
5a5b600490 std::collections::list: add List.init_from_array
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-10-11 12:46:28 +02:00
Christoffer Lerno
1472d60c8a Update version and releasenotes. 2023-10-10 23:32:04 +02:00
Christoffer Lerno
a9c28cce6d Default protocols. Closes #1039 2023-10-10 23:30:33 +02:00
Christoffer Lerno
b7a896805d Fix bug in growable bitset. Always insert 0xAA in malloc on testing. 2023-10-09 14:23:41 +02:00
Pierre Curto
6b571fe427 List capacity and SubProcess field name change (#1038)
* std::collections::list: adjust increased capacity
* std::os::process: rename conflicting field in SubProcess
* c3c: adjust spacing for --list-builtins and --list-keywords
2023-10-09 12:52:23 +02:00
Christoffer Lerno
3f77e868b1 Fixes #1040. 2023-10-09 00:55:54 +02:00
Christoffer Lerno
31bc766944 Fix issue where in error messages, integers were assumed to be unicode characters. 2023-10-09 00:41:31 +02:00
Christoffer Lerno
ebddbfb416 Restrict any -> Protocol conversion. Protocol <-> looks at parent. Detect duplicate method definitions for protocols. 2023-10-08 23:43:02 +02:00
Pierre Curto
3aa85cf641 misc (#1033)
* make conv::char32_to_utf8_unsafe() return the number of bytes it wrote
add tests for DString
fix pointer arithmetic in DString.insert_at

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* add support to printf for %d and enums

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-10-08 02:12:20 +02:00
Christoffer Lerno
312a39ee24 Handle protocol inheritance. Allow overlapping protocol methods. Remove the need for &self in protocol declarations. Fix cast rules for protocol. Fix cast rules for bitstruct #1034. 2023-10-08 02:10:28 +02:00
Christoffer Lerno
99cfaa1583 Refactor protocols. 2023-10-06 22:31:41 +02:00
Christoffer Lerno
f3e3aa231d Make Random use protocols. 2023-10-06 22:31:41 +02:00
Christoffer Lerno
a1bce81ed0 Fix growable bitset (#1032) 2023-10-06 01:27:32 +02:00
Christoffer Lerno
dad21bfc6f See if we can get better errors. 2023-10-05 20:35:40 +02:00
Pierre Curto
9643a7c2b2 add DString.insert_at (#1026)
* add DString.insert
* make conv::utf32to8 more C3-like
2023-10-05 19:12:47 +02:00
Christoffer Lerno
d16ad0b4c7 Update "clean". 2023-10-05 19:06:55 +02:00
Christoffer Lerno
32f6d711ac Revert linker changes. 2023-10-05 18:48:05 +02:00
Christoffer Lerno
a07ba63917 Compiling does not leave exe when successful, and also works with generic modules. #1027. For now, silence errors due to the macos linker changes. #1028. Try update clean on Windows #456. 2023-10-05 16:31:11 +02:00
Christoffer Lerno
70f906c71a Dynamic protocols. Correctly widen unsigned array indices (see #1029) 2023-10-05 15:20:41 +02:00
Christoffer Lerno
49c4595457 Dynamic protocols. 2023-10-05 15:20:41 +02:00
Christoffer Lerno
4cc30c0d33 Replace static initializer with @init / @finalizer 2023-10-03 12:45:43 +02:00
Pierre Curto
757a5b58e8 std::core::dstring: fix DString.zstr() (#1024)
rename DString.zstr to DString.zstr_view
2023-10-03 00:32:56 +02:00
Christoffer Lerno
2b9276b495 Add 'exec' 2023-09-30 23:28:22 +02:00
Pierre Curto
b2c7b713f2 add descriptions to the --list-project-properties cli option (#1021)
* fix constant name typo
* add descriptions to the --list-project-properties cli option
* add missing file name for main.c3
2023-09-30 19:52:11 +02:00
Pierre Curto
99ec44ad78 c3c init: add missing file name
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-09-29 14:07:59 +02:00
Christoffer Lerno
db4298431d Remove debug printout. 2023-09-29 14:04:13 +02:00
Christoffer Lerno
fc8b185b5b Add library init. 2023-09-28 11:06:51 +02:00
Christoffer Lerno
f3752d273c Add /run dir for projects. See #921 2023-09-28 10:01:20 +02:00
Christoffer Lerno
aa6101d8ea Correctly deduce the return type for macros with implicit return. See #1018. 2023-09-26 17:37:27 +02:00
Christoffer Lerno
ee42992c37 Make local constants behave as global: see #974 2023-09-26 00:18:59 +02:00
Christoffer Lerno
c5404c6573 get_env for Win32, @pierrec's get_config_dir and get_home_dir 2023-09-25 16:29:49 +02:00
Christoffer Lerno
2a683a6a05 Update mingw LLVM. 2023-09-25 00:00:42 +02:00
Christoffer Lerno
a1ecf2211f as_str() replaced by str_view() 2023-09-24 23:50:16 +02:00
Christoffer Lerno
3675254af4 Fixed test. 2023-09-24 22:01:20 +02:00
Christoffer Lerno
30d794653d Resolve type fully before checking casts, addressing #1013. Correctly show the error location when a method is missing its single argument #1012. 2023-09-24 20:17:41 +02:00
Christoffer Lerno
709fe1c2c0 Some general refactoring in the builder / project code. 2023-09-23 03:22:11 +02:00
Christoffer Lerno
ad776c76a7 Add benchfn and testfn allowing easy overwrite of test and benchmark runners. #990 2023-09-23 00:30:09 +02:00
Christoffer Lerno
dde73e029c Fix of issue #1008 2023-09-22 23:24:21 +02:00
Pierre Curto
e5b990691e std::lib::io: make printn_gen discardable
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-09-22 15:16:44 +02:00
Pierre Curto
d6edd80f3b lib::std::encoding: add varint::{read,write}
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-09-22 15:16:44 +02:00
Christoffer Lerno
e706a8acd0 Fix Linux constant in posix.c3. Address issue #1009. Sanitizes the module name in generated project. 2023-09-22 15:15:47 +02:00
Christoffer Lerno
8dad8f2b1c Use regular backtrace for Mac on signals as well. 2023-09-22 01:12:48 +02:00
Christoffer Lerno
c4228e08c5 MacOS uses regular stacktrace for errors. 2023-09-21 16:39:13 +02:00
Christoffer Lerno
c074e79069 Bitstruct members would get partly evaluated using checks, then incorrectly reset on error. Also, copying bitstruct members were broken. This addresses #1005. 2023-09-19 18:03:20 +02:00
Christoffer Lerno
6e0982327d Added example project file and updated project file defaults. 2023-09-19 10:06:22 +02:00
Dmitry Atamanov
a06cc76c9b Support additional keys in projects. 2023-09-19 09:56:49 +02:00
Christoffer Lerno
9eef34049d Remove vestiges of top down widening. 2023-09-19 09:45:56 +02:00
Dmitry Atamanov
e91cb85a66 Update checkout action to v4 2023-09-19 08:15:17 +02:00
OdnetninI (Eduardo José Gómez Hernández)
05c2737f46 Fix timeout at tcp::connect 2023-09-18 23:47:01 +02:00
Christoffer Lerno
cbdc746c9d Fix alignment in new fetch builtins. 2023-09-17 21:07:00 +02:00
OdnetninI (Eduardo José Gómez Hernández)
8d11794f83 Fix atomic_fetch_sub builtin + Updated atomic library (#997)
Fix atomic_fetch_sub builtin + Updated atomic library
2023-09-17 13:22:12 +02:00
Christoffer Lerno
8ed9be9c58 Update build options --nostdlib --nolibc --emit-stdlib --forcelinker … (#999)
Update build options --nostdlib --nolibc --emit-stdlib --forcelinker --strip-unused. Fix error with vectors in $foreach. Also error if a $foreach iterating over an empty list. Rename forcelinker -> system-linker
2023-09-17 13:19:01 +02:00
Christoffer Lerno
d49365b4a7 Change how -O works and create -optsize / -optlevel. Update --safe / --fast. 2023-09-17 00:40:32 +02:00
Christoffer Lerno
03345bef10 Stricter checking of compare_exchange builtin. 2023-09-16 22:25:03 +02:00
Christoffer Lerno
ff05128a87 Add atomic_fetch_exchange builtin. 2023-09-15 18:22:37 +02:00
Christoffer Lerno
f6e18ded5b Add atomic_fetch builtins. 2023-09-15 18:07:15 +02:00
Dmitry Atamanov
d129cd49a5 Add libc-free hello_world 2023-09-15 16:46:38 +02:00
Christoffer Lerno
9233305bd6 Feature flags possible to add in project.json. See #991 2023-09-15 14:28:23 +02:00
Dmitry Atamanov
d6e9985a26 Add log(x, base) function to std::math module. 2023-09-15 09:22:34 +02:00
Christoffer Lerno
6be61aa19c Fixed asm parsing issue. Use of pointer as argument. 2023-09-15 08:25:43 +02:00
Dmitry Atamanov
4a232b7935 Add builtin benchmarks to changelog. [skip_ci] 2023-09-14 20:58:49 +02:00
Christoffer Lerno
44fafdbd7c Fix issue with asm_target initialized multiple times as mentioned in #989 2023-09-14 10:38:02 +02:00
Christoffer Lerno
2eddda9061 Add gather/scatter for vectors. 2023-09-14 10:19:15 +02:00
Christoffer Lerno
b2ac4b4253 Allow use of pointers in vectors. 2023-09-13 13:45:33 +02:00
Christoffer Lerno
1d04b70efe Fixed issues inferring length with subarrays. Removed old, non-working example. Infer length in the case of subarray literals. 2023-09-12 22:24:20 +02:00
Pierre Curto
d61482dffc fix Object.free (#982)
* lib/std/collections: add HashMap.@each_entry()
* lib/std/json: fix Object.free() when object is a map
* lib/std/collections: fix allocator use in Object.{set,set_at,append}
* lib/std: add char.from_hex
* lib/std/collections: print arrays and objects compactly
* lib/std/io: fix Formatter.vprintf result
* lib/std/io/stream: rename module for ByteBuffer
* lib/std/io/stream: make Scanner a Stream reader
* lib/std/io: make std{in,err,out} return File* if no libc
2023-09-12 13:49:52 +02:00
Christoffer Lerno
37bb16cca1 Updated cast code. 2023-09-12 12:48:52 +02:00
Christoffer Lerno
ca1885fe09 Updated releasenotes. 2023-09-09 22:50:25 +02:00
Christoffer Lerno
d67e846712 Remove cast from void! to anyfault. Rename @catchof to @catch 2023-09-09 22:49:32 +02:00
Christoffer Lerno
dfe097931c Add masked_load / masked_store 2023-09-09 01:05:51 +02:00
Christoffer Lerno
4ef74a1205 Add $$select. "--fp-math" options. Fixed issue with accidentally silent error on failed vector conversions. 2023-09-08 09:20:27 +02:00
Pierre Curto
b894e5be69 lib/std/encoding: remove use of pushback_byte in json
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-09-08 09:05:38 +02:00
Christoffer Lerno
dc7f8057a3 Set msvc compile-test to -O1 2023-09-07 09:42:05 +02:00
Christoffer Lerno
66b436f7f8 No optimizations by default. 2023-09-07 09:01:12 +02:00
Christoffer Lerno
224e38c6c7 Fixes to features. 2023-09-06 23:49:56 +02:00
Dmitry Atamanov
51a72ccd37 Add new x86 cpu features. 2023-09-06 23:38:24 +02:00
Dmitry Atamanov
40d5ce0937 Fixes $$set_rounding_mode builtin. 2023-09-06 23:11:33 +02:00
Pierre Curto
51f76c69c4 lib/std/collections: add Bitset
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-09-06 23:09:37 +02:00
Christoffer Lerno
b87e27d8a3 Update tests and CI (#979)
Update CI. Explicit native mutex "initialized" bool.
2023-09-06 22:43:07 +02:00
Christoffer Lerno
50e99b571f Add frame pointer on "enable stacktrace". Set no-trapping-math. Update fmuladd. 2023-09-06 14:38:21 +02:00
Christoffer Lerno
e3412da033 Removed broken code. Update formatter for precision. Fix of panic. 2023-09-05 22:53:56 +02:00
Christoffer Lerno
69418ba44d Fix issue in is_autoimport ordering. 2023-09-05 19:45:42 +02:00
Christoffer Lerno
cfe5c649c5 Prevent ordering issues with "builtin" by resolving it early. 2023-09-05 17:29:54 +02:00
Pierre Curto
f8fa9a057e lib/std/io: support . string format speficier (#970)
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-09-05 14:39:51 +02:00
Christoffer Lerno
5a2ef79fe6 Update "not working" examples. 2023-09-05 14:36:17 +02:00
Christoffer Lerno
74649ef672 Fix of from_float. 2023-09-05 13:52:14 +02:00
Christoffer Lerno
e5f9cc26a8 Updated lexer suffixes. 2023-09-05 13:48:39 +02:00
Christoffer Lerno
6744a41644 Updated grammar for definitions. 2023-09-05 13:40:19 +02:00
Christoffer Lerno
ffb7935e12 Updating time duration functions. 2023-09-05 10:57:50 +02:00
Christoffer Lerno
53598b8c40 Make ZString print natively with %s. 2023-09-04 22:30:35 +02:00
Christoffer Lerno
fe0ae4a9aa Error when splat is used with raw varargs. 2023-09-04 22:24:46 +02:00
Christoffer Lerno
d1bb9c55ee Add blocking connection with timeout, and initial poll functionality. 2023-09-03 19:03:00 +02:00
Christoffer Lerno
29cc9ad8b1 Order IoError declarations. 2023-09-03 10:32:26 +02:00
Christoffer Lerno
4c081f59ff Refactoring, adding printf / printfn to all streams. 2023-09-03 10:14:04 +02:00
Christoffer Lerno
9a6d83f526 Updated stream API. 2023-09-03 01:14:15 +02:00
Christoffer Lerno
a6cff5c2a5 Removal of old Network, added nonblocking set and async connect. 2023-09-02 17:39:51 +02:00
Pierre Curto
e56313a204 use slice assign and add List.*using_test functions (#967)
* std/collections: use slice assignment
* std/collections: add List.remove_using_test and List.retain_using_test
2023-09-01 12:16:15 +02:00
Christoffer Lerno
70b9e811bd Update of enummap. 2023-09-01 11:19:20 +02:00
Christoffer Lerno
46582af0ae Add contracts to memcpy. Fix bug when compile time local declarations are used as expressions. This caused a check to be invalid in the @pool code. 2023-09-01 11:13:43 +02:00
Christoffer Lerno
0387816cb9 Gracefully handle unlocalized errors. Fix collisions with tests and using $test variable. 2023-09-01 10:03:09 +02:00
Christoffer Lerno
b6756b5b35 Overlapping slice assign is now safe. 2023-09-01 09:40:07 +02:00
Christoffer Lerno
34e1f89ded Changed atomic calls to be macros. 2023-09-01 08:32:38 +02:00
Christoffer Lerno
0142e5fd33 Removed the need for lambda in remove_if, removed potential implementation specific behaviour. 2023-08-31 23:39:00 +02:00
Pierre Curto
cd950a0359 lib/std/collections: fix comments, List.remove_if() and List.retain_if() methods
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-31 23:20:50 +02:00
Christoffer Lerno
770115abd1 Fix default library linking order. 2023-08-31 20:20:51 +02:00
Christoffer Lerno
faf7782b1e Fix problems with slice assign over distinct types. 2023-08-31 13:41:21 +02:00
Christoffer Lerno
82ba02a904 Allow indexing of constant strings. Fixed reverse indexing of constant initializers. 2023-08-30 16:36:29 +02:00
Christoffer Lerno
dd0dc1a936 Allow anyfault and any aliasing. Fix any comparison. 2023-08-30 13:56:16 +02:00
Christoffer Lerno
eac19814e1 Make typeid switches always use subtype matching. Update seeder mixing. 2023-08-29 22:48:26 +02:00
OdnetninI (Eduardo José Gómez Hernández)
7aca8a02cb Architecture generic Atomics (C11 + extras) (#958)
* Initial implementation for c11 atomics
* Fix cmpxchg usage
* Support for floating point atomics
* Added atomic min and max
* Updated copyright notice
* Removed Floats from and or xor. Added mul, div, bitshift
* Changed get_atomic_compatible_type to lower_to_atomic_compatible_type
* Require non-null pointers
* Fix spacing
* Added Atomic type
* Added macro to reduce code
* Small reorder and cleanup
* Added cmpxchg constrains
* Apply all the restrictions for atomic loads/stores and cmpxchg
2023-08-29 14:25:43 +02:00
Dmitry Atamanov
efb492eace Add simple benchmark runner. 2023-08-29 12:28:06 +02:00
Christoffer Lerno
79f964dce9 Fix of atomic checks. Renamed MONOTONIC -> RELAXED. 2023-08-29 12:27:40 +02:00
Christoffer Lerno
a23112fae6 Added parentof. 2023-08-29 11:51:09 +02:00
Pierre Curto
092296984a std/math/random: add missing SimpleRandom.init() method
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-28 10:03:30 +02:00
Christoffer Lerno
565f511cc7 Updated error handling on test/benchmark attribute. 2023-08-28 08:17:57 +02:00
Dmitry Atamanov
b8c92c69b0 Implement builtin benchmarks. 2023-08-28 08:13:21 +02:00
Christoffer Lerno
6ebb3caa20 Fix issue where type wasn't canonical. Addresses #957 2023-08-27 19:04:34 +02:00
Dmitry Atamanov
cc2c737357 Fix calling llvm::writeArchive for LLVM > 17. 2023-08-27 18:11:24 +02:00
Christoffer Lerno
69553fd80e Bitstruct designated initializer sometimes failed. This addresses. #954 2023-08-27 18:10:22 +02:00
Christoffer Lerno
190e1b19f1 Fix of next_short 2023-08-26 16:17:30 +02:00
Pierre Curto
c09b6154f4 std/lib/collections: add Object.get_len(); add some tests for encoding/json
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-26 13:24:23 +02:00
Pierre Curto
120d5a672c std/collections: Object.get* return an error if requested type if invalid
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-26 13:24:23 +02:00
Pierre Curto
307212a19c std/encoding: use char.is_digit() where applicable
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-26 13:24:23 +02:00
Pierre Curto
eedb2c3c52 std/collections: add RingBuffer.popc()
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-26 13:24:23 +02:00
Christoffer Lerno
b1f52cf8a9 Updated random interface further. 2023-08-26 13:22:02 +02:00
Christoffer Lerno
bea9ac010c Updated random interface. 2023-08-26 12:58:57 +02:00
Christoffer Lerno
6ebd437a5f Fix bug when converting from typedef to distinct. Ordered struct fields. Update debug type when returning an optional. 2023-08-25 14:11:23 +02:00
Pierre Curto
c0b109fbc1 #934 followup
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-24 18:10:18 +02:00
Dmitry Atamanov
b77f254ab1 Tune @expect, @likely, @unlikely and @prefetch macros. 2023-08-24 16:09:56 +02:00
Dmitry Atamanov
f5fea69ef9 Move get_var, set_var and clear_var to os::env module. 2023-08-24 13:31:48 +02:00
Pierre Curto
c63b3d4209 Small updates to std::lib (#949)
* lib/std/io: add Stream.supports_{read,Write}_byte and Stream.write_all
* std/io/: use char.digit()
2023-08-24 11:59:00 +02:00
Christoffer Lerno
056ffa5876 Fix bug dropping bounds checks for arrays. This addresses #943. 2023-08-23 14:21:11 +02:00
Christoffer Lerno
0120498ec8 Fix seeder, update with dynamic interface for random. Insert unreachable after panic in asserts. Macro ensure static check. 2023-08-23 13:52:27 +02:00
Christoffer Lerno
0b67f1a8e4 Fix windows .dyn_search. 2023-08-22 21:34:10 +02:00
Christoffer Lerno
16e71c14b9 New resolution of $define only check on the last element. Fix issue with pointer and array element types not considered live. 2023-08-22 18:16:26 +02:00
Christoffer Lerno
27445e6c1d Semi-fix of $embed on empty. Inline caches for dynamic dispatch. 2023-08-22 15:24:21 +02:00
Dmitry Atamanov
fcb4bc0781 Reimplement QuickSort (non-recursive modification). 2023-08-22 14:48:12 +02:00
Christoffer Lerno
6c60b0d2a6 Update errno listings. Update ai flags in std::net. Fix incorrect socket error results on Win32. Change behaviour Socket set_option. TcpSocket/TcpServerSocket/UdpSocket. Rename "TimeDuration" to "Duration". Allow @if on enum values. 2023-08-19 22:41:54 +02:00
Christoffer Lerno
ed70f39da8 Update the json API 2023-08-19 00:53:28 +02:00
Christoffer Lerno
d5aebb434c Eliding length for ":"-ranges is no longer allowed. 2023-08-18 22:25:51 +02:00
Christoffer Lerno
17f69d8da8 Update slice assign so that it looks at the arguments and does slice copying or splat assign as needed. 2023-08-18 18:46:38 +02:00
Christoffer Lerno
c07dc700df Fix of #936. Also some general cleanup. 2023-08-18 15:55:43 +02:00
Christoffer Lerno
957ce320ae Cleanup and size reduction of Ast/Expr. 2023-08-18 01:57:56 +02:00
Pierre Curto
7a39933c97 add NanoDuration.to_format() (#935)
* lib/std/time: avoid switch in DateTime.compare_to()
* lib/std/time: add NanoDuration.to_format()
* std/lib: fix #934
2023-08-17 10:30:20 +02:00
Christoffer Lerno
9b0da89a03 Added compare_to as a standard macro. 2023-08-17 10:13:00 +02:00
Christoffer Lerno
b05ba8d110 Updated the test runner code slightly. 2023-08-16 17:31:16 +02:00
Christoffer Lerno
0448038c68 Added test. 2023-08-16 15:58:33 +02:00
Christoffer Lerno
e694d60f23 Updates and fixes to Mutex (#933)
Updating Mutex to have specific types: TimedMutex, RecursiveMutex, TimedRecursiveMutex. Fixes to the win32 implementation.
2023-08-16 15:45:49 +02:00
Christoffer Lerno
8a4337e819 Some improvements for the stacktrace. 2023-08-16 13:54:21 +02:00
Pierre Curto
5bd21c10b6 improve tests (#932)
* test: fix warnings generated by Python's interpreter

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/core/runtime: sort tests to run; improve tests output

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-16 12:28:07 +02:00
Christoffer Lerno
87c9c29ee8 Make -O1 the default, not -O2. 2023-08-16 10:51:43 +02:00
Pierre Curto
6c3d6a4b05 std/lib: fix module path for RingBuffer
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-15 15:58:28 +02:00
Christoffer Lerno
f39dd82adc Fix issue where imports could be made more than once. Addresses #929 2023-08-15 10:47:40 +02:00
Pierre Curto
f4ad9fcee0 move num_cpu() to std::os (#928)
* lib/std: move num_cpu() to std::os

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* add ThreadPool (#926)

* lib/std/collections: fix tab indentation

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/threads: add ThreadPool

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* ats/lib/threads: add num_cpu()

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/os: move macos constants to std::os::macos

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-14 17:04:06 +02:00
Pierre Curto
65bea1cb2d add ThreadPool (#926)
* lib/std/collections: fix tab indentation

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/threads: add ThreadPool

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* ats/lib/threads: add num_cpu()

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-14 15:33:51 +02:00
Christoffer Lerno
f912e53038 Fix where designated initializers had optional arguments. See #923 2023-08-13 20:57:50 +02:00
Christoffer Lerno
3c8bbc2b90 Fix issue combining void! in macros in some cases. See #927 2023-08-13 20:35:49 +02:00
Christoffer Lerno
e22afe5424 Allow "if (try foo())"." 2023-08-13 18:15:20 +02:00
Pierre Curto
c060569599 add tests for Mutex (#925)
* std/lib: add tests for Mutex

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/collections: add missing import

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* std/collections: add RingBuffer

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-13 17:39:09 +02:00
Christoffer Lerno
d83f591184 Check for mutex initialization. Fix missing check on optional for certain macro situations. 2023-08-12 11:55:49 +02:00
Christoffer Lerno
b6f302d1c6 Fix issues with thread, add some simple test. 2023-08-12 02:16:46 +02:00
Pierre Curto
a846ab9cc0 std/lib/threads: fix typo
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-11 21:08:17 +02:00
Christoffer Lerno
f0d4c4d2ce Ensure type is checked before analysis of compound literals. #919 2023-08-11 18:15:39 +02:00
Christoffer Lerno
3e765a3f3e Hash maps now copy keys if keys are copyable. 2023-08-10 21:14:24 +02:00
Christoffer Lerno
356b6bb1b7 Fixed test. 2023-08-10 19:09:00 +02:00
Christoffer Lerno
951a9f2b43 Fix of ++ and -- on bitstructs. 2023-08-10 18:01:50 +02:00
Christoffer Lerno
6d870fbef0 Fix for arithmetic promotion of aliases. Some work towards $exec scripting. 2023-08-10 17:14:29 +02:00
Christoffer Lerno
01a89e2145 Fix bug where library source files were sometimes ignored. 2023-08-09 21:34:58 +02:00
Christoffer Lerno
6df6d2c084 Update type changes. 2023-08-09 02:06:50 +02:00
Christoffer Lerno
2ca67d1489 Fix void[] -> char[] cast. 2023-08-09 01:03:06 +02:00
OdnetninI (Eduardo José Gómez Hernández)
eec6ce2210 README: Updated instructions for AUR (#915)
* README: Updated instructions for AUR

* README: Missing space
2023-08-08 16:42:42 +02:00
Eduardo José Gómez Hernández
c44a0528df Tests:std/lib/io/file: Added unit tests for read_any and write_any 2023-08-08 10:12:36 +02:00
Christoffer Lerno
5b5bc7fdbb Fix void[] -> char[] cast. 2023-08-07 20:58:20 +02:00
Christoffer Lerno
68aadc958f Added read_any/write_any. 2023-08-07 19:55:10 +02:00
Christoffer Lerno
91bb31856b Introduce initial $exec. 2023-08-07 19:43:57 +02:00
Christoffer Lerno
a864822a89 Do not assert when encountering resolved non-identifier access child expressions. Addresses #913 2023-08-04 15:50:21 +02:00
Christoffer Lerno
a1a0958415 Prevent parameters to end with comma. 2023-08-03 14:37:23 +02:00
Christoffer Lerno
def97eea9d Fixes member visibility for anonymous bitstruct. Bitstruct member attributes works. Anonymous bitstruct assignment fixed. 2023-08-03 01:00:30 +02:00
Pierre Curto
e4febe62ef lib/std/io: make PathWalker return an optional
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-02 23:05:28 +02:00
Christoffer Lerno
fc0973f378 Fix issue casting an untyped list. Addresses #908. 2023-08-02 23:03:50 +02:00
Pierre Curto
9b1c75d061 std/lib: simplify String.{,r}index_of and improve speed for the index… (#907)
* std/lib: simplify String.{,r}index_of and improve speed for the index_of one

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/collections: add EnumMap.get_ref

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-02 11:43:58 +02:00
Christoffer Lerno
09bb7d3525 Fix issue with type_info not being completely poisoned when encountering an error. Fixes #905 2023-08-01 12:26:49 +02:00
Pierre Curto
701d6a0746 std/lib/io: add Scanner (#904)
* std/lib/io: add Scanner

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/core: use existing methods in String.convert_ascii_to_{lower, upper}

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-08-01 10:47:21 +02:00
Christoffer Lerno
a0df1fd728 Add missing keys to project. 2023-07-31 21:15:05 +02:00
Christoffer Lerno
6a3e618ffd Another overeager type resolution removed. 2023-07-31 14:19:41 +02:00
Christoffer Lerno
d63bc10d74 Fix issue with type properties. 2023-07-31 14:02:20 +02:00
Christoffer Lerno
4fd45700a2 Fix issue with type properties. 2023-07-31 13:55:53 +02:00
Christoffer Lerno
f0c0efca8d Remove eager resolve. 2023-07-31 12:46:51 +02:00
Christoffer Lerno
72f5bac346 When a member is checked, still add it to the environment. Addresses #903. 2023-07-31 12:35:41 +02:00
Christoffer Lerno
20699c1262 Fix missing ] 2023-07-31 11:00:25 +02:00
Christoffer Lerno
8a335fc64c Updated stack trace. This addresses #898. 2023-07-31 10:57:56 +02:00
Christoffer Lerno
8a9522a363 Updated release notes. 2023-07-31 10:43:15 +02:00
Christoffer Lerno
9315443866 Fix issue where lambdas were copied incorrectly from generics. This addresses #900. 2023-07-30 21:12:02 +02:00
Christoffer Lerno
95cb2cc28e Removal of def Boo = Bar<int>; Delay array checking for flexible array. Replace resolve_fn_ptr with resolve_type_structure. Resolve type structure before entering initializers. 2023-07-30 19:59:26 +02:00
Christoffer Lerno
151fc83815 Fix issue with attribute argument resolution. 2023-07-30 16:46:31 +02:00
Christoffer Lerno
b759abc954 Fix to DString reserve. 2023-07-29 22:42:55 +02:00
Pierre Curto
6808a38c9f add std::io::stream::ByteBuffer; fix std::io::Path::walk (#895)
* lib/std/io/stream: add some inlines

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/io/stream add ByteBuffer

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/io/path: fix free of paths in walk

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/bits: remove unnecessary receiver type

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-29 21:21:45 +02:00
Christoffer Lerno
108b2244d8 Fix bug in aligned_realloc, fixes issue when a type is not resolved before the cast. #897 2023-07-28 22:21:23 +02:00
Christoffer Lerno
283a95dea2 Fix of issue where generic faults and enums would not get the "parent" id correctly fixed up. This addresses #896. 2023-07-28 21:49:22 +02:00
Christoffer Lerno
1219e8ba37 Fix accidental tracing preventing test from working. 2023-07-28 11:14:38 +02:00
Christoffer Lerno
ada3ea08fc Some initial parse output. Enforce handling of optionals. Fix issue where constants were folded despite the fact that they shouldn't be. Fix bug related to return foo() where foo() returns void!. (#893) 2023-07-27 22:58:41 +02:00
Pierre Curto
d0fa473d61 lib/std/io: assert in Formatter.out_str on "Invalid enum" if an enum value is out of range (#892)
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-27 21:18:31 +02:00
Christoffer Lerno
7b0408f79d Adding feature flags. 2023-07-26 23:28:11 +02:00
Christoffer Lerno
c18526f10a Fix of nested union/struct initialization. Fixes #886. 2023-07-26 21:12:04 +02:00
Christoffer Lerno
499c82b089 Updated indentation to C3 standard. 2023-07-26 14:01:24 +02:00
Pierre Curto
a376d8e2bf add ERRNO for macOS, improve net error messages (#885)
* lib/std/libc: add ERRNO values for macOS

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/net: improve error messages

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-26 13:41:35 +02:00
Pierre Curto
59b077223b use IoError.UNSUPPORTED_OPERATION instead of asserts; improve Path.walk
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-26 13:05:12 +02:00
Christoffer Lerno
9c503cf6fd Update to handle LLVM 18 part 2 2023-07-26 01:19:06 +02:00
Christoffer Lerno
7954db9a89 Update to handle LLVM 18 2023-07-26 00:56:45 +02:00
Christoffer Lerno
3929e2057d Change unreachable code to error. Remove unreachable where the compiler now can detect reachability. 2023-07-25 23:31:05 +02:00
Pierre Curto
242006d05d add is_absolute and absolute methods to path::Path (#882)
* lib/std/io/os: remove unnecessary dup in native_ls

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/core: add String.index_of_char and String.rindex_of_char

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/io: add Path.is_absolute

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/io: add Path.absolute

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std: fix Path.normalize on files starting with `.`; add Path.walk

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-25 23:23:56 +02:00
Christoffer Lerno
b74b62e242 Fixes to broken docs parsing. Fixes #880. 2023-07-25 17:55:46 +02:00
Christoffer Lerno
d0c00b859b Fixes incorrect type resolution of && and || with optionals. Fixes #879 2023-07-25 15:16:47 +02:00
Pierre Curto
3e78a70552 lib/std/io: replace IoError.FILE_EOF with IoError.EOF
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-25 11:32:47 +02:00
Christoffer Lerno
fad0adfcd0 Fix testcase. 2023-07-25 11:25:20 +02:00
Christoffer Lerno
9e477056ed Fixes to $defined implementation. 2023-07-23 23:55:38 +02:00
Christoffer Lerno
de9bb1d0cc Fix test. 2023-07-23 21:51:38 +02:00
Christoffer Lerno
45d1b1d671 Fix where bitstructs in subarrays triggered the wrong lowering. Fixes #877 2023-07-23 20:50:53 +02:00
Christoffer Lerno
afb902d792 Fixes #874 2023-07-23 16:24:02 +02:00
Christoffer Lerno
5f1ebdcd28 Fixes #873 2023-07-23 16:14:33 +02:00
Dmitry Atamanov
e72ec2f605 std::bits fixes. 2023-07-22 17:40:08 +02:00
Christoffer Lerno
d7d7fd3a10 Rewrite of function pointer handling. 2023-07-21 01:44:37 +02:00
Christoffer Lerno
7bccde72ed Fix missing declaration resolve when inlining enum data. 2023-07-20 18:34:02 +02:00
Christoffer Lerno
3f41e58dbd Fix pseudo-circular function pointer definitions. 2023-07-20 12:49:02 +02:00
Christoffer Lerno
581ecdb2a8 Only add a director as a link path if the directory can be found. Update raylib examples. 2023-07-19 22:06:57 +02:00
Christoffer Lerno
c4f8d5f25e Update range. 2023-07-19 21:56:52 +02:00
Dmitry Atamanov
7fbedae604 Add has function to Range and ExclusiveRange. 2023-07-19 21:45:14 +02:00
Pierre Curto
b453186de5 lib/std/collections: add init and tinit methods to PriorityQueue
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-19 21:44:20 +02:00
Dmitry Atamanov
49ea950f78 Add is_even and is_odd to math module 2023-07-19 11:38:49 +02:00
Christoffer Lerno
5aae9f3204 Correctly apply fixup to switch macros. Addresses new issue in #862. Updates @pool. 2023-07-19 11:38:18 +02:00
Christoffer Lerno
5c2e82fc8b More use of temp allocator. 2023-07-19 02:46:43 +02:00
Christoffer Lerno
5a2fe4c9d9 Fix to macro codegen. 2023-07-19 01:34:50 +02:00
Christoffer Lerno
4dcfb7a675 Fix of #862 where enums declarations where not regenerated. Updated @pool implementation. 2023-07-18 23:23:53 +02:00
Pierre Curto
491c5ceec5 support for List in quicksort; add HashMap.@each (#861)
* compiler: fix typo in error message about failed name resolution

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/sort: add List support to quicksort

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/collections: add HashMap.@each

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-18 12:18:12 +02:00
Christoffer Lerno
2e6c8721bc Fix of is_comparable. 2023-07-17 21:22:16 +02:00
Pierre Curto
fd5336c56e lib/std/io/stream: add LimitReader (#858)
* lib/std/io/stream: add LimitReader

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std: more method conversions to use new receiver notation

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-17 20:22:29 +02:00
Christoffer Lerno
209d994336 Finalize subprograms after writing them. Correct debug info for C varargs. Add uwtable metadata. Removed visibility from functions. 2023-07-17 20:21:10 +02:00
Christoffer Lerno
aa216fa510 Dev (#859)
* Fix bug where analysing subexpr relied on them not being analysed. Fix issue where converting a const initializer bool to integer failed. Fix of issue where the case check assumed other cases were const values.

* Fix bug where analysing subexpr relied on them not being analysed. Fix issue where converting a const initializer bool to integer failed. Fix of issue where the case check assumed other cases were const values. Remove PTHREAD for windows.

* Fix bug where analysing subexpr relied on them not being analysed. Fix issue where converting a const initializer bool to integer failed. Fix of issue where the case check assumed other cases were const values. Remove PTHREAD for windows.
2023-07-17 02:00:27 +02:00
Pierre Curto
89e084938f cross platform socket interface (#857)
* lib/std/net: add Network, Socket and Listener

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/net: add SocketOption

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/net: fixes for win32 and wasm

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-16 14:14:36 +02:00
Christoffer Lerno
11eb187fee Allow init from slices: float[4] x = foo[:4] 2023-07-16 01:23:12 +02:00
Christoffer Lerno
8a4e6f7dd3 Fix bug where @local declarations would get registered as @private for generic modules. #856. Update to consistent tab spacing. 2023-07-15 19:27:24 +02:00
Pierre Curto
35bffdadc2 improve the sort and collections libs (#853)
* lib/std/sort: unify binarysearch and binarysearch_with; add comments to quicksort

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/collections: mark List.{len, is_empty, get} with @inline

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/collections: add PriorityQueueMax; add tests for PriorityQueue and PriorityQueueMax

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-15 19:08:54 +02:00
Christoffer Lerno
97e5dbf62c Allow enum and fault constants to parameterize modules. 2023-07-15 17:03:52 +02:00
Christoffer Lerno
4325d017c8 Compacted section list. Somewhat more controlled ctarg search. 2023-07-15 16:31:53 +02:00
Christoffer Lerno
34306cbf5d Fixes missing checks to body arguments. Do not create debug declaration for value-only parameter. Bug in alignment for atomics. Macro ref parameters are pointers. 2023-07-15 15:11:44 +02:00
Kenta
90d91b4891 Update build-with-docker.sh
Remove debug echo call in config check
2023-07-14 10:39:29 +02:00
Christoffer Lerno
ab32e0dc9d Fix for tests & callstack. 2023-07-13 21:48:48 +02:00
Christoffer Lerno
26ee4babcf Native_fopen for Win32 had incorrect arguments. 2023-07-13 19:54:27 +02:00
Christoffer Lerno
c7d90baad1 Error message on bus error or segmentation fault. Some additional SIG… (#848)
* Error message on bus error or segmentation fault. Some additional SIG info. Full debug info by default. Trapping is now debugtrap rather than trap for LLVM. Row now initialized when entering function for stacktrace.
2023-07-13 15:25:06 +02:00
Christoffer Lerno
c99f298cad Fix issue where rethrow in a macro not allowing optional caused an assert to trigger. #849 2023-07-13 12:34:15 +02:00
Christoffer Lerno
fc316b1031 Improve the error message on accidentally passing a ref parameter as a pointer. 2023-07-11 12:18:18 +02:00
Pierre Curto
1ffe430df0 lib/std/sort: use Hoare's algorithm in quicksort
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-11 11:09:51 +02:00
Pierre Curto
0efb142c88 std/lib/sort: avoid overflow in binary search
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-11 11:09:51 +02:00
Christoffer Lerno
dddeca1856 Remove assert(try). Prevent nextcase with expression if there is no switch. Fix issue with multiple declarations and compile time variables. Nextcase default. Dropped support for non-const-int ranges. 2023-07-11 01:04:35 +02:00
Christoffer Lerno
3b0370c8bb Fix of tests. 2023-07-10 20:21:12 +02:00
Pierre Curto
2437573a8f lib/std/io: add Stream.read_all (#843)
* lib/std/io: add Stream.read_all

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/core: use shortened receiver notation

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-10 20:13:31 +02:00
Christoffer Lerno
e2676a5c7f Change zero terminated wide strings to use WString over Char16 2023-07-09 20:10:17 +02:00
Christoffer Lerno
68af987c60 Version update. 2023-07-09 16:25:37 +02:00
Christoffer Lerno
cd73b9bc42 Require exhaustive enum switching. 2023-07-09 16:25:15 +02:00
IgneousRed
943d010dfc New Rngs (#841)
* New Rngs

* Mistake

* fix Mcg using wrong constant
2023-07-09 15:00:19 +02:00
Christoffer Lerno
38cc24af27 binarysearch is now a builtin, quicksort as well. 2023-07-09 01:49:43 +02:00
Christoffer Lerno
053f7880e5 Simplified quicksort with $switch. 2023-07-09 01:46:45 +02:00
Christoffer Lerno
9543fbbf1c Exhaustive switch on enums. This addresses #838 2023-07-09 01:18:01 +02:00
Christoffer Lerno
8b605d9183 $embed. 2023-07-09 01:18:01 +02:00
Pierre Curto
77b3214746 std/lib/sort: update quicksort to use the new generics
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-08 14:06:40 +02:00
Christoffer Lerno
5f711408c0 Fix compiler error when taking the address of a parameterized function. Fix issue when being generic over a function type, this fixes #836. 2023-07-08 13:32:32 +02:00
Pierre Curto
d709c18f5f std/lib/core: rename DString.str to DString.as_str (#834)
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-08 00:10:04 +02:00
Christoffer Lerno
8780df8467 Correctly treat distinct inline types as having their inner type's methods available. 2023-07-07 13:54:33 +02:00
Pierre Curto
7dc1eab185 std/lib/collections: make List support the []= operator (#831)
* std/lib/collections: make List support the []= operator

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* std/lib/io: rename receiver to self

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-07 13:51:44 +02:00
Christoffer Lerno
79e2d683b6 Live tracing failed to trace expression types. This fixes #832. 2023-07-07 12:00:59 +02:00
Christoffer Lerno
4f7b42cdc4 New generic syntax and ad hoc invocation. 2023-07-06 23:43:36 +02:00
Christoffer Lerno
276281c3f9 Fix of "INLINE" function. 2023-07-06 21:33:27 +02:00
Christoffer Lerno
b74de0b1e4 Fix issue where array comparison would emit the wrong basic block reference to phi when underlying comparison would emit a block. 2023-07-06 20:29:00 +02:00
Pierre Curto
df9bc377dd std/lib/net/os: add bind, listen and accept for posix platforms
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-06 17:36:54 +02:00
Christoffer Lerno
97ded16ea2 Fix issue where macros with bodies are not filtered from liveness tracing. #818 2023-07-06 17:30:50 +02:00
Pierre Curto
731729cf1b std/lib/hash: rename receiver to self
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-04 21:07:20 +02:00
Christoffer Lerno
daa952d990 Update quicksort. 2023-07-04 21:03:48 +02:00
Pierre Curto
f8a3e4f6f0 add basic quicksort support (#816)
* lib/std/sort: refactor binarysearch namespace to prepare for sorting

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* std/lib/sort: add basic quicksort support

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/hash: use method first parameter inferred type

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/hash: add fnv64a support

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-04 20:15:03 +02:00
Christoffer Lerno
6231cc83d9 Reduce recursion max further. 2023-07-04 18:51:34 +02:00
Christoffer Lerno
20c0bbc911 Reduce recursion max. 2023-07-04 18:39:22 +02:00
Christoffer Lerno
559dcffdf2 Do not poison macro body. 2023-07-04 18:30:12 +02:00
Christoffer Lerno
7ed0aeced2 Should fix #814. 2023-07-04 16:30:09 +02:00
Christoffer Lerno
c249c3f3b6 Fix of accidentally printing "prev" when using $checks. Updated binary search. 2023-07-04 02:29:02 +02:00
Pierre Curto
55d17ec990 add the std::sort::binarysearch module
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-04 01:35:10 +02:00
Christoffer Lerno
bbbcd9bf48 Updated name mangling for static vars in llvm. 2023-07-04 01:12:39 +02:00
Christoffer Lerno
c2c6f09d68 Fixes #811. Update some stdlib methods. Header printout correctly picks a name. #804 2023-07-03 15:45:06 +02:00
Christoffer Lerno
848a5212ef Fixes #811. Update some stdlib methods. Header printout correctly picks a name. #804 2023-07-03 13:17:43 +02:00
Tonis
eaf45436f8 Edit Matrix4 perspective fn 2023-07-02 20:02:59 +02:00
Christoffer Lerno
50784d4df6 Updated to use the new implicit type for method calls in some libraries. Made the grammar a little more liberal. 2023-07-02 10:55:25 +02:00
Christoffer Lerno
21d8a8b6da Fix #809 missing checks on generic types, accepting both types where constants should be and vice versa. 2023-07-01 23:58:00 +02:00
Christoffer Lerno
45820d45e5 Allow using inferred type on method first parameter. 2023-07-01 22:47:54 +02:00
Pierre Curto
2ac213a3ce lib/std/io/stream: add ReadBuffer and WriteBuffer streams
ReadBuffer and WriterBuffer buffer stream reads and writes to a stream.
Useful in situations where the underlying stream is sensitive to the number
of read or write calls.

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-07-01 17:38:09 +02:00
Christoffer Lerno
70ea6ce04b Fix #806, and also makes sure that things like FOO.x.a is a compile time value. 2023-07-01 16:29:17 +02:00
Christoffer Lerno
9102fc6032 Comments and cleanup. 2023-06-30 18:02:34 +02:00
Pierre Curto
378ea1deea Update std::io::Stream (#805)
* add missing newlines in output messages when creating libraries

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/io: add Stream.supports_flush; fix AvailableStreamFn

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-06-30 17:37:08 +02:00
Christoffer Lerno
f74e294dc2 Push stack codegen to function for cleaner binaries. Some refactoring in allocators. 2023-06-30 01:01:58 +02:00
Christoffer Lerno
57c8b5fc75 Fix on rethrow + macros. 2023-06-27 20:41:17 +02:00
Pierre Curto
550b1f23ec add missing newlines in output messages when creating libraries
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-06-27 14:06:00 +02:00
Christoffer Lerno
f651a59294 Fix on rethrow + macros. 2023-06-27 13:19:28 +02:00
Christoffer Lerno
fee80682b1 Update to error message when expecting a type. 2023-06-27 10:19:09 +02:00
Christoffer Lerno
b88916214f Fix of allocator gen. 2023-06-26 21:34:36 +02:00
Christoffer Lerno
685be0981f Fix priorityqueue. 2023-06-26 17:43:03 +02:00
Christoffer Lerno
fc054dad81 Added some to_string. 2023-06-26 16:21:07 +02:00
Christoffer Lerno
83f8bbb91b Update range to have exclusive and inclusive range. 2023-06-26 10:18:09 +02:00
Christoffer Lerno
0ec64c3be8 Remove broken free check on temp allocator. 2023-06-25 22:07:44 +02:00
Christoffer Lerno
f878191e6f Fix missing defer live tracing bug #801 2023-06-25 22:04:27 +02:00
Christoffer Lerno
8c73a450a1 Reduce memory consumtion. Add "range" 2023-06-25 21:39:53 +02:00
Pierre Curto
1dccd6af79 lib/std: display any caught error in tests
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-06-24 21:53:03 +02:00
Christoffer Lerno
dcfbca076f Fix to zero length strings and better error when using assert with a non-constant error value. 2023-06-24 20:32:30 +02:00
Christoffer Lerno
b68b1e01b3 Updated grammar. 2023-06-24 18:24:04 +02:00
Christoffer Lerno
be04473af4 Name change, some updates using "from end" indexing. 2023-06-24 18:21:16 +02:00
Christoffer Lerno
fedffc2f35 Name change, some updates using "from end" indexing. 2023-06-24 15:20:40 +02:00
Christoffer Lerno
a187c55dfe Improved pthread availability. 2023-06-24 14:23:42 +02:00
Pierre Curto
55a1f794cf lib/std/encoding: add base64 support
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-06-24 14:23:18 +02:00
Christoffer Lerno
68f6cb1286 Updated organization file functions, env naming. 2023-06-24 10:29:17 +02:00
Pierre Curto
0ab0f727ad lib/std: fix ByteWriter.read_from method (#793)
* lib/std: fix ByteWriter.read_from method

When reading from a stream which does not have an available method,
ByteWriter would not make any progress if its buffer was empty.

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* test/unit/stdlib/io: use separate module for TestReader

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-06-23 10:44:36 +02:00
Christoffer Lerno
c46017f0dc Make MSVC happy about definite assignment. 2023-06-23 00:26:32 +02:00
Christoffer Lerno
1bd729a4bb Remove accidental code include. 2023-06-23 00:17:13 +02:00
Christoffer Lerno
0eee9daf1d Macros generating lambdas now actually is a thing. 2023-06-22 23:42:40 +02:00
Christoffer Lerno
d90fa5e292 Make errno implicitly convertible to int. Add getaddrinfo on all platforms. Fix addrinfo struct and sizes. 2023-06-22 02:06:37 +02:00
Christoffer Lerno
503a4de277 Cleanup of libc 2023-06-21 16:27:53 +02:00
Christoffer Lerno
ae9fca52ca Update version 2023-06-21 16:27:53 +02:00
Christoffer Lerno
eddae3b7f7 Close linker context. 2023-06-21 16:27:53 +02:00
Christoffer Lerno
d5b01d3a8f Native ls 2023-06-21 16:27:53 +02:00
Christoffer Lerno
ab93389031 - Updated posix/win32 stdlib namespacing
- Process stdlib
- Fix to void expression blocks
2023-06-21 16:27:53 +02:00
Christoffer Lerno
5c9eb264e8 Delete object files after linking. 2023-06-21 16:27:53 +02:00
Christoffer Lerno
4d552ae44d Update $include syntax and behaviour. Remove top level $if completely. 2023-06-21 16:27:53 +02:00
Christoffer Lerno
3dd1741484 Fix of contract error location for @require #788 2023-06-20 21:35:48 +02:00
Christoffer Lerno
f9548cb213 Fix nested hash resolution for access identifiers. Fixes #789 2023-06-20 17:05:13 +02:00
Christoffer Lerno
c3da240bc0 Grammar fix. 2023-06-19 23:34:46 +02:00
Pierre Curto
f439539c6e lib::std::core::bitorder: add read and write
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2023-06-19 23:34:30 +02:00
Christoffer Lerno
57424d8b6b Fixes #786, constant initialization with constants sometimes causing an error lowering to LLVM. Fixes bug passing void as a vararg argument to an any vararg. 2023-06-19 10:38:37 +02:00
Christoffer Lerno
3bdeec3bc2 Incorrect defer/return value ordering in some cases. 2023-06-18 23:25:33 +02:00
Christoffer Lerno
2d46bdf8e3 $$trap and $$unreachable is automatically no_return. 2023-06-15 21:09:05 +02:00
Christoffer Lerno
5f87cb4c4f Fix of #780. 2023-06-13 18:30:57 +02:00
Christoffer Lerno
76d75ac375 Fix passing temporary objects by reference for methods. 2023-06-13 12:22:35 +02:00
Christoffer Lerno
75a6ae7111 Enable LLVM 15 2023-06-12 09:42:23 +02:00
Christoffer Lerno
cf83651c79 The new @if directive. 2023-06-11 18:56:37 +02:00
Christoffer Lerno
4c1edfb941 Dev (#777)
* The new @if directive.
2023-06-10 23:16:28 +02:00
Christoffer Lerno
82c3facb65 --obj, --emit-stdlib, --strip-unused 2023-06-09 09:37:07 +02:00
Christoffer Lerno
266dba466c Rename to no-emit-stdlib 2023-06-06 15:25:25 +02:00
Christoffer Lerno
379a5f670f Add no-obj and no-stdlib-codegen options. 2023-06-06 15:22:28 +02:00
Christoffer Lerno
8eaad81800 Dead strip by default. Add list to_string. Fix missing check for dynamic calls. 2023-06-05 14:54:17 +02:00
Christoffer Lerno
4baacc7d52 Formatting. 2023-06-03 12:08:11 +02:00
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
2960 changed files with 293126 additions and 54201 deletions

25
.editorconfig Normal file
View File

@@ -0,0 +1,25 @@
# EditorConfig is awesome: https://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
[CMakeLists.txt]
indent_style = space
indent_size = 4
[*.{c,cc,h}]
indent_style = tab
[*.{c3}]
indent_style = tab
[*.{json,toml,yml,gyp}]
indent_style = space
indent_size = 2
[*.{py,pyi}]
indent_style = tab

2
.gitattributes vendored
View File

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

14
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
# These are supported funding model platforms
github: [c3lang]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: c3lang
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -2,13 +2,117 @@ name: CI
on:
push:
branches: [ master, dev, ci_testing ]
branches: [ master, dev, ci_testing, experiments ]
pull_request:
branches: [ master ]
branches: [ master, dev ]
env:
LLVM_RELEASE_VERSION_WINDOWS: 18
LLVM_RELEASE_VERSION_MAC: 17
LLVM_RELEASE_VERSION_LINUX: 17
LLVM_RELEASE_VERSION_UBUNTU20: 17
LLVM_DEV_VERSION: 21
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@v4
- 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 -L C:\ --print-linking examples\hello_world_many.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run --print-linking examples\time.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run --print-linking 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\ls.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\process.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\args.c3 -- foo -bar "baz baz"
..\build\${{ matrix.build_type }}\c3c.exe compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples\constants.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run msvc_stack.c3
- name: Build testproject
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32 --trust=full
dir build\llvm\windows-x64
..\..\build\${{ matrix.build_type }}\c3c.exe clean
dir build\llvm\windows-x64
- name: Build testproject lib
run: |
cd resources/testproject
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv build hello_world_win32_lib --trust=full
- name: Compile and run dynlib-test
run: |
cd resources/examples/dynlib-test
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv dynamic-lib add.c3
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
- name: Compile and run staticlib-test
run: |
cd resources/examples/staticlib-test
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv static-lib add.c3
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
- name: Vendor-fetch
run: |
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib5
- name: Try raylib5
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib5
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_arkanoid.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_tetris.c3
- name: Compile run unit tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1
- name: run compiler tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-run -O1 src/test_suite_runner.c3 --stdlib ..\lib7 --enable-new-generics -- ..\build\${{ matrix.build_type }}\c3c.exe test_suite7/ --stdlib ../lib7 --no-terminal
- name: Test python script
run: |
py msvc_build_libraries.py --accept-license
dir msvc_sdk
- name: upload artifacts
uses: actions/upload-artifact@v4
with:
name: c3-windows-${{ matrix.build_type }}
path: |
build\${{ matrix.build_type }}\c3c.exe
build\${{ matrix.build_type }}\c3c_rt
build-msys2-mingw:
runs-on: windows-latest
# if: ${{ false }}
strategy:
# Don't abort runners if a single one fails
fail-fast: false
@@ -19,29 +123,53 @@ jobs:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: git binutils mingw-w64-x86_64-mlir 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-clang 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-19.1.7-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-19.1.7-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_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run --print-linking examples/hello_world_many.c3
../build/c3c compile-run --print-linking examples/process.c3
../build/c3c compile-run --print-linking examples/time.c3
../build/c3c compile-run --print-linking examples/fannkuch-redux.c3
../build/c3c compile-run --print-linking examples/contextfree/boolerr.c3
../build/c3c compile-run --print-linking examples/load_world.c3
../build/c3c compile-run --print-linking examples/args.c3 -- foo -bar "baz baz"
../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build --debug-log
../../build/c3c run -vvv --trust=full
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib5
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --cc cc -vvv --trust=full
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c.exe test_suite/
../build/c3c.exe compile --target windows-x64 -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics
./test_suite_runner.exe ../build/c3c.exe test_suite7/ --stdlib ../lib7 --no-terminal
build-msys2-clang:
runs-on: windows-latest
@@ -56,7 +184,7 @@ jobs:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
@@ -66,64 +194,419 @@ jobs:
- 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
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build --debug-log
../../build/c3c run -vvv --trust=full
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib -vvv --trust=full
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c.exe test_suite/
../build/c3c.exe compile-run -O1 --stdlib ../lib7 src/test_suite_runner.c3 --enable-new-generics -- ../build/c3c.exe test_suite7/ --stdlib ../lib7 --no-terminal
build-linux:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [12, 13, 14, 15]
llvm_version: [17, 18, 19, 20, 21]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install common deps
run: |
sudo apt-get install zlib1g zlib1g-dev python3
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
- name: Install Clang ${{ matrix.llvm_version }}
- 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}}" < 15 ]]; then
if [[ "${{matrix.llvm_version}}" < 18 ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
sudo apt-get update
sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev libmlir-${{matrix.llvm_version}} \
libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
else
if [[ "${{matrix.llvm_version}}" < "${{env.LLVM_DEV_VERSION}}" ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
sudo apt-get update
sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
else
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
sudo apt-get install -y -t llvm-toolchain-focal libpolly-${{matrix.llvm_version}}-dev \
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
fi
fi
- name: CMake
if: matrix.llvm_version < 18 || matrix.llvm_version == env.LLVM_DEV_VERSION
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: CMake18
if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION
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}}.1
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-only examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile-only 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
../build/c3c compile-run examples/process.c3
../build/c3c compile-run examples/ls.c3
../build/c3c compile-run --linker=builtin linux_stack.c3
../build/c3c compile-run linux_stack.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
- name: Compile and run dynlib-test
run: |
cd resources/examples/dynlib-test
../../../build/c3c -vv dynamic-lib add.c3
mv add.so libadd.so
cc test.c -L. -ladd -Wl,-rpath=.
./a.out
../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=.
- name: Compile and run staticlib-test
run: |
cd resources/examples/staticlib-test
../../../build/c3c -vv static-lib add.c3
mv add.a libadd.a
cc test.c -L. -ladd
./a.out
../../../build/c3c compile-run test.c3 -L . -l add
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run -vvv --trust=full
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
- name: Install QEMU and Risc-V toolchain
run: |
sudo apt-get install opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf
- name: Compile and run Baremetal Risc-V Example
run: |
cd resources/examples/embedded/riscv-qemu
make C3C_PATH=../../../../build/ run
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run -vvv --linker=builtin --trust=full
- name: Init a library & a project
run: |
./build/c3c init-lib mylib
ls mylib.c3l
./build/c3c init myproject
ls myproject
- name: run compiler tests
run: |
cd test
../build/c3c compile-run -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics -- ../build/c3c test_suite7/ --stdlib ../lib7
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
run: |
mkdir c3
cp -r lib c3
cp msvc_build_libraries.py c3
cp build/c3c c3
tar czf c3-linux-${{matrix.build_type}}.tar.gz c3
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
uses: actions/upload-artifact@v4
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: [17, 18, 19, 20, 21]
steps:
- uses: actions/checkout@v4
- 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}}" < "${{env.LLVM_DEV_VERSION}}" ]]; 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
if [[ "${{matrix.llvm_version}}" > 12 ]]; then
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
fi
- name: CMake
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 Old
if: matrix.llvm_version < 18 || matrix.llvm_version == env.LLVM_DEV_VERSION
run: |
mkdir build && cd build
cmake .. -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} -DC3_LLVM_VERSION=${{matrix.llvm_version}} -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: CMake
if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION
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}}.1
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile examples/gameoflife.c3
../build/c3c compile-only examples/levenshtein.c3
../build/c3c compile-only examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/spectralnorm.c3
../build/c3c compile examples/swap.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile-run examples/hash.c3
../build/c3c compile-run examples/nbodies.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/contextfree/dynscope.c3
../build/c3c compile-run examples/contextfree/multi.c3
../build/c3c compile-run 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/load_world.c3
../build/c3c compile-run examples/base64.c3
../build/c3c compile-run examples/binarydigits.c3
../build/c3c compile-run examples/brainfk.c3
../build/c3c compile-run examples/factorial_macro.c3
../build/c3c compile-run examples/fasta.c3
../build/c3c compile-run examples/process.c3
../build/c3c compile-run --linker=builtin linux_stack.c3
../build/c3c compile-run linux_stack.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit --sanitize=address
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build --debug-log
../../build/c3c run -vvv --trust=full
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run -vvv --linker=builtin --trust=full
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
../build/c3c compile-run -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics -- ../build/c3c test_suite7/ --stdlib ../lib7
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
run: |
mkdir c3
cp -r lib c3
cp msvc_build_libraries.py c3
cp build/c3c c3
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz c3
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
uses: actions/upload-artifact@v4
with:
name: c3-ubuntu-20-${{matrix.build_type}}
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
build-with-docker:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
ubuntu_version: [20.04, 22.04]
build_type: [Release, Debug]
llvm_version: [17, 18, 19]
steps:
- uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Make script executable
run: chmod +x ./build-with-docker.sh
- name: Run build
run: |
LLVM_VERSION=${{ matrix.llvm_version }} UBUNTU_VERSION=${{ matrix.ubuntu_version }} CMAKE_BUILD_TYPE=${{ matrix.build_type }} ./build-with-docker.sh
- 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-only examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile-only 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
../build/c3c compile-run examples/process.c3
../build/c3c compile-run examples/ls.c3
../build/c3c compile-run --linker=builtin linux_stack.c3
../build/c3c compile-run linux_stack.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run -vvv --trust=full
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --reloc=none --target wasm32 -g0 --no-entry -Os wasm4.c3
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run -vvv --linker=builtin --trust=full
- name: Init a library & a project
run: |
./build/c3c init-lib mylib
ls mylib.c3l
./build/c3c init myproject
ls myproject
- name: run compiler tests
run: |
cd test
../build/c3c compile-run -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics -- ../build/c3c test_suite7/ --stdlib ../lib7
build-mac:
runs-on: macos-latest
@@ -132,28 +615,190 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [12]
llvm_version: [17, 18]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Download LLVM
run: |
brew install llvm@${{ matrix.llvm_version }} botan
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
brew install llvm@${{ matrix.llvm_version }} ninja curl
echo "/opt/homebrew/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
- name: CMake
if: matrix.llvm_version < 18
run: |
mkdir build && cd build
cmake .. -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: CMake18
if: matrix.llvm_version >= 18
run: |
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}}.1 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib5
- 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/process.c3
../build/c3c compile-run examples/load_world.c3
../build/c3c compile-run -O5 examples/load_world.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
- name: Compile and run dynlib-test
run: |
cd resources/examples/dynlib-test
../../../build/c3c -vv dynamic-lib add.c3
../../../build/c3c compile-run test.c3 -l ./add.dylib
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit -O1
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build --debug-log
../../build/c3c run -vvv --trust=full
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run -vvv --linker=builtin --trust=full
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib -vvv --trust=full
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
../build/c3c compile -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics
./test_suite_runner ../build/c3c test_suite7/ --stdlib ../lib7
- name: run build test suite runner
run: |
cd test
../build/c3c compile -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
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_MAC
uses: actions/upload-artifact@v4
with:
name: c3-macos-${{matrix.build_type}}
path: c3-macos-${{matrix.build_type}}.zip
build-nix:
runs-on: ubuntu-22.04
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [ Release, Debug ]
nixpkgs: [ Lock, Latest ]
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- name: Update flake (if necessary)
run: |
if [[ matrix.nixpkgs == "Latest" ]]; then
nix flake update
fi
nix flake info
- name: Build and check
run: |
if [[ ${{ matrix.build_type }} = "Debug" ]]; then
nix build -L ".#c3c-debug-checks"
else
nix build -L ".#c3c-checks"
fi
release:
runs-on: ubuntu-22.04
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu20]
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v4
- 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@v4
- 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: zip -r c3-windows.zip c3-windows-Release
- run: zip -r c3-windows-debug.zip c3-windows-Debug
- run: mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux-Release/c3-linux.tar.gz
- run: mv c3-linux-Debug/c3-linux-Debug.tar.gz c3-linux-Debug/c3-linux-debug.tar.gz
- run: mv c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
- run: mv c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
- run: mv c3-macos-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip
- run: mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-Debug/c3-macos-debug.zip
- id: create_release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: latest
release_name: latest
draft: false
prerelease: true
files: |
c3-windows.zip
c3-windows-debug.zip
c3-linux-Release/c3-linux.tar.gz
c3-linux-Debug/c3-linux-debug.tar.gz
c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
c3-macos-Release/c3-macos.zip
c3-macos-Debug/c3-macos-debug.zip

18
.gitignore vendored
View File

@@ -19,6 +19,7 @@
# Libraries
*.lib
*.tlb
*.a
*.la
*.lo
@@ -67,3 +68,20 @@ out/
/cmake-build-debug/
/cmake-build-release/
# Emacs files
TAGS
# Clangd LSP files
/.cache/
/compile_commands.json
# 'nix build' resulting symlink
result
# macOS
.DS_Store
# tests
/test/tmp/*
/test/testrun

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,13 +1,71 @@
cmake_minimum_required(VERSION 3.10)
project(c3c)
cmake_minimum_required(VERSION 3.20)
# 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 the project and version
project(c3c VERSION ${CMAKE_MATCH_1})
message("C3C version: ${CMAKE_PROJECT_VERSION}")
# Avoid warning for FetchContent
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
cmake_policy(SET CMP0135 NEW)
endif()
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
if (MSVC)
set(CMAKE_INSTALL_LIBDIR "c:\\c3c\\lib")
set(CMAKE_INSTALL_BINDIR "c:\\c3c")
else ()
set(CMAKE_INSTALL_LIBDIR "/usr/local/lib/c3")
set(CMAKE_INSTALL_BINDIR "/usr/local/bin/c3c")
endif()
endif ()
# 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)
message(STATUS "MSVC version ${MSVC_VERSION}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc /utf-8")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc /utf-8")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
else()
if (true)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O0 -fno-exceptions")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O0 -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")
else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined,address -fno-exceptions")
endif()
endif()
option(C3_LINK_DYNAMIC "link dynamically with LLVM/LLD libs")
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")
option(C3_WITH_LLVM "Build with LLVM" ON)
option(C3_LLD_DIR "Use custom LLD directory" "")
option(LLVM_CRT_LIBRARY_DIR "Use custom llvm's compiler-rt directory" "")
set(C3_USE_MIMALLOC OFF)
if(C3_USE_MIMALLOC)
@@ -16,145 +74,241 @@ if(C3_USE_MIMALLOC)
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}
mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
GIT_TAG ${C3_MIMALLOC_TAG}
)
FetchContent_MakeAvailable(mimalloc)
endif()
if(NOT C3_LLVM_VERSION STREQUAL "auto")
if(${C3_LLVM_VERSION} VERSION_LESS 12 OR ${C3_LLVM_VERSION} VERSION_GREATER 15)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
if (NOT WIN32)
find_package(CURL)
endif()
if(C3_WITH_LLVM)
if (NOT C3_LLVM_VERSION STREQUAL "auto")
if (${C3_LLVM_VERSION} VERSION_LESS 17 OR ${C3_LLVM_VERSION} VERSION_GREATER 21)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
endif()
endif()
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
else()
find_package(LLVM REQUIRED CONFIG)
endif()
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
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()
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
# Clangd LSP support
option(C3_ENABLE_CLANGD_LSP "Enable/Disable output of compile commands during generation." OFF)
if(C3_ENABLE_CLANGD_LSP)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
execute_process(
COMMAND ${CMAKE_COMMAND} -E create_symlink
${CMAKE_BINARY_DIR}/compile_commands.json
${CMAKE_SOURCE_DIR}/compile_commands.json
)
endif(C3_ENABLE_CLANGD_LSP)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if(C3_WITH_LLVM)
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "19")
endif()
FetchContent_Declare(
LLVM_Windows
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-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(llvm_dir ${llvm_windows_debug_SOURCE_DIR})
else()
message("Loading Windows LLVM libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows)
set(llvm_dir ${llvm_windows_SOURCE_DIR})
endif()
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3")
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()
set(LLVM_LINK_COMPONENTS
AllTargetsAsmParsers
AllTargetsCodeGens
AllTargetsDescs
AllTargetsDisassemblers
AllTargetsInfos
Analysis
AsmPrinter
BitReader
Core
DebugInfoPDB
InstCombine
IrReader
LibDriver
Linker
LTO
MC
MCDisassembler
native
nativecodegen
Object
Option
ScalarOpts
Support
Target
TransformUtils
WindowsManifest
if (EXISTS /usr/lib)
# Some systems (such as Alpine Linux) seem to put some of the relevant
# LLVM files in /usr/lib, but this doesn't seem to be included in the
# value of LLVM_LIBRARY_DIRS.
list(APPEND LLVM_LIBRARY_DIRS /usr/lib)
endif()
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
if (NOT LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 15.0)
message(FATAL_ERROR "LLVM version 15.0 or later is required.")
endif()
if(LLVM_ENABLE_RTTI)
message(STATUS "LLVM was built with RTTI")
else()
message(STATUS "LLVM was not built with RTTI")
endif()
string(REPLACE "." ";" VERSION_LIST ${LLVM_PACKAGE_VERSION})
list(GET VERSION_LIST 0 LLVM_MAJOR_VERSION)
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
add_definitions(${LLVM_DEFINITIONS})
if(NOT C3_LINK_DYNAMIC)
set(LLVM_LINK_COMPONENTS
AllTargetsAsmParsers
AllTargetsCodeGens
AllTargetsDescs
AllTargetsDisassemblers
AllTargetsInfos
Analysis
AsmPrinter
BitReader
Core
DebugInfoPDB
InstCombine
IrReader
LibDriver
Linker
LTO
MC
MCDisassembler
native
nativecodegen
Object
Option
ScalarOpts
Support
Target
TransformUtils
WindowsManifest
WindowsDriver
)
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER 14)
set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} WindowsDriver)
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
message("C3_LLD_DIR: " ${C3_LLD_DIR})
set(LLVM_LIBRARY_DIRS
"${LLVM_LIBRARY_DIRS}"
"${C3_LLD_DIR}"
)
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
endif()
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
else()
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(llvm_libs ${LLVM})
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
endif()
endif()
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
# These don't seem to be reliable on windows.
if(UNIX)
message(STATUS "using find_library")
# find_library(TB_LIB NAMES tinybackend.a PATHS ${CMAKE_SOURCE_DIR}/resources/tblib)
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_ELF NAMES lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_MACHO NAMES lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_MINGW NAMES lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_WASM NAMES lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS})
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
find_library(LLD_CORE NAMES lldCore.a liblldCore.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_YAML NAMES lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS})
endif()
if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
endif()
if(C3_WITH_LLVM)
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}
${LLD_COFF}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
${LLD_COMMON}
)
if (APPLE)
set(lld_libs ${lld_libs} xar)
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
set(sanitizer_runtime_libraries
${RT_ASAN_DYNAMIC}
${RT_TSAN_DYNAMIC}
# Unused
# ${RT_UBSAN_DYNAMIC}
# ${RT_LSAN_DYNAMIC}
)
message(STATUS "linking to llvm libs ${llvm_libs} ${lld_libs}")
endif()
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
endif()
message(STATUS "Found LLD ${lld_libs}")
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
add_library(miniz STATIC dependencies/miniz/miniz.c)
add_executable(c3c
src/build/builder.c
src/build/build_options.c
src/build/project_creation.c
src/build/project_manipulation.c
src/build/libraries.c
src/compiler/ast.c
src/compiler/bigint.c
src/compiler/c_abi_internal.h
src/compiler/codegen_general.c
src/compiler/compiler.c
src/compiler/compiler.h
src/compiler/subprocess.c
src/compiler/subprocess.h
src/compiler/context.c
src/compiler/copying.c
src/compiler/diagnostics.c
src/compiler/dwarf.h
src/compiler/enums.h
src/compiler/float.c
src/compiler/headers.c
src/compiler/json_output.c
src/compiler/lexer.c
src/compiler/linker.c
src/compiler/llvm_codegen.c
src/compiler/llvm_codegen_c_abi_aarch64.c
src/compiler/llvm_codegen_c_abi.c
src/compiler/llvm_codegen_c_abi_riscv.c
src/compiler/llvm_codegen_c_abi_wasm.c
src/compiler/llvm_codegen_c_abi_win64.c
src/compiler/llvm_codegen_c_abi_x64.c
src/compiler/llvm_codegen_c_abi_x86.c
src/compiler/llvm_codegen_debug_info.c
src/compiler/llvm_codegen_expr.c
src/compiler/llvm_codegen_function.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/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/module.c
src/compiler/number.c
src/compiler/parse_expr.c
@@ -167,6 +321,9 @@ add_executable(c3c
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
@@ -174,11 +331,7 @@ add_executable(c3c
src/compiler/source_file.c
src/compiler/symtab.c
src/compiler/target.c
src/compiler/tb_codegen.c
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/sema_asm.c
src/compiler_tests/benchmark.c
src/compiler_tests/tests.c
src/compiler/tokens.c
@@ -195,54 +348,213 @@ add_executable(c3c
src/utils/vmem.c
src/utils/vmem.h
src/utils/whereami.c
src/utils/cpus.c
src/utils/unzipper.c
src/compiler/c_codegen.c
src/compiler/decltable.c
src/compiler/tilde_codegen_storeload.c
src/compiler/llvm_codegen_storeload.c
src/compiler/tilde_codegen_expr.c
src/compiler/tilde_codegen_stmt.c
src/compiler/tilde_codegen_type.c)
src/compiler/mac_support.c
src/compiler/windows_support.c
src/compiler/codegen_asm.c
src/compiler/asm_target.c
src/compiler/expr.c
src/utils/time.c
src/utils/http.c
src/compiler/sema_liveness.c
src/build/common_build.c
src/compiler/sema_const.c
${CMAKE_BINARY_DIR}/git_hash.h
)
if(NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC")
message(STATUS "using gcc/clang warning switches")
target_compile_options(c3c PRIVATE -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
# We are inside of a git repository so rebuilding the hash every time something changes.
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/git_hash.h
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/git_hash.cmake"
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/.git")
else()
# We are NOT inside of a git repository. Building the has only once.
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/git_hash.h
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/git_hash.cmake")
endif()
if(C3_WITH_LLVM)
target_sources(c3c PRIVATE
src/compiler/llvm_codegen.c
src/compiler/llvm_codegen_debug_info.c
src/compiler/llvm_codegen_expr.c
src/compiler/llvm_codegen_function.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/llvm_codegen_storeload.c
src/compiler/llvm_codegen_builtins.c)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
else()
target_sources(c3c PRIVATE src/utils/hostinfo.c)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
endif()
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/src/")
"${CMAKE_SOURCE_DIR}/src/"
"${CMAKE_BINARY_DIR}")
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/tb/")
target_include_directories(miniz PUBLIC
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/src/")
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/")
if(UNIX)
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
target_link_libraries(c3c m ${llvm_libs} c3c_wrappers ${lld_libs})
# target_link_libraries(c3c m ${llvm_libs} c3c_wrappers ${TB_LIB} ${lld_libs})
if(C3_USE_MIMALLOC)
target_link_libraries(c3c m mimalloc-static)
endif()
else()
# todo: maybe get this from llvm-config somehow? it should be in LLVM_DIR\..\..\..\bin I think.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -LIBPATH:C:\\llvm\\llvm\\build\\Release\\lib") # needed for lldCommon.lib
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -LIBPATH:${LLVM_LIBPATH}") # This doesn't seem to work for some reason
message(STATUS "${LLVM_LIBPATH}")
target_link_libraries(c3c debug ${llvm_libs} c3c_wrappers lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO lldYAML Advapi32)
target_link_libraries(c3c optimized ${llvm_libs} c3c_wrappers lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO lldYAML Advapi32)
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=0)
endif()
if(C3_WITH_LLVM)
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/include/")
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/include/")
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
else()
target_link_libraries(c3c ${llvm_libs} miniz ${lld_libs})
endif()
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_compile_definitions(c3c PRIVATE USE_PTHREAD=1)
target_compile_options(c3c PRIVATE -mlong-double-64)
target_link_libraries(c3c Winhttp.lib)
endif()
if(MINGW)
message("Increase stack for msys")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,8388608")
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)
if(C3_WITH_LLVM)
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
if(NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PUBLIC /GR-)
endif()
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(c3c PUBLIC /MTd)
if (C3_WITH_LLVM)
target_compile_options(c3c_wrappers PUBLIC /MTd)
endif()
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)
if (C3_WITH_LLVM)
target_compile_options(c3c_wrappers PUBLIC /MT)
endif()
target_compile_options(miniz PUBLIC /MT)
if (C3_USE_TB)
target_compile_options(tilde-backend PUBLIC /MT)
endif()
endif()
if(C3_WITH_LLVM)
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
set(sanitizer_runtime_libraries
${clang_lib_dir}/clang_rt.asan-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
endif()
else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)
if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
endif()
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
endif()
install(TARGETS c3c DESTINATION bin)
install(DIRECTORY lib/ DESTINATION lib/c3)
# Man page install (OSX/Linux only)
if (NOT WIN32)
install(FILES c3c.1 DESTINATION "share/man/man1")
endif()
if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
add_custom_command(TARGET c3c POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
VERBATIM
COMMENT "Copying sanitizer runtime libraries to output directory")
if (APPLE)
# Change LC_ID_DYLIB to be rpath-based instead of having an absolute path
add_custom_command(TARGET c3c POST_BUILD
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
VERBATIM)
endif()
install(DIRECTORY $<TARGET_FILE_DIR:c3c>/c3c_rt/ DESTINATION bin/c3c_rt)
endif()
feature_summary(WHAT ALL)

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.

267
README.md
View File

@@ -1,8 +1,17 @@
# C3 Language
C3 is a C-like language striving to be an evolution of C, rather than a
completely new language. As an alternative in the C/C++ niche it
aims to be fast and close to the metal.
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.
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).
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
- MacOS Arm64 [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).
@@ -24,17 +33,16 @@ whole new language.
### Example code
The following code shows [generic modules](http://www.c3-lang.org/generics/) (more examples can be found at http://www.c3-lang.org/examples/).
The following code shows [generic modules](https://c3-lang.org/references/docs/generics/) (more examples can be found at https://c3-lang.org/references/docs/examples/).
```c++
module stack <Type>;
```cpp
module stack (<Type>);
// Above: the parameterized type is applied to the entire module.
import std::mem;
struct Stack
{
usize capacity;
usize size;
usz capacity;
usz size;
Type* elems;
}
@@ -47,7 +55,8 @@ fn void Stack.push(Stack* this, Type element)
if (this.capacity == this.size)
{
this.capacity *= 2;
this.elems = mem::realloc(this.elems, $sizeof(Type) * this.capacity);
if (this.capacity < 16) this.capacity = 16;
this.elems = realloc(this.elems, Type.sizeof * this.capacity);
}
this.elems[this.size++] = element;
}
@@ -71,20 +80,20 @@ import stack;
// Define our new types, the first will implicitly create
// a complete copy of the entire Stack module with "Type" set to "int"
define IntStack = Stack<int>;
def IntStack = Stack(<int>);
// The second creates another copy with "Type" set to "double"
define DoubleStack = Stack<double>;
def DoubleStack = Stack(<double>);
// If we had added "define IntStack2 = Stack<int>"
// 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
// 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 test()
fn void main()
{
IntStack stack;
// Note that C3 uses zero initialization by default
@@ -104,17 +113,17 @@ fn void test()
dstack.push(2.3);
dstack.push(3.141);
dstack.push(1.1235);
// Prints pop: 1.1235
// Prints pop: 1.123500
printf("pop: %f\n", dstack.pop());
}
```
### In what ways do C3 differ from C?
### In what ways does C3 differ from C?
- No mandatory header files
- New semantic macro system
- Module based name spacing
- Subarrays (slices)
- Slices
- Compile time reflection
- Enhanced compile time execution
- Generics based on generic modules
@@ -123,36 +132,60 @@ fn void test()
- Value methods
- Associated enum data
- No preprocessor
- Less undefined behaviour and runtime checks in "safe" mode
- Less undefined behaviour and added runtime checks in "safe" mode
- Limited operator overloading to enable userland dynamic arrays
- Optional pre and post conditions
### Current status
The current version of the compiler is alpha release 0.1.0.
The current stable version of the compiler is **version 0.6.7**.
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.
Design work on C3 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.
The upcoming 0.6.x releases will focus on expanding the standard library.
Follow the issues [here](https://github.com/c3lang/c3c/issues).
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).
The compiler should compile on Linux, Windows (under MSVC, Mingw or MSYS2) and MacOS,
but needs some install documentation for Windows.
The compiler is currently verified to compile on Linux, Windows and MacOS.
**Support matrix**
| Platform | Native C3 compiler available? | Target supported | Stack trace | Threads | Sockets | Inline asm |
|--------------------------|-------------------------------|-------------------------|-------------|----------|----------|------------|
| Win32 x64 | Yes | Yes + cross compilation | Yes | Yes | Yes | Yes* |
| Win32 Aarch64 | Untested | Untested | Untested | Untested | Untested | Yes* |
| MacOS x64 | Yes | Yes + cross compilation | Yes | Yes | Yes | Yes* |
| MacOS Aarch64 | Yes | Yes + cross compilation | Yes | Yes | Yes | Yes* |
| iOS Aarch64 | No | Untested | Untested | Yes | Yes | Yes* |
| Linux x86 | Yes | Yes | Yes | Yes | Yes | Yes* |
| Linux x64 | Yes | Yes | Yes | Yes | Yes | Yes* |
| Linux Aarch64 | Yes | Yes | Yes | Yes | Yes | Yes* |
| Linux Riscv32 | Yes | Yes | Yes | Yes | Yes | Untested |
| Linux Riscv64 | Yes | Yes | Yes | Yes | Yes | Untested |
| ELF freestanding x86 | No | Untested | No | No | No | Yes* |
| ELF freestanding x64 | No | Untested | No | No | No | Yes* |
| ELF freestanding Aarch64 | No | Untested | No | No | No | Yes* |
| ELF freestanding Riscv64 | No | Untested | No | No | No | Untested |
| ELF freestanding Riscv32 | No | Untested | No | No | No | Untested |
| FreeBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
| FreeBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
| NetBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
| NetBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
| OpenBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
| OpenBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
| MCU x86 | No | Untested | No | No | No | Yes* |
| Wasm32 | No | Yes | No | No | No | No |
| Wasm64 | No | Untested | No | No | No | No |
*\* Inline asm is still a work in progress*
More platforms will be supported in the future.
#### What can you help with?
- 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 and know your way around Github CI? Please help us get MSVC CI working!
- 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.
@@ -160,20 +193,59 @@ but needs some install documentation for Windows.
### Installing
#### Installing on Ubuntu 20.10
#### 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`.
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 12 (or greater: C3C supports LLVM 12-15): `sudo apt-get install clang-12 zlib1g zlib1g-dev libllvm12 llvm-12 llvm-12-dev llvm-12-runtime liblld-12-dev liblld-12`
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. Build: `cmake --build .`
#### 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 should now have a `c3c` executable.
#### Installing on Ubuntu with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20-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 MacOS 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`.
(*Note that there is a known issue with debug symbol generation on MacOS 13, see [issue #1086](https://github.com/c3lang/c3c/issues/1086))
#### Installing on Arch Linux
Arch includes c3c in the official 'extra' repo. It can be easily installed the usual way:
```sh
sudo pacman -S c3c
# or paru -S c3c
# or yay -S c3c
# or aura -A c3c
```
There is also an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git).
You can use your AUR package manager:
```sh
paru -S c3c-git
# or yay -S c3c-git
# or aura -A c3c-git
```
Or clone it manually:
```sh
git clone https://aur.archlinux.org/c3c-git.git
cd c3c-git
makepkg -si
```
#### Building via Docker
@@ -195,7 +267,7 @@ A `c3c` executable will be found under `bin/`.
#### Installing on OS X using Homebrew
2. Install CMake: `brew install cmake`
3. Install LLVM 13: `brew install llvm`
3. Install LLVM 17+: `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`
@@ -203,6 +275,14 @@ A `c3c` executable will be found under `bin/`.
8. Set up CMake build for debug: `cmake ..`
9. Build: `cmake --build .`
#### Installing on Windows using Scoop
c3c is included in 'Main' bucket.
```sh
scoop install c3
```
#### Getting started with a "hello world"
Create a `main.c3` file with:
@@ -212,7 +292,7 @@ import std::io;
fn void main()
{
io::println("Hello, world!");
io::printn("Hello, world!");
}
```
@@ -223,7 +303,89 @@ Then run
c3c compile main.c3
```
The generated binary will be called `a.out`.
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++"
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 24.04 LTS
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 LLVM 18 `sudo apt-get install cmake git clang zlib1g zlib1g-dev libllvm18 llvm llvm-dev llvm-runtime liblld-dev liblld-18 libpolly-18-dev`
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: `cmake ..`
8. 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 Void Linux
1. As root, ensure that all project dependencies are installed: `xbps-install git cmake llvm17 llvm17-devel lld17-devel libcurl-devel ncurses-devel zlib-devel libzstd-devel libxml2-devel`
2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
- If you only need the latest commit, you may want to make a shallow clone instead: `git clone https://github.com/c3lang/c3c.git --depth=1`
3. Enter the directory: `cd c3c`
4. Create a build directory: `mkdir build`
5. Enter the build directory: `cd build`
6. Create the CMake build cache: `cmake ..`
7. Build: `cmake --build .`
Your c3c executable should have compiled properly. You may want to test it: `./c3c compile ../resources/examples/hash.c3`
For a sytem-wide installation, run the following as root: `cmake --install .`
#### Compiling on Fedora
1. Install required project dependencies: `dnf install cmake clang git llvm llvm-devel lld lld-devel ncurses-devel`
2. Optionally, install additional dependencies: `dnf install libcurl-devel zlib-devel libzstd-devel libxml2-devel libffi-devel`
3. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
- If you only need the latest commit, you may want to make a shallow clone: `git clone https://github.com/c3lang/c3c.git --depth=1`
4. Enter the C3C directory: `cd c3c`
5. Create a build directory and navigate into it: `mkdir build && cd build`
6. Create the CMake build cache. The Fedora repositories provide `.so` libraries for lld, so you need to set the C3_LINK_DYNAMIC flag: `cmake .. -DC3_LINK_DYNAMIC=1`
7. Build the project: `cmake --build .`
The c3c binary should be created in the build directory. 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 17+ 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
@@ -232,4 +394,19 @@ MIT licensed.
#### Editor plugins
Editor plugins can be found at https://github.com/c3lang/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 -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.
## Thank yous
A huge **THANK YOU** goes out to all contributors and sponsors.
A special thank you to sponsors [Caleb-o](https://github.com/Caleb-o) and [devdad](https://github.com/devdad) for going the extra mile.

View File

@@ -0,0 +1,69 @@
module sort_bench;
import std::sort;
fn void init() @init
{
set_benchmark_warmup_iterations(5);
set_benchmark_max_iterations(10_000);
}
fn void quicksort_bench() @benchmark
{
// test set: 500 numbers between 0 and 99;
int[] data = {
71, 28, 2, 13, 62, 10, 54, 78, 63, 86,
33, 65, 89, 51, 58, 0, 51, 16, 87, 30,
89, 14, 52, 41, 88, 25, 83, 91, 56, 86,
14, 64, 76, 18, 39, 24, 79, 62, 34, 58,
90, 24, 56, 73, 85, 82, 79, 63, 47, 69,
78, 29, 49, 28, 43, 47, 56, 53, 79, 56,
19, 63, 29, 52, 71, 93, 61, 46, 30, 11,
21, 26, 37, 86, 93, 74, 62, 0, 41, 17,
26, 27, 34, 11, 54, 69, 72, 44, 74, 3,
61, 62, 80, 90, 3, 82, 16, 12, 28, 1,
2, 49, 4, 44, 57, 86, 63, 74, 33, 41,
76, 77, 56, 57, 56, 88, 74, 71, 6, 59,
40, 42, 94, 55, 21, 17, 17, 63, 21, 83,
73, 19, 39, 88, 93, 74, 21, 0, 63, 45,
69, 66, 22, 68, 86, 86, 85, 67, 8, 50,
23, 98, 64, 80, 64, 36, 40, 30, 73, 36,
23, 14, 1, 77, 82, 8, 18, 73, 37, 86,
29, 70, 27, 87, 64, 81, 13, 0, 4, 83,
90, 17, 71, 66, 38, 39, 54, 22, 86, 18,
84, 66, 77, 25, 64, 93, 80, 91, 2, 92,
47, 32, 90, 16, 46, 29, 56, 87, 70, 73,
89, 41, 5, 54, 93, 63, 16, 39, 71, 84,
74, 91, 69, 59, 49, 87, 74, 37, 75, 83,
77, 19, 51, 44, 79, 62, 94, 20, 24, 83,
37, 70, 57, 32, 93, 8, 29, 11, 7, 92,
8, 23, 20, 21, 7, 70, 28, 20, 96, 6,
50, 58, 30, 61, 66, 42, 50, 54, 64, 7,
10, 53, 63, 44, 16, 39, 83, 73, 3, 29,
97, 32, 36, 68, 84, 64, 73, 5, 29, 13,
48, 3, 84, 65, 75, 68, 66, 22, 39, 33,
39, 24, 27, 85, 18, 34, 3, 63, 32, 9,
29, 66, 24, 90, 75, 50, 11, 95, 47, 14,
92, 1, 76, 45, 76, 41, 55, 54, 38, 67,
43, 40, 5, 61, 97, 11, 61, 24, 92, 24,
76, 53, 60, 34, 78, 80, 70, 75, 30, 90,
65, 99, 80, 61, 94, 75, 63, 67, 10, 35,
23, 42, 31, 48, 14, 68, 84, 14, 79, 1,
25, 94, 23, 53, 49, 69, 44, 73, 63, 51,
44, 96, 88, 51, 94, 24, 64, 72, 59, 81,
73, 93, 14, 35, 9, 53, 25, 48, 50, 88,
46, 97, 67, 40, 27, 17, 2, 42, 11, 82,
0, 46, 44, 38, 31, 88, 63, 88, 10, 82,
77, 61, 24, 39, 27, 33, 10, 91, 69, 22,
42, 74, 71, 13, 32, 56, 12, 46, 81, 74,
17, 26, 45, 50, 76, 84, 76, 36, 43, 65,
81, 64, 0, 49, 70, 11, 76, 19, 60, 55,
15, 98, 31, 91, 56, 8, 97, 9, 3, 94,
3, 88, 7, 2, 3, 98, 10, 51, 21, 79,
99, 3, 8, 76, 52, 13, 40, 90, 85, 15,
70, 77, 43, 30, 4, 89, 18, 21, 59, 17,
};
sort::quicksort(data);
}

View File

@@ -1,56 +1,44 @@
#!/bin/bash
## build-with-docker.sh
## @author gdm85
##
## Script to build c3c for either Ubuntu 20, 21 or 22.
##
#
if [ $# -ne 1 -a $# -ne 2 ]; then
echo "Usage: build-with-docker.sh (20|21|22) [Debug|Release]" 1>&2
exit 1
: ${DOCKER:=docker}
: ${IMAGE:="c3c-builder"}
: ${CMAKE_BUILD_TYPE:=Release}
: ${LLVM_VERSION:=18}
: ${UBUNTU_VERSION:="22.04"}
: ${CMAKE_VERSION:="3.20.0"}
cd docker || exit 1 # Exit if the 'docker' directory doesn't exist
$DOCKER build \
--build-arg LLVM_VERSION=$LLVM_VERSION \
--build-arg CMAKE_VERSION=$CMAKE_VERSION \
--build-arg UBUNTU_VERSION=$UBUNTU_VERSION \
-t $IMAGE .
if [ $? -ne 0 ]; then
echo "Docker image build failed. Exiting."
exit 1
fi
set -e
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
if [ -z "$2" ]; then
CMAKE_BUILD_TYPE=Debug
else
CMAKE_BUILD_TYPE="$2"
fi
TAG="$1"
if [ "$1" = 20 ]; then
UBUNTU_VERSION="20.04"
LLVM_VERSION="12"
elif [ "$1" = 21 ]; then
UBUNTU_VERSION="21.10"
LLVM_VERSION="13"
elif [ "$1" = 22 ]; then
UBUNTU_VERSION="22.04"
LLVM_VERSION="14"
else
echo "ERROR: expected 20, 21 or 22 as Ubuntu version argument" 1>&2
exit 2
fi
IMAGE="$IMAGE:$TAG"
cd docker && $DOCKER build -t $IMAGE --build-arg UID=$(id -u) --build-arg GID=$(id -g) \
--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
mkdir -p build bin
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/"
chmod -R 777 build bin
exec $DOCKER run -i --rm \
-v "$PWD":/home/c3c/source \
-w /home/c3c/source $IMAGE bash -c \
"cmake -S . -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
-DCMAKE_C_COMPILER=clang-$LLVM_VERSION \
-DCMAKE_CXX_COMPILER=clang++-$LLVM_VERSION \
-DCMAKE_LINKER=lld-$LLVM_VERSION \
-DCMAKE_OBJCOPY=llvm-objcopy-$LLVM_VERSION \
-DCMAKE_STRIP=llvm-strip-$LLVM_VERSION \
-DCMAKE_DLLTOOL=llvm-dlltool-$LLVM_VERSION \
-DC3_LLVM_VERSION=auto && \
cmake --build build && \
cp -r build/c3c build/lib bin"

552
c3c.1 Normal file
View File

@@ -0,0 +1,552 @@
.TH "c3c" "1" "2024-10-27" "C3 Compiler" "User Commands"
.SH NAME
c3c \- Compiler for the C3 programming language
.SH SYNOPSIS
.B c3c
[\fIoptions\fR ...] \fIcommand\fR [\fIargs\fR ...]
.SH DESCRIPTION
.B c3c
is the compiler for the C3 language, providing commands for compilation, project
management, testing, and distribution. The available commands allow users to
compile files, initialize projects, build targets, run benchmarks, clean build
files, and more.
.SH COMMANDS
.PP
.B c3c compile
\fIfile1\fR [\fIfile2\fR ...]
.RS
Compile files without a project into an executable.
.RE
.PP
.B c3c init
\fIproject name\fR
.RS
Initialize a new project structure.
.RE
.PP
.B c3c init-lib
\fIlibrary name\fR
.RS
Initialize a new library structure.
.RE
.PP
.B c3c build
[\fItarget\fR]
.RS
Build the target in the current project.
.RE
.PP
.B c3c benchmark
.RS
Run the benchmarks in the current project.
.RE
.PP
.B c3c test
.RS
Run the unit tests in the current project.
.RE
.PP
.B c3c clean
.RS
Clean all build files.
.RE
.PP
.B c3c run
[\fItarget\fR] [-- [\fIarg1\fR ...]]
.RS
Run (and build if needed) the target in the current project.
.RE
.PP
.B c3c dist
[\fItarget\fR]
.RS
Clean and build a target for distribution.
.RE
.PP
.B c3c directives
[\fItarget\fR]
.RS
Generate documentation for the target.
.RE
.PP
.B c3c bench
[\fItarget\fR]
.RS
Benchmark a target.
.RE
.PP
.B c3c clean-run
[\fItarget\fR] [-- [\fIarg1\fR ...]]
.RS
Clean, then run the target.
.RE
.PP
.B c3c compile-run
\fIfile1\fR [\fIfile2\fR ...] [-- [\fIarg1\fR ...]]
.RS
Compile files then immediately run the result.
.RE
.PP
.B c3c compile-only
\fIfile1\fR [\fIfile2\fR ...]
.RS
Compile files but do not perform linking.
.RE
.PP
.B c3c compile-benchmark
\fIfile1\fR [\fIfile2\fR ...]
.RS
Compile files into an executable and run benchmarks.
.RE
.PP
.B c3c compile-test
\fIfile1\fR [\fIfile2\fR ...]
.RS
Compile files into an executable and run unit tests.
.RE
.PP
.B c3c static-lib
\fIfile1\fR [\fIfile2\fR ...]
.RS
Compile files without a project into a static library.
.RE
.PP
.B c3c dynamic-lib
\fIfile1\fR [\fIfile2\fR ...]
.RS
Compile files without a project into a dynamic library.
.RE
.PP
.B c3c headers
\fIfile1\fR [\fIfile2\fR ...]
.RS
Analyze files and generate C headers for public methods.
.RE
.PP
.B c3c vendor-fetch
\fIlibrary\fR ...
.RS
Fetch one or more libraries from the vendor collection.
.RE
.PP
.B c3c project
\fIsubcommand\fR ...
.RS
Manipulate or view project files.
.RE
.SH OPTIONS
.PP
.B --stdlib
\fIdir\fR
.RS
Use this directory as the C3 standard library path.
.RE
.PP
.B --no-entry
.RS
Do not generate (or require) a main function.
.RE
.PP
.B --libdir
\fIdir\fR
.RS
Add this directory to the C3 library search paths.
.RE
.PP
.B --lib
\fIname\fR
.RS
Add this library to the compilation.
.RE
.PP
.B --path
\fIdir\fR
.RS
Use this as the base directory for the current command.
.RE
.PP
.B --template
\fItemplate\fR
.RS
Select template for 'init': "exe", "static-lib", "dynamic-lib" or a path.
.RE
.PP
.B --about
Prints a short description of C3.
.PP
.B --symtab
\fIvalue\fR
.RS
Sets the preferred symtab size.
.RE
.PP
.B --max-mem
\fIvalue\fR
.RS
Sets the preferred max memory size.
.RE
.PP
.B --run-once
.RS
After running the output file, delete it immediately.
.RE
.PP
.B -V, --version
Print version information.
.PP
.B -E
Lex only.
.PP
.B -P
Only parse and output the AST as JSON.
.PP
.B -C
Only lex, parse and check.
.PP
.B -
\fIcode\fR...
.RS
Read code from standard in.
.RE
.PP
.B -o
\fIfile\fR
.RS
Write output to \fIfile\fR.
.RE
.PP
.B -O0
Safe, no optimizations, emit debug info.
.PP
.B -O1
Safe, high optimization, emit debug info.
.PP
.B -O2
Unsafe, high optimization, emit debug info.
.PP
.B -O3
Unsafe, high optimization, single module, emit debug info.
.PP
.B -O4
Unsafe, highest optimization, relaxed maths, single module, emit debug info, no panic messages.
.PP
.B -O5
Unsafe, highest optimization, fast maths, single module, emit debug info, no panic messages, no backtrace.
.PP
.B -Os
Unsafe, high optimization, small code, single module, no debug info, no panic messages.
.PP
.B -Oz
Unsafe, high optimization, tiny code, single module, no debug info, no panic messages, no backtrace.
.PP
.B -D
\fIname\fR
.RS
Add feature flag \fIname\fR.
.RE
.PP
.B -U
\fIname\fR
.RS
Remove feature flag \fIname\fR.
.RE
.PP
.B --trust=
\fIoption\fR
.RS
Trust level: none (default), include ($include allowed), full ($exec / exec allowed).
.RE
.PP
.B --output-dir
\fIdir\fR
.RS
Override general output directory.
.RE
.PP
.B --threads
\fInumber\fR
.RS
Set the number of threads to use for compilation.
.RE
.PP
.B --show-backtrace=
\fIyes|no\fR
.RS
Show detailed backtrace on segfaults.
.RE
.PP
.B -g
Emit debug info.
.PP
.B -g0
Emit no debug info.
.PP
.B -l
\fIlibrary\fR
.RS
Link with the library provided.
.RE
.PP
.B -L
\fIlibrary\fR \fIdir\fR
.RS
Append the directory to the linker search paths.
.RE
.PP
.B -z
\fIargument\fR
.RS
Send the \fIargument\fR as a parameter to the linker.
.RE
.PP
.B --cc
\fIpath\fR
.RS
Set C compiler (for C files in projects and use as system linker).
.RE
.PP
.B --linker=
\fIoption\fR [\fIpath\fR]
.RS
Specify the linker: builtin, cc, custom (default is 'cc'). 'Custom' requires a path.
.RE
.PP
.B --use-stdlib=
\fIyes|no\fR
.RS
Include the standard library (default: yes).
.RE
.PP
.B --link-libc=
\fIyes|no\fR
.RS
Link libc and other default libraries (default: yes).
.RE
.PP
.B --emit-stdlib=
\fIyes|no\fR
.RS
Output files for the standard library (default: yes).
.RE
.PP
.B --panicfn
\fIname\fR
.RS
Override the panic function name.
.RE
.PP
.B --testfn
\fIname\fR
.RS
Override the test runner function name.
.RE
.PP
.B --benchfn
\fIname\fR
.RS
Override the benchmark runner function name.
.RE
.PP
.B --reloc=
\fIoption\fR
.RS
Specify the relocation model: none, pic, PIC, pie, PIE.
.RE
.PP
.B --x86cpu=
\fIoption\fR
.RS
Set general level of x64 CPU: baseline, ssse3, sse4, avx1, avx2-v1, avx2-v2 (Skylake/Zen1+), avx512 (Icelake/Zen4+), native.
.RE
.PP
.B --x86vec=
\fIoption\fR
.RS
Set maximum type of vector use: none, mmx, sse, avx, avx512, default.
.RE
.PP
.B --riscvfloat=
\fIoption\fR
.RS
Set type of RISC-V float support: none, float, double.
.RE
.PP
.B --memory-env=
\fIoption\fR
.RS
Set the memory environment: normal, small, tiny, none.
.RE
.PP
.B --strip-unused=
\fIyes|no\fR
.RS
Strip unused code and globals from the output (default: yes).
.RE
.PP
.B --fp-math=
\fIoption\fR
.RS
Specify floating-point math behavior: strict, relaxed, fast.
.RE
.PP
.B --win64-simd=
\fIoption\fR
.RS
Windows SIMD ABI: array, full.
.RE
.PP
.B --debug-stats
Print debug statistics.
.PP
.B --print-linking
Print linker arguments.
.PP
.B --debug-log
Print debug logging to stdout.
.PP
.B --benchmarking
Run built-in benchmarks.
.PP
.B --testing
Run built-in tests.
.PP
.B --list-attributes
List all attributes.
.PP
.B --list-builtins
List all builtins.
.PP
.B --list-keywords
List all keywords.
.PP
.B --list-operators
List all operators.
.PP
.B --list-precedence
List operator precedence order.
.PP
.B --list-project-properties
List all available keys used in project.json files.
.PP
.B --list-manifest-properties
List all available keys used in manifest.json files.
.PP
.B --list-targets
List all architectures the compiler supports.
.PP
.B --list-type-properties
List all type properties.
.PP
.B --print-output
Print the object files created to stdout.
.PP
.B --print-input
Print inputted C3 files to stdout.
.PP
.B --winsdk
\fIdir\fR
.RS
Set the directory for Windows system library files for cross-compilation.
.RE
.PP
.B --wincrt=
\fIoption\fR
.RS
Windows CRT linking: none, static-debug, static, dynamic-debug (default if debug info enabled), dynamic (default).
.RE
.PP
.B --windef
\fIfile\fR
.RS
Use Windows 'def' file for function exports instead of 'dllexport'.
.RE
.PP
.B --macossdk
\fIdir\fR
.RS
Set the directory for the MacOS SDK for cross-compilation.
.RE
.PP
.B --macos-min-version
\fIver\fR
.RS
Set the minimum MacOS version to compile for.
.RE
.PP
.B --macos-sdk-version
\fIver\fR
.RS
Set the MacOS SDK version to compile for.
.RE
.PP
.B --linux-crt
\fIdir\fR
.RS
Set the directory to use for finding crt1.o and related files.
.RE
.PP
.B --linux-crtbegin
\fIdir\fR
.RS
Set the directory to use for finding crtbegin.o and related files.
.RE
.PP
.B --vector-conv=
\fIoption\fR
.RS
Set vector conversion behavior: default, old.
.RE
.PP
.B --sanitize=
\fIoption\fR
.RS
Enable a sanitizer: address, memory, thread.
.RE
.SH EXAMPLES
.PP
Create a project:
.RS
.B c3c init new_project
.RE
.PP
Create a library project:
.RS
.B c3c init-lib new_library
.RE
.PP
Compile a file:
.RS
.B c3c compile main.c3
.RE
.PP
Build the current project:
.RS
.B c3c build
.RE
.PP
Run tests for the current project:
.RS
.B c3c test
.RE

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

@@ -1,16 +1,49 @@
ARG UBUNTU_VERSION=22.04
FROM ubuntu:${UBUNTU_VERSION}
ARG UBUNTU_VERSION
FROM ubuntu:$UBUNTU_VERSION
ARG LLVM_VERSION=18
ENV LLVM_DEV_VERSION=20
ARG DEPS
ARG CMAKE_VERSION=3.20
RUN export DEBIAN_FRONTEND=noninteractive && export TERM=xterm && apt-get update && apt-get install -y build-essential cmake zlib1g zlib1g-dev \
$DEPS && \
rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y wget gnupg software-properties-common zlib1g zlib1g-dev python3 ninja-build curl g++ && \
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-$CMAKE_VERSION-linux-x86_64.sh && \
mkdir -p /opt/cmake && \
sh cmake-${CMAKE_VERSION}-linux-x86_64.sh --prefix=/opt/cmake --skip-license && \
rm cmake-${CMAKE_VERSION}-linux-x86_64.sh && \
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
ARG GID=1000
ARG UID=1000
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
if [ "${LLVM_VERSION}" -lt 18 ]; then \
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
apt-get update && \
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
libpolly-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev libmlir-${LLVM_VERSION} \
libmlir-${LLVM_VERSION}-dev mlir-${LLVM_VERSION}-tools; \
elif [ "${LLVM_VERSION}" -lt "${LLVM_DEV_VERSION}" ]; then \
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
apt-get update && \
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
libpolly-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} clang++-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
else \
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" && \
apt-get update && \
apt-get install -y -t llvm-toolchain-focal \
libpolly-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
fi && \
rm -rf /var/lib/apt/lists/*
RUN groupadd --gid=$GID c3c && useradd --gid=$GID --uid=$GID --create-home --shell /bin/bash c3c
RUN groupadd -g 1337 c3c && \
useradd -m -u 1337 -g c3c c3c
# Add cmake to PATH for user c3c
USER c3c
ENV PATH="/opt/cmake/bin:${PATH}"
WORKDIR /home/c3c

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1738297584,
"narHash": "sha256-AYvaFBzt8dU0fcSK2jKD0Vg23K2eIRxfsVXIPCW9a0E=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "9189ac18287c599860e878e905da550aa6dec1cd",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

44
flake.nix Normal file
View File

@@ -0,0 +1,44 @@
{
description = "C3 compiler flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, ... } @ inputs: inputs.flake-utils.lib.eachDefaultSystem
(system:
let pkgs = import inputs.nixpkgs { inherit system; };
call = set: pkgs.callPackage ./nix/default.nix (
set // {
rev = self.rev or "unknown";
}
);
in {
packages = {
default = self.packages.${system}.c3c;
c3c = call {};
c3c-checks = pkgs.callPackage ./nix/default.nix {
checks = true;
};
c3c-debug = pkgs.callPackage ./nix/default.nix {
debug = true;
};
c3c-debug-checks = pkgs.callPackage ./nix/default.nix {
debug = true;
checks = true;
};
};
devShells = {
default = pkgs.callPackage ./nix/shell.nix {
c3c = self.packages.${system}.c3c-debug;
};
};
}
);
}

15
git_hash.cmake Normal file
View File

@@ -0,0 +1,15 @@
find_package(Git QUIET)
set(GIT_HASH "unknown")
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git")
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
endif()
message("Git Hash: ${GIT_HASH}")
file(WRITE ${CMAKE_BINARY_DIR}/git_hash.h "#pragma once\n#define GIT_HASH \"${GIT_HASH}\"\n")

View File

@@ -1,19 +0,0 @@
// 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::array;
import std::mem;
macro make($Type, usize elements)
{
assert(elements > 0);
$Type* ptr = mem::alloc($sizeof($Type), elements);
return ptr[0..(elements - 1)];
}
macro make_zero($Type, usize elements)
{
assert(elements > 0);
$Type* ptr = mem::calloc($sizeof($Type), elements);
return ptr[0..(elements - 1)];
}

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

@@ -0,0 +1,77 @@
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);
<*
@require c.is_xdigit()
*>
fn char char.from_hex(char c) => c.is_digit() ? c - '0' : 10 + (c | 0x20) - 'a';
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);

View File

@@ -1,5 +1,520 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2023 Eduardo José Gómez Hernández. 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;
module std::atomic::types(<Type>);
struct Atomic
{
Type data;
}
<*
Loads data atomically, by default this uses SEQ_CONSISTENT ordering.
@param ordering "The ordering, cannot be release or acquire-release."
@require ordering != RELEASE && ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
*>
macro Type Atomic.load(&self, AtomicOrdering ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
switch(ordering)
{
case NOT_ATOMIC: return $$atomic_load(data, false, AtomicOrdering.NOT_ATOMIC.ordinal);
case UNORDERED: return $$atomic_load(data, false, AtomicOrdering.UNORDERED.ordinal);
case RELAXED: return $$atomic_load(data, false, AtomicOrdering.RELAXED.ordinal);
case ACQUIRE: return $$atomic_load(data, false, AtomicOrdering.ACQUIRE.ordinal);
case SEQ_CONSISTENT: return $$atomic_load(data, false, AtomicOrdering.SEQ_CONSISTENT.ordinal);
case ACQUIRE_RELEASE:
case RELEASE: unreachable("Invalid ordering.");
}
}
<*
Stores data atomically, by default this uses SEQ_CONSISTENT ordering.
@param ordering "The ordering, cannot be acquire or acquire-release."
@require ordering != ACQUIRE && ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
*>
macro void Atomic.store(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
switch(ordering)
{
case NOT_ATOMIC: $$atomic_store(data, value, false, AtomicOrdering.NOT_ATOMIC.ordinal);
case UNORDERED: $$atomic_store(data, value, false, AtomicOrdering.UNORDERED.ordinal);
case RELAXED: $$atomic_store(data, value, false, AtomicOrdering.RELAXED.ordinal);
case RELEASE: $$atomic_store(data, value, false, AtomicOrdering.RELEASE.ordinal);
case SEQ_CONSISTENT: $$atomic_store(data, value, false, AtomicOrdering.SEQ_CONSISTENT.ordinal);
case ACQUIRE_RELEASE:
case ACQUIRE: unreachable("Invalid ordering.");
}
}
macro Type Atomic.add(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_add, data, value, ordering);
}
macro Type Atomic.sub(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_sub, data, value, ordering);
}
macro Type Atomic.mul(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_mul, data, value, ordering);
}
macro Type Atomic.div(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_div, data, value, ordering);
}
macro Type Atomic.max(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_div, data, value, ordering);
}
macro Type Atomic.min(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_min, data, value, ordering);
}
macro Type Atomic.or(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_or, data, value, ordering);
}
fn Type Atomic.xor(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_xor, data, value, ordering);
}
macro Type Atomic.and(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_and, data, value, ordering);
}
macro Type Atomic.shift_right(&self, uint amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_shift_right, data, amount, ordering);
}
macro Type Atomic.shift_left(&self, uint amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_shift_left, data, amount, ordering);
}
macro @atomic_exec(#func, data, value, ordering) @local
{
switch(ordering)
{
case RELAXED: return #func(data, value, RELAXED);
case ACQUIRE: return #func(data, value, ACQUIRE);
case RELEASE: return #func(data, value, RELEASE);
case ACQUIRE_RELEASE: return #func(data, value, ACQUIRE_RELEASE);
case SEQ_CONSISTENT: return #func(data, value, SEQ_CONSISTENT);
default: unreachable("Ordering may not be non-atomic or unordered.");
}
}
module std::atomic;
import std::math;
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if $alignment == 0:
$alignment = $typeof(*ptr).sizeof;
$endif
return $$atomic_fetch_add(ptr, y, $volatile, $ordering.ordinal, $alignment);
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if $alignment == 0:
$alignment = $typeof(*ptr).sizeof;
$endif
return $$atomic_fetch_sub(ptr, y, $volatile, $ordering.ordinal, $alignment);
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = old_value * y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = old_value / y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if types::is_int($typeof(*ptr)):
return $$atomic_fetch_or(ptr, y, $volatile, $ordering.ordinal, $alignment);
$endif
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value | storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if types::is_int($typeof(*ptr)):
return $$atomic_fetch_xor(ptr, y, $volatile, $ordering.ordinal, $alignment);
$endif
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value ^ storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if types::is_int($typeof(*ptr)):
return $$atomic_fetch_and(ptr, y, $volatile, $ordering.ordinal, $alignment);
$endif
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value & storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value >> storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value << storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
$typeof(*ptr) old_value;
$typeof(*ptr) new_value = true;
do {
old_value = $$atomic_load(ptr, false, $ordering.ordinal);
} while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
$typeof(*ptr) old_value;
$typeof(*ptr) new_value = false;
do {
old_value = $$atomic_load(ptr, false, $ordering.ordinal);
} while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if $alignment == 0:
$alignment = $typeof(*ptr).sizeof;
$endif
return $$atomic_fetch_max(ptr, y, $volatile, $ordering.ordinal, $alignment);
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
*>
macro fetch_min(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if $alignment == 0:
$alignment = $typeof(*ptr).sizeof;
$endif
return $$atomic_fetch_min(ptr, y, $volatile, $ordering.ordinal, $alignment);
}

63
lib/std/atomic_nolibc.c3 Normal file
View File

@@ -0,0 +1,63 @@
// Copyright (c) 2023 Eduardo José Gómez Hernández. 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;
macro @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, $success, failure, $alignment) {
switch(failure)
{
case AtomicOrdering.RELAXED.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.RELAXED.ordinal, $alignment);
case AtomicOrdering.ACQUIRE.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.ACQUIRE.ordinal, $alignment);
case AtomicOrdering.SEQ_CONSISTENT.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.SEQ_CONSISTENT.ordinal, $alignment);
default: unreachable("Unrecognized failure ordering");
}
return 0;
}
macro @__atomic_compare_exchange_ordering_success(ptr, expected, desired, success, failure, $alignment)
{
switch(success)
{
case AtomicOrdering.RELAXED.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.RELAXED.ordinal, failure, $alignment);
case AtomicOrdering.ACQUIRE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.ACQUIRE.ordinal, failure, $alignment);
case AtomicOrdering.RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.RELEASE.ordinal, failure, $alignment);
case AtomicOrdering.ACQUIRE_RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.ACQUIRE_RELEASE.ordinal, failure, $alignment);
case AtomicOrdering.SEQ_CONSISTENT.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.SEQ_CONSISTENT.ordinal, failure, $alignment);
default: unreachable("Unrecognized success ordering");
}
return 0;
}
fn CInt __atomic_compare_exchange(CInt size, any ptr, any expected, any desired, CInt success, CInt failure) @extern("__atomic_compare_exchange") @export
{
switch (size)
{
case 1:
char* pt = (char*)ptr;
char ex = *(char*)expected;
char de = *(char*)desired;
if (ex == @__atomic_compare_exchange_ordering_success(pt, ex, de, success, failure, 1)) return 1;
case 2:
short* pt = (short*)ptr;
short ex = *(short*)expected;
short de = *(short*)desired;
if (ex == @__atomic_compare_exchange_ordering_success(pt, ex, de, success, failure, 2)) return 1;
case 4:
int* pt = (int*)ptr;
int ex = *(int*)expected;
int de = *(int*)desired;
if (ex == @__atomic_compare_exchange_ordering_success(pt, ex, de, success, failure, 4)) return 1;
case 8:
$if iptr.sizeof >= 8:
long* pt = (long*)ptr;
long ex = *(long*)expected;
long de = *(long*)desired;
if (ex == @__atomic_compare_exchange_ordering_success(pt, ex, de, success, failure, 8)) return 1;
$else
nextcase;
$endif
default:
unreachable("Unsuported size (%d) for atomic_compare_exchange", size);
}
return 0;
}

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

@@ -0,0 +1,171 @@
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(self) => $$popcount(self);
macro uint[<?>].ctz(self) => $$ctz(self);
macro uint[<?>].clz(self) => $$clz(self);
macro uint[<?>] uint[<?>].fshl(hi, uint[<?>] lo, uint[<?>] shift) => $$fshl(hi, lo, shift);
macro uint[<?>] uint[<?>].fshr(hi, uint[<?>] lo, uint[<?>] shift) => $$fshr(hi, lo, shift);
macro uint[<?>] uint[<?>].rotl(self, uint[<?>] shift) => $$fshl(self, self, shift);
macro uint[<?>] uint[<?>].rotr(self, uint[<?>] shift) => $$fshr(self, self, shift);
macro int[<?>].popcount(self) => $$popcount(self);
macro int[<?>].ctz(self) => $$ctz(self);
macro int[<?>].clz(self) => $$clz(self);
macro int[<?>] int[<?>].fshl(hi, int[<?>] lo, int[<?>] shift) => $$fshl(hi, lo, shift);
macro int[<?>] int[<?>].fshr(hi, int[<?>] lo, int[<?>] shift) => $$fshr(hi, lo, shift);
macro int[<?>] int[<?>].rotl(self, int[<?>] shift) => $$fshl(self, self, shift);
macro int[<?>] int[<?>].rotr(self, int[<?>] shift) => $$fshr(self, self, shift);
macro ushort[<?>].popcount(self) => $$popcount(self);
macro ushort[<?>].ctz(self) => $$ctz(self);
macro ushort[<?>].clz(self) => $$clz(self);
macro ushort[<?>] ushort[<?>].fshl(hi, ushort[<?>] lo, ushort[<?>] shift) => $$fshl(hi, lo, shift);
macro ushort[<?>] ushort[<?>].fshr(hi, ushort[<?>] lo, ushort[<?>] shift) => $$fshr(hi, lo, shift);
macro ushort[<?>] ushort[<?>].rotl(self, ushort[<?>] shift) => $$fshl(self, self, shift);
macro ushort[<?>] ushort[<?>].rotr(self, ushort[<?>] shift) => $$fshr(self, self, shift);
macro short[<?>].popcount(self) => $$popcount(self);
macro short[<?>].ctz(self) => $$ctz(self);
macro short[<?>].clz(self) => $$clz(self);
macro short[<?>] short[<?>].fshl(hi, short[<?>] lo, short[<?>] shift) => $$fshl(hi, lo, shift);
macro short[<?>] short[<?>].fshr(hi, short[<?>] lo, short[<?>] shift) => $$fshr(hi, lo, shift);
macro short[<?>] short[<?>].rotl(self, short[<?>] shift) => $$fshl(self, self, shift);
macro short[<?>] short[<?>].rotr(self, short[<?>] shift) => $$fshr(self, self, shift);
macro char[<?>].popcount(self) => $$popcount(self);
macro char[<?>].ctz(self) => $$ctz(self);
macro char[<?>].clz(self) => $$clz(self);
macro char[<?>] char[<?>].fshl(hi, char[<?>] lo, char[<?>] shift) => $$fshl(hi, lo, shift);
macro char[<?>] char[<?>].fshr(hi, char[<?>] lo, char[<?>] shift) => $$fshr(hi, lo, shift);
macro char[<?>] char[<?>].rotl(self, char[<?>] shift) => $$fshl(self, self, shift);
macro char[<?>] char[<?>].rotr(self, char[<?>] shift) => $$fshr(self, self, shift);
macro ichar[<?>].popcount(self) => $$popcount(self);
macro ichar[<?>].ctz(self) => $$ctz(self);
macro ichar[<?>].clz(self) => $$clz(self);
macro ichar[<?>] ichar[<?>].fshl(hi, ichar[<?>] lo, ichar[<?>] shift) => $$fshl(hi, lo, shift);
macro ichar[<?>] ichar[<?>].fshr(hi, ichar[<?>] lo, ichar[<?>] shift) => $$fshr(hi, lo, shift);
macro ichar[<?>] ichar[<?>].rotl(self, ichar[<?>] shift) => $$fshl(self, self, shift);
macro ichar[<?>] ichar[<?>].rotr(self, ichar[<?>] shift) => $$fshr(self, self, shift);
macro ulong[<?>].popcount(self) => $$popcount(self);
macro ulong[<?>].ctz(self) => $$ctz(self);
macro ulong[<?>].clz(self) => $$clz(self);
macro ulong[<?>] ulong[<?>].fshl(hi, ulong[<?>] lo, ulong[<?>] shift) => $$fshl(hi, lo, shift);
macro ulong[<?>] ulong[<?>].fshr(hi, ulong[<?>] lo, ulong[<?>] shift) => $$fshr(hi, lo, shift);
macro ulong[<?>] ulong[<?>].rotl(self, ulong[<?>] shift) => $$fshl(self, self, shift);
macro ulong[<?>] ulong[<?>].rotr(self, ulong[<?>] shift) => $$fshr(self, self, shift);
macro long[<?>].popcount(self) => $$popcount(self);
macro long[<?>].ctz(self) => $$ctz(self);
macro long[<?>].clz(self) => $$clz(self);
macro long[<?>] long[<?>].fshl(hi, long[<?>] lo, long[<?>] shift) => $$fshl(hi, lo, shift);
macro long[<?>] long[<?>].fshr(hi, long[<?>] lo, long[<?>] shift) => $$fshr(hi, lo, shift);
macro long[<?>] long[<?>].rotl(self, long[<?>] shift) => $$fshl(self, self, shift);
macro long[<?>] long[<?>].rotr(self, long[<?>] shift) => $$fshr(self, self, shift);
macro uint128[<?>].popcount(self) => $$popcount(self);
macro uint128[<?>].ctz(self) => $$ctz(self);
macro uint128[<?>].clz(self) => $$clz(self);
macro uint128[<?>] uint128[<?>].fshl(hi, uint128[<?>] lo, uint128[<?>] shift) => $$fshl(hi, lo, shift);
macro uint128[<?>] uint128[<?>].fshr(hi, uint128[<?>] lo, uint128[<?>] shift) => $$fshr(hi, lo, shift);
macro uint128[<?>] uint128[<?>].rotl(self, uint128[<?>] shift) => $$fshl(self, self, shift);
macro uint128[<?>] uint128[<?>].rotr(self, uint128[<?>] shift) => $$fshr(self, self, shift);
macro int128[<?>].popcount(self) => $$popcount(self);
macro int128[<?>].ctz(self) => $$ctz(self);
macro int128[<?>].clz(self) => $$clz(self);
macro int128[<?>] int128[<?>].fshl(hi, int128[<?>] lo, int128[<?>] shift) => $$fshl(hi, lo, shift);
macro int128[<?>] int128[<?>].fshr(hi, int128[<?>] lo, int128[<?>] shift) => $$fshr(hi, lo, shift);
macro int128[<?>] int128[<?>].rotl(self, int128[<?>] shift) => $$fshl(self, self, shift);
macro int128[<?>] int128[<?>].rotr(self, int128[<?>] shift) => $$fshr(self, self, shift);
macro uint.popcount(self) => $$popcount(self);
macro uint.ctz(self) => $$ctz(self);
macro uint.clz(self) => $$clz(self);
macro uint uint.fshl(hi, uint lo, uint shift) => $$fshl(hi, lo, shift);
macro uint uint.fshr(hi, uint lo, uint shift) => $$fshr(hi, lo, shift);
macro uint uint.rotl(self, uint shift) => $$fshl(self, self, shift);
macro uint uint.rotr(self, uint shift) => $$fshr(self, self, shift);
macro int.popcount(self) => $$popcount(self);
macro int.ctz(self) => $$ctz(self);
macro int.clz(self) => $$clz(self);
macro int int.fshl(hi, int lo, int shift) => $$fshl(hi, lo, shift);
macro int int.fshr(hi, int lo, int shift) => $$fshr(hi, lo, shift);
macro int int.rotl(self, int shift) => $$fshl(self, self, shift);
macro int int.rotr(self, int shift) => $$fshr(self, self, shift);
macro ushort.popcount(self) => $$popcount(self);
macro ushort.ctz(self) => $$ctz(self);
macro ushort.clz(self) => $$clz(self);
macro ushort ushort.fshl(hi, ushort lo, ushort shift) => $$fshl(hi, lo, shift);
macro ushort ushort.fshr(hi, ushort lo, ushort shift) => $$fshr(hi, lo, shift);
macro ushort ushort.rotl(self, ushort shift) => $$fshl(self, self, shift);
macro ushort ushort.rotr(self, ushort shift) => $$fshr(self, self, shift);
macro short.popcount(self) => $$popcount(self);
macro short.ctz(self) => $$ctz(self);
macro short.clz(self) => $$clz(self);
macro short short.fshl(hi, short lo, short shift) => $$fshl(hi, lo, shift);
macro short short.fshr(hi, short lo, short shift) => $$fshr(hi, lo, shift);
macro short short.rotl(self, short shift) => $$fshl(self, self, shift);
macro short short.rotr(self, short shift) => $$fshr(self, self, shift);
macro char.popcount(self) => $$popcount(self);
macro char.ctz(self) => $$ctz(self);
macro char.clz(self) => $$clz(self);
macro char char.fshl(hi, char lo, char shift) => $$fshl(hi, lo, shift);
macro char char.fshr(hi, char lo, char shift) => $$fshr(hi, lo, shift);
macro char char.rotl(self, char shift) => $$fshl(self, self, shift);
macro char char.rotr(self, char shift) => $$fshr(self, self, shift);
macro ichar.popcount(self) => $$popcount(self);
macro ichar.ctz(self) => $$ctz(self);
macro ichar.clz(self) => $$clz(self);
macro ichar ichar.fshl(hi, ichar lo, ichar shift) => $$fshl(hi, lo, shift);
macro ichar ichar.fshr(hi, ichar lo, ichar shift) => $$fshr(hi, lo, shift);
macro ichar ichar.rotl(self, ichar shift) => $$fshl(self, self, shift);
macro ichar ichar.rotr(self, ichar shift) => $$fshr(self, self, shift);
macro ulong.popcount(self) => $$popcount(self);
macro ulong.ctz(self) => $$ctz(self);
macro ulong.clz(self) => $$clz(self);
macro ulong ulong.fshl(hi, ulong lo, ulong shift) => $$fshl(hi, lo, shift);
macro ulong ulong.fshr(hi, ulong lo, ulong shift) => $$fshr(hi, lo, shift);
macro ulong ulong.rotl(self, ulong shift) => $$fshl(self, self, shift);
macro ulong ulong.rotr(self, ulong shift) => $$fshr(self, self, shift);
macro long.popcount(self) => $$popcount(self);
macro long.ctz(self) => $$ctz(self);
macro long.clz(self) => $$clz(self);
macro long long.fshl(hi, long lo, long shift) => $$fshl(hi, lo, shift);
macro long long.fshr(hi, long lo, long shift) => $$fshr(hi, lo, shift);
macro long long.rotl(self, long shift) => $$fshl(self, self, shift);
macro long long.rotr(self, long shift) => $$fshr(self, self, shift);
macro uint128.popcount(self) => $$popcount(self);
macro uint128.ctz(self) => $$ctz(self);
macro uint128.clz(self) => $$clz(self);
macro uint128 uint128.fshl(hi, uint128 lo, uint128 shift) => $$fshl(hi, lo, shift);
macro uint128 uint128.fshr(hi, uint128 lo, uint128 shift) => $$fshr(hi, lo, shift);
macro uint128 uint128.rotl(self, uint128 shift) => $$fshl(self, self, shift);
macro uint128 uint128.rotr(self, uint128 shift) => $$fshr(self, self, shift);
macro int128.popcount(self) => $$popcount(self);
macro int128.ctz(self) => $$ctz(self);
macro int128.clz(self) => $$clz(self);
macro int128 int128.fshl(hi, int128 lo, int128 shift) => $$fshl(hi, lo, shift);
macro int128 int128.fshr(hi, int128 lo, int128 shift) => $$fshr(hi, lo, shift);
macro int128 int128.rotl(self, int128 shift) => $$fshl(self, self, shift);
macro int128 int128.rotr(self, int128 shift) => $$fshr(self, self, shift);

View File

@@ -1,176 +0,0 @@
// 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::builtin;
fault VarCastResult
{
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) @autoimport
{
$typeof(variable) temp = variable;
defer variable = temp;
@body();
}
/**
* Convert a variant type to a type, returning an failure if there is a type mismatch.
*
* @param v `the variant to convert to the given type.`
* @param $Type `the type to convert to`
* @return `The variant.ptr converted to its type.`
**/
macro varcast(variant v, $Type) @autoimport
{
if (v.type != $Type.typeid) return VarCastResult.TYPE_MISMATCH!;
return ($Type*)v.ptr;
}
extern fn void printf(char*, ...);
struct CallstackElement
{
CallstackElement* prev;
char* function;
char* file;
uint line;
}
fn void panic(char* message, char *file, char *function, uint line) @autoimport
{
CallstackElement* stack = $$stacktrace();
$if ($defined(libc::stderr) && $defined(libc::fprintf)):
if (stack) stack = stack.prev;
if (stack)
{
libc::fprintf(@libc::stderr(), "\nERROR: '%s'\n", message);
}
else
{
libc::fprintf(@libc::stderr(), "\nERROR: '%s', function %s (%s:%d)\n", message, function, file, line);
}
while (stack)
{
libc::fprintf(@libc::stderr(), " at function %s (%s:%u)\n", stack.function, stack.file, stack.line);
if (stack == stack.prev) break;
stack = stack.prev;
}
$endif;
$$trap();
}
macro unreachable($string = "Unreachable statement reached.") @autoimport @noreturn
{
panic($string, $$FILE, $$FUNC, $$LINE);
$$unreachable();
}
/*
enum TypeKind
{
VOID,
BOOL,
FLOAT,
INTEGER,
STRUCT,
UNION,
ERROR,
ENUM,
ARRAY,
POINTER,
VAR_ARRAY,
SUBARRAY,
OPAQUE
// ALIAS,
}
struct TypeData
{
typeid typeId;
TypeKind kind;
int size;
int alignment;
char* name;
char* fullName;
}
struct TypeAlias
{
TypeData data;
typeid aliasType;
}
struct TypeError
{
TypeData data;
TypeErrorValue[] errors;
}
struct TypeArray
{
TypeData data;
typeid elementType;
ulong elements;
}
struct TypeVarArray
{
TypeData data;
typeid elementType;
}
struct TypeSubarray
{
TypeData data;
typeid elementType;
}
struct TypePointer
{
TypeData data;
typeid baseType;
}
struct TypeStruct
{
TypeData data;
TypeData*[] fields;
}
struct TypeUnion
{
TypeData data;
TypeData*[] variants;
}
struct TypeEnum
{
TypeData data;
typeid valueType;
TypeData*[] associated_value_types;
}
struct TypeEnumValue
{
char* name;
ulong value;
void*[] associated_values;
}
struct TypeErrorValue
{
char* name;
ulong value;
}
*/

View File

@@ -1,87 +0,0 @@
// 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::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:
define CInt = long;
define CUInt = ulong;
$case 32:
define CInt = int;
define CUInt = uint;
$case 16:
define CInt = short;
define CUInt = ushort;
$default:
$assert(false, "Invalid C int size");
$endswitch;
$switch ($$C_LONG_SIZE):
$case 64:
define CLong = long;
define CULong = ulong;
$case 32:
define CLong = int;
define CULong = uint;
$case 16:
define CLong = short;
define CULong = ushort;
$default:
$assert(false, "Invalid C long size");
$endswitch;
$switch ($$C_SHORT_SIZE):
$case 32:
define CShort = int;
define CUShort = uint;
$case 16:
define CShort = short;
define CUShort = ushort;
$case 8:
define CShort = ichar;
define CUShort = char;
$default:
$assert(false, "Invalid C short size");
$endswitch;
$switch ($$C_LONG_LONG_SIZE):
$case 128:
define CLongLong = int128;
define CULongLong = uint128;
$case 64:
define CLongLong = long;
define CULongLong = ulong;
$case 32:
define CLongLong = int;
define CULongLong = uint;
$case 16:
define CLongLong = short;
define CULongLong = ushort;
$default:
$assert(false, "Invalid C long long size");
$endswitch;
define CSChar = ichar;
define CUChar = char;
$if ($$C_CHAR_IS_SIGNED):
define CChar = ichar;
$else:
define CChar = char;
$endif;

View File

@@ -0,0 +1,514 @@
// Copyright (c) 2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::anylist;
import std::io,std::math;
def AnyPredicate = fn bool(any value);
def AnyTest = fn bool(any type, any context);
struct AnyList (Printable)
{
usz size;
usz capacity;
Allocator allocator;
any* entries;
}
<*
Use `init` for to use a custom allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = null) @deprecated("Use init(mem)")
{
return self.init(allocator ?: allocator::heap(), initial_capacity) @inline;
}
<*
@param [&inout] allocator "The allocator to use"
@param initial_capacity "The initial capacity to reserve"
*>
fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
{
self.allocator = allocator;
self.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
self.entries = allocator::alloc_array(allocator, any, initial_capacity);
}
else
{
self.entries = null;
}
self.capacity = initial_capacity;
return self;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit")
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16)
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
case 0:
return formatter.print("[]")!;
case 1:
return formatter.printf("[%s]", self.entries[0])!;
default:
usz n = formatter.print("[")!;
foreach (i, element : self.entries[:self.size])
{
if (i != 0) formatter.print(", ")!;
n += formatter.printf("%s", element)!;
}
n += formatter.print("]")!;
return n;
}
}
fn String AnyList.to_new_string(&self, Allocator allocator = null) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String AnyList.to_string(&self, Allocator allocator) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String AnyList.to_tstring(&self) => string::tformat("%s", *self);
<*
Push an element on the list by cloning it.
*>
macro void AnyList.push(&self, element)
{
if (!self.allocator) self.allocator = allocator::heap();
self.append_internal(allocator::clone(self.allocator, element));
}
fn void AnyList.append_internal(&self, any element) @local
{
self.ensure_capacity();
self.entries[self.size++] = element;
}
<*
Free a retained element removed using *_retained.
*>
fn void AnyList.free_element(&self, any element) @inline
{
allocator::free(self.allocator, element.ptr);
}
<*
Pop a value who's type is known. If the type is incorrect, this
will still pop the element.
@return! CastResult.TYPE_MISMATCH, IteratorResult.NO_MORE_ELEMENT
*>
macro AnyList.pop(&self, $Type)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return *anycast(self.entries[--self.size], $Type);
}
<*
Pop the last value and allocate the copy using the given allocator.
@return! IteratorResult.NO_MORE_ELEMENT
*>
fn any! AnyList.copy_pop(&self, Allocator allocator = allocator::heap())
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return allocator::clone_any(allocator, self.entries[--self.size]);
}
<*
Pop the last value and allocate the copy using the given allocator.
@return! IteratorResult.NO_MORE_ELEMENT
@deprecated `use copy_pop`
*>
fn any! AnyList.new_pop(&self, Allocator allocator = allocator::heap())
{
return self.copy_pop(allocator);
}
<*
Pop the last value and allocate the copy using the temp allocator
@return! IteratorResult.NO_MORE_ELEMENT
@deprecated `use tcopy_pop`
*>
fn any! AnyList.temp_pop(&self) => self.copy_pop(allocator::temp());
<*
Pop the last value and allocate the copy using the temp allocator
@return! IteratorResult.NO_MORE_ELEMENT
*>
fn any! AnyList.tcopy_pop(&self) => self.copy_pop(allocator::temp());
<*
Pop the last value. It must later be released using list.free_element()
@return! IteratorResult.NO_MORE_ELEMENT
*>
fn any! AnyList.pop_retained(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
fn void AnyList.clear(&self)
{
for (usz i = 0; i < self.size; i++)
{
self.free_element(self.entries[i]);
}
self.size = 0;
}
<*
Same as pop() but pops the first value instead.
*>
macro AnyList.pop_first(&self, $Type)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
return *anycast(self.entries[0], $Type);
}
<*
Same as pop_retained() but pops the first value instead.
*>
fn any! AnyList.pop_first_retained(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
<*
Same as new_pop() but pops the first value instead.
@deprecated `use copy_pop_first`
*>
fn any! AnyList.new_pop_first(&self, Allocator allocator = allocator::heap())
{
return self.copy_pop_first(allocator) @inline;
}
<*
Same as new_pop() but pops the first value instead.
*>
fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap())
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
defer self.remove_at(0);
return allocator::clone_any(allocator, self.entries[0]);
}
<*
Same as temp_pop() but pops the first value instead.
*>
fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(allocator::temp());
<*
Same as temp_pop() but pops the first value instead.
@deprecated `use tcopy_pop_first`
*>
fn any! AnyList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
<*
@require index < self.size
*>
fn void AnyList.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.free_element(self.entries[index]);
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
fn void AnyList.add_all(&self, AnyList* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
foreach (value : other_list)
{
self.entries[self.size++] = allocator::clone_any(self.allocator, value);
}
}
<*
Reverse the elements in a list.
*>
fn void AnyList.reverse(&self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
usz end = self.size - 1;
for (usz i = 0; i < half; i++)
{
self.swap(i, end - i);
}
}
fn any[] AnyList.array_view(&self)
{
return self.entries[:self.size];
}
<*
Push an element to the front of the list.
*>
macro void AnyList.push_front(&self, type)
{
self.insert_at(0, type);
}
<*
@require index < self.size
*>
macro void AnyList.insert_at(&self, usz index, type) @local
{
any value = allocator::copy(self.allocator, type);
self.insert_at_internal(self, index, value);
}
<*
@require index < self.size
*>
fn void AnyList.insert_at_internal(&self, usz index, any value) @local
{
self.ensure_capacity();
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.entries[index] = value;
}
<*
@require self.size > 0
*>
fn void AnyList.remove_last(&self)
{
self.free_element(self.entries[--self.size]);
}
<*
@require self.size > 0
*>
fn void AnyList.remove_first(&self)
{
self.remove_at(0);
}
macro AnyList.first(&self, $Type)
{
return *anycast(self.first_any(), $Type);
}
fn any! AnyList.first_any(&self) @inline
{
return self.size ? self.entries[0] : IteratorResult.NO_MORE_ELEMENT?;
}
macro AnyList.last(&self, $Type)
{
return *anycast(self.last_any(), $Type);
}
fn any! AnyList.last_any(&self) @inline
{
return self.size ? self.entries[self.size - 1] : IteratorResult.NO_MORE_ELEMENT?;
}
fn bool AnyList.is_empty(&self) @inline
{
return !self.size;
}
fn usz AnyList.len(&self) @operator(len) @inline
{
return self.size;
}
<*
@require index < self.size "Index out of range"
*>
macro AnyList.get(&self, usz index, $Type)
{
return *anycast(self.entries[index], $Type);
}
<*
@require index < self.size "Index out of range"
*>
fn any AnyList.get_any(&self, usz index) @inline
{
return self.entries[index];
}
fn void AnyList.free(&self)
{
if (!self.allocator) return;
self.clear();
allocator::free(self.allocator, self.entries);
self.capacity = 0;
self.entries = null;
}
fn void AnyList.swap(&self, usz i, usz j)
{
any temp = self.entries[i];
self.entries[i] = self.entries[j];
self.entries[j] = temp;
}
<*
@param filter "The function to determine if it should be removed or not"
@return "the number of deleted elements"
*>
fn usz AnyList.remove_if(&self, AnyPredicate filter)
{
return self._remove_if(filter, false);
}
<*
@param selection "The function to determine if it should be kept or not"
@return "the number of deleted elements"
*>
fn usz AnyList.retain_if(&self, AnyPredicate selection)
{
return self._remove_if(selection, true);
}
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && filter(&self.entries[i - 1])) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$endif
}
return size - self.size;
}
fn usz AnyList.remove_using_test(&self, AnyTest filter, any context)
{
return self._remove_using_test(filter, false, context);
}
fn usz AnyList.retain_using_test(&self, AnyTest filter, any context)
{
return self._remove_using_test(filter, true, context);
}
macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$endif
}
return size - self.size;
}
<*
Reserve at least min_capacity
*>
fn void AnyList.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
self.capacity = min_capacity;
}
macro any AnyList.@item_at(&self, usz index) @operator([])
{
return self.entries[index];
}
<*
@require index <= self.size "Index out of range"
*>
macro void AnyList.set(&self, usz index, value)
{
if (index == self.size)
{
self.push(value);
return;
}
self.free_element(self.entries[index]);
self.entries[index] = allocator::copy(self.allocator, value);
}
fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.reserve(new_capacity);
}

View File

@@ -0,0 +1,170 @@
<*
@require SIZE > 0
*>
module std::collections::bitset(<SIZE>);
def Type = uint;
const BITS = Type.sizeof * 8;
const SZ = (SIZE + BITS - 1) / BITS;
struct BitSet
{
Type[SZ] data;
}
fn usz BitSet.cardinality(&self)
{
usz n;
foreach (x : self.data)
{
n += x.popcount();
}
return n;
}
<*
@require i < SIZE
*>
fn void BitSet.set(&self, usz i)
{
usz q = i / BITS;
usz r = i % BITS;
self.data[q] |= 1 << r;
}
<*
@require i < SIZE
*>
fn void BitSet.unset(&self, usz i)
{
usz q = i / BITS;
usz r = i % BITS;
self.data[q] &= ~(1 << r);
}
<*
@require i < SIZE
*>
fn bool BitSet.get(&self, usz i) @operator([]) @inline
{
usz q = i / BITS;
usz r = i % BITS;
return self.data[q] & (1 << r) != 0;
}
fn usz BitSet.len(&self) @operator(len) @inline
{
return SZ * BITS;
}
<*
@require i < SIZE
*>
fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
{
if (value) return self.set(i);
self.unset(i);
}
<*
@require Type.kindof == UNSIGNED_INT
*>
module std::collections::growablebitset(<Type>);
import std::collections::list;
const BITS = Type.sizeof * 8;
def GrowableBitSetList = List(<Type>);
struct GrowableBitSet
{
GrowableBitSetList data;
}
<*
@param initial_capacity
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
*>
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
self.data.init(allocator, initial_capacity);
return self;
}
<*
@param initial_capacity
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
*>
fn GrowableBitSet* GrowableBitSet.init(&self, Allocator allocator, usz initial_capacity = 1)
{
self.data.init(allocator, initial_capacity);
return self;
}
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
fn GrowableBitSet* GrowableBitSet.tinit(&self, usz initial_capacity = 1)
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
fn void GrowableBitSet.free(&self)
{
self.data.free();
}
fn usz GrowableBitSet.cardinality(&self)
{
usz n;
foreach (x : self.data)
{
n += x.popcount();
}
return n;
}
fn void GrowableBitSet.set(&self, usz i)
{
usz q = i / BITS;
usz r = i % BITS;
usz current_len = self.data.len();
while (q >= current_len)
{
self.data.push(0);
current_len++;
}
self.data.set(q, self.data[q] | (1 << r));
}
fn void GrowableBitSet.unset(&self, usz i)
{
usz q = i / BITS;
usz r = i % BITS;
if (q >= self.data.len()) return;
self.data.set(q, self.data[q] &~ (1 << r));
}
fn bool GrowableBitSet.get(&self, usz i) @operator([]) @inline
{
usz q = i / BITS;
usz r = i % BITS;
if (q >= self.data.len()) return false;
return self.data[q] & (1 << r) != 0;
}
fn usz GrowableBitSet.len(&self) @operator(len)
{
usz n = self.data.len() * BITS;
if (n > 0) n -= (usz)self.data[^1].clz();
return n;
}
fn void GrowableBitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
{
if (value) return self.set(i);
self.unset(i);
}

View File

@@ -0,0 +1,455 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
<*
@require MAX_SIZE >= 1 `The size must be at least 1 element big.`
*>
module std::collections::elastic_array(<Type, MAX_SIZE>);
import std::io, std::math, std::collections::list_common;
def ElementPredicate = fn bool(Type *type);
def ElementTest = fn bool(Type *type, any context);
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
struct ElasticArray (Printable)
{
usz size;
Type[MAX_SIZE] entries;
}
fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
case 0:
return formatter.print("[]")!;
case 1:
return formatter.printf("[%s]", self.entries[0])!;
default:
usz n = formatter.print("[")!;
foreach (i, element : self.entries[:self.size])
{
if (i != 0) formatter.print(", ")!;
n += formatter.printf("%s", element)!;
}
n += formatter.print("]")!;
return n;
}
}
fn String ElasticArray.to_string(&self, Allocator allocator) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String ElasticArray.to_new_string(&self, Allocator allocator = nul) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String ElasticArray.to_tstring(&self)
{
return string::tformat("%s", *self);
}
fn void! ElasticArray.push_try(&self, Type element) @inline
{
if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?;
self.entries[self.size++] = element;
}
<*
@require self.size < MAX_SIZE `Tried to exceed the max size`
*>
fn void ElasticArray.push(&self, Type element) @inline
{
self.entries[self.size++] = element;
}
fn Type! ElasticArray.pop(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
fn void ElasticArray.clear(&self)
{
self.size = 0;
}
<*
@require self.size > 0
*>
fn Type! ElasticArray.pop_first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
<*
@require index < self.size
*>
fn void ElasticArray.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
<*
@require other_list.size + self.size <= MAX_SIZE
*>
fn void ElasticArray.add_all(&self, ElasticArray* other_list)
{
if (!other_list.size) return;
foreach (&value : other_list)
{
self.entries[self.size++] = *value;
}
}
<*
Add as many elements as possible to the new array,
returning the number of elements that didn't fit.
*>
fn usz ElasticArray.add_all_to_limit(&self, ElasticArray* other_list)
{
if (!other_list.size) return 0;
foreach (i, &value : other_list)
{
if (self.size == MAX_SIZE) return other_list.size - i;
self.entries[self.size++] = *value;
}
return 0;
}
<*
Add as many values from this array as possible, returning the
number of elements that didn't fit.
@param [in] array
*>
fn usz ElasticArray.add_array_to_limit(&self, Type[] array)
{
if (!array.len) return 0;
foreach (i, &value : array)
{
if (self.size == MAX_SIZE) return array.len - i;
self.entries[self.size++] = *value;
}
return 0;
}
<*
Add the values of an array to this list.
@param [in] array
@require array.len + self.size <= MAX_SIZE `Size would exceed max.`
@ensure self.size >= array.len
*>
fn void ElasticArray.add_array(&self, Type[] array)
{
if (!array.len) return;
foreach (&value : array)
{
self.entries[self.size++] = *value;
}
}
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
fn Type[] ElasticArray.to_new_aligned_array(&self)
{
return list_common::list_to_new_aligned_array(Type, self, allocator::heap());
}
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
fn Type[] ElasticArray.to_aligned_array(&self, Allocator allocator)
{
return list_common::list_to_new_aligned_array(Type, self, allocator);
}
<*
@require !type_is_overaligned() : "This function is not available on overaligned types"
*>
macro Type[] ElasticArray.to_new_array(&self)
{
return list_common::list_to_array(Type, self, allocator::heap());
}
<*
@require !type_is_overaligned() : "This function is not available on overaligned types"
*>
macro Type[] ElasticArray.to_array(&self, Allocator allocator)
{
return list_common::list_to_new_array(Type, self, allocator);
}
fn Type[] ElasticArray.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_aligned_array(allocator::temp());
$else
return self.to_array(allocator::temp());
$endif;
}
<*
Reverse the elements in a list.
*>
fn void ElasticArray.reverse(&self)
{
list_common::list_reverse(self);
}
fn Type[] ElasticArray.array_view(&self)
{
return self.entries[:self.size];
}
<*
@require self.size < MAX_SIZE `List would exceed max size`
*>
fn void ElasticArray.push_front(&self, Type type) @inline
{
self.insert_at(0, type);
}
<*
@require self.size < MAX_SIZE `List would exceed max size`
*>
fn void! ElasticArray.push_front_try(&self, Type type) @inline
{
return self.insert_at_try(0, type);
}
<*
@require index <= self.size
*>
fn void! ElasticArray.insert_at_try(&self, usz index, Type value)
{
if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?;
self.insert_at(index, value);
}
<*
@require self.size < MAX_SIZE `List would exceed max size`
@require index <= self.size
*>
fn void ElasticArray.insert_at(&self, usz index, Type type)
{
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.entries[index] = type;
}
<*
@require index < self.size
*>
fn void ElasticArray.set_at(&self, usz index, Type type)
{
self.entries[index] = type;
}
fn void! ElasticArray.remove_last(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.size--;
}
fn void! ElasticArray.remove_first(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.remove_at(0);
}
fn Type! ElasticArray.first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[0];
}
fn Type! ElasticArray.last(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[self.size - 1];
}
fn bool ElasticArray.is_empty(&self) @inline
{
return !self.size;
}
fn usz ElasticArray.byte_size(&self) @inline
{
return Type.sizeof * self.size;
}
fn usz ElasticArray.len(&self) @operator(len) @inline
{
return self.size;
}
fn Type ElasticArray.get(&self, usz index) @inline
{
return self.entries[index];
}
fn void ElasticArray.swap(&self, usz i, usz j)
{
@swap(self.entries[i], self.entries[j]);
}
<*
@param filter "The function to determine if it should be removed or not"
@return "the number of deleted elements"
*>
fn usz ElasticArray.remove_if(&self, ElementPredicate filter)
{
return list_common::list_remove_if(self, filter, false);
}
<*
@param selection "The function to determine if it should be kept or not"
@return "the number of deleted elements"
*>
fn usz ElasticArray.retain_if(&self, ElementPredicate selection)
{
return list_common::list_remove_if(self, selection, true);
}
fn usz ElasticArray.remove_using_test(&self, ElementTest filter, any context)
{
return list_common::list_remove_using_test(self, filter, false, context);
}
fn usz ElasticArray.retain_using_test(&self, ElementTest filter, any context)
{
return list_common::list_remove_using_test(self, filter, true, context);
}
macro Type ElasticArray.@item_at(&self, usz index) @operator([])
{
return self.entries[index];
}
fn Type* ElasticArray.get_ref(&self, usz index) @operator(&[]) @inline
{
return &self.entries[index];
}
fn void ElasticArray.set(&self, usz index, Type value) @operator([]=)
{
self.entries[index] = value;
}
// Functions for equatable types
fn usz! ElasticArray.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
}
fn usz! ElasticArray.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach_r (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
}
fn bool ElasticArray.equals(&self, ElasticArray other_list) @if(ELEMENT_IS_EQUATABLE)
{
if (self.size != other_list.size) return false;
foreach (i, v : self)
{
if (!equals(v, other_list.entries[i])) return false;
}
return true;
}
<*
Check for presence of a value in a list.
@param [&in] self "the list to find elements in"
@param value "The value to search for"
@return "True if the value is found, false otherwise"
*>
fn bool ElasticArray.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
foreach (i, v : self)
{
if (equals(v, value)) return true;
}
return false;
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "true if the value was found"
*>
fn bool ElasticArray.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return @ok(self.remove_at(self.rindex_of(value)));
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "true if the value was found"
*>
fn bool ElasticArray.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return @ok(self.remove_at(self.index_of(value)));
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "the number of deleted elements."
*>
fn usz ElasticArray.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return list_common::list_remove_item(self, value);
}
fn void ElasticArray.remove_all_from(&self, ElasticArray* other_list) @if(ELEMENT_IS_EQUATABLE)
{
if (!other_list.size) return;
foreach (v : other_list) self.remove_item(v);
}
<*
@param [&in] self
@return "The number non-null values in the list"
*>
fn usz ElasticArray.compact_count(&self) @if(ELEMENT_IS_POINTER)
{
usz vals = 0;
foreach (v : self) if (v) vals++;
return vals;
}
fn usz ElasticArray.compact(&self) @if(ELEMENT_IS_POINTER)
{
return list_common::list_compact(self);
}

View File

@@ -0,0 +1,71 @@
<*
@require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap"
*>
module std::collections::enummap(<Enum, ValueType>);
import std::io;
struct EnumMap (Printable)
{
ValueType[Enum.len] values;
}
fn void EnumMap.init(&self, ValueType init_value)
{
foreach (&a : self.values)
{
*a = init_value;
}
}
fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
{
usz n = formatter.print("{ ")!;
foreach (i, &value : self.values)
{
if (i != 0) formatter.print(", ")!;
n += formatter.printf("%s: %s", Enum.from_ordinal(i), *value)!;
}
n += formatter.print(" }")!;
return n;
}
fn String EnumMap.to_string(&self, Allocator allocator) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String EnumMap.to_new_string(&self, Allocator allocator = null) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String EnumMap.to_tstring(&self) @dynamic
{
return string::tformat("%s", *self);
}
<*
@return "The total size of this map, which is the same as the number of enum values"
@pure
*>
fn usz EnumMap.len(&self) @operator(len) @inline
{
return self.values.len;
}
<*
@return "Retrieve a value given the underlying enum, if there is no entry, then the zero value for the value is returned."
*>
fn ValueType EnumMap.get(&self, Enum key) @operator([]) @inline
{
return self.values[key.ordinal];
}
fn ValueType* EnumMap.get_ref(&self, Enum key) @operator(&[]) @inline
{
return &self.values[key.ordinal];
}
fn void EnumMap.set(&self, Enum key, ValueType value) @operator([]=) @inline
{
self.values[key.ordinal] = value;
}

View File

@@ -0,0 +1,177 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self 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 may be used with an enumset"
*>
module std::collections::enumset(<Enum>);
import std::io;
def EnumSetType = $typefrom(private::type_for_enum_elements(Enum.elements)) @private;
const IS_CHAR_ARRAY = Enum.elements > 128;
distinct EnumSet (Printable) = EnumSetType;
fn void EnumSet.add(&self, Enum v)
{
$if IS_CHAR_ARRAY:
(*self)[(usz)v.ordinal / 8] |= (char)(1u << ((usz)v.ordinal % 8));
$else
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v.ordinal);
$endif
}
fn void EnumSet.clear(&self)
{
$if IS_CHAR_ARRAY:
*self = {};
$else
*self = 0;
$endif
}
fn bool EnumSet.remove(&self, Enum v)
{
$if IS_CHAR_ARRAY:
if (!self.has(v) @inline) return false;
(*self)[(usz)v.ordinal / 8] &= (char)~(1u << ((usz)v.ordinal % 8));
return true;
$else
EnumSetType old = (EnumSetType)*self;
EnumSetType new = old & ~(1u << (EnumSetType)v.ordinal);
*self = (EnumSet)new;
return old != new;
$endif
}
fn bool EnumSet.has(&self, Enum v)
{
$if IS_CHAR_ARRAY:
return (bool)(((*self)[(usz)v.ordinal / 8] << ((usz)v.ordinal % 8)) & 0x01);
$else
return ((EnumSetType)*self & (1u << (EnumSetType)v.ordinal)) != 0;
$endif
}
fn void EnumSet.add_all(&self, EnumSet s)
{
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*self)[i] |= c;
$else
*self = (EnumSet)((EnumSetType)*self | (EnumSetType)s);
$endif
}
fn void EnumSet.retain_all(&self, EnumSet s)
{
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*self)[i] &= c;
$else
*self = (EnumSet)((EnumSetType)*self & (EnumSetType)s);
$endif
}
fn void EnumSet.remove_all(&self, EnumSet s)
{
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*self)[i] &= ~c;
$else
*self = (EnumSet)((EnumSetType)*self & ~(EnumSetType)s);
$endif
}
fn EnumSet EnumSet.and_of(&self, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *self;
copy.retain_all(s);
return copy;
$else
return (EnumSet)((EnumSetType)*self & (EnumSetType)s);
$endif
}
fn EnumSet EnumSet.or_of(&self, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *self;
copy.add_all(s);
return copy;
$else
return (EnumSet)((EnumSetType)*self | (EnumSetType)s);
$endif
}
fn EnumSet EnumSet.diff_of(&self, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *self;
copy.remove_all(s);
return copy;
$else
return (EnumSet)((EnumSetType)*self & ~(EnumSetType)s);
$endif
}
fn EnumSet EnumSet.xor_of(&self, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *self;
foreach (i, c : s) copy[i] ^= c;
return copy;
$else
return (EnumSet)((EnumSetType)*self ^ (EnumSetType)s);
$endif
}
fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
{
usz n = formatter.print("[")!;
bool found;
foreach (value : Enum.values)
{
if (!set.has(value)) continue;
if (found) n += formatter.print(", ")!;
found = true;
n += formatter.printf("%s", value)!;
}
n += formatter.print("]")!;
return n;
}
fn String EnumSet.to_new_string(&set, Allocator allocator = allocator::heap()) @dynamic
{
return string::format("%s", *set, allocator: allocator);
}
fn String EnumSet.to_string(&set, Allocator allocator) @dynamic
{
return string::format("%s", *set, allocator: allocator);
}
fn String EnumSet.to_tstring(&set) @dynamic
{
return string::tformat("%s", *set);
}
module std::collections::enumset::private;
macro typeid type_for_enum_elements(usz $elements)
{
$switch
$case ($elements > 128):
return char[($elements + 7) / 8].typeid;
$case ($elements > 64):
return uint128.typeid;
$case ($elements > 32 || $$C_INT_SIZE > 32):
return ulong.typeid;
$case ($elements > 16 || $$C_INT_SIZE > 16):
return uint.typeid;
$case ($elements > 8 || $$C_INT_SIZE > 8):
return ushort.typeid;
$default:
return char.typeid;
$endswitch
}

View File

@@ -0,0 +1,669 @@
// 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.
<*
@require $defined((Key){}.hash()) `No .hash function found on the key`
*>
module std::collections::map(<Key, Value>);
import std::math;
import std::io @norecurse;
struct HashMap (Printable)
{
Entry*[] table;
Allocator allocator;
uint count; // Number of elements
uint threshold; // Resize limit
float load_factor;
}
<*
@param [&inout] allocator "The allocator to use"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = null) @deprecated("Use init(mem)")
{
return self.init(allocator ?: allocator::heap(), capacity, load_factor);
}
<*
@param [&inout] allocator "The allocator to use"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.load_factor = load_factor;
self.threshold = (uint)(capacity * load_factor);
self.table = allocator::new_array(allocator, Entry*, capacity);
return self;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), capacity, load_factor) @inline;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init(allocator::temp(), capacity, load_factor) @inline;
}
<*
@param [&inout] allocator "The allocator to use"
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use init_with_key_values(mem)")
{
self.init(capacity, load_factor, allocator);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use init_from_keys_and_values(mem)")
{
assert(keys.len == values.len);
self.init(allocator, capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
}
<*
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Use tinit_with_key_values")
{
self.tinit(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
}
<*
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.tinit(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.temp_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use tinit_from_keys_and_values")
{
assert(keys.len == values.len);
self.tinit(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
self.tinit(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
}
<*
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(&map)
{
return (bool)map.allocator;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map) @deprecated("Use init_from_map(mem, map)")
{
return self.init_from_map(allocator::heap(), other_map) @inline;
}
<*
@param [&inout] allocator "The allocator to use"
@param [&in] other_map "The map to copy from."
*>
fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map)
{
self.init(allocator, other_map.table.len, other_map.load_factor);
self.put_all_for_create(other_map);
return self;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map) @deprecated("Use tinit_from_map")
{
return map.init_from_map(allocator::temp(), other_map) @inline;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn HashMap* HashMap.tinit_from_map(&map, HashMap* other_map)
{
return map.init_from_map(allocator::temp(), other_map) @inline;
}
fn bool HashMap.is_empty(&map) @inline
{
return !map.count;
}
fn usz HashMap.len(&map) @inline
{
return map.count;
}
fn Value*! HashMap.get_ref(&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(&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
@require $assignable(#expr, Value)
*>
macro Value HashMap.@get_or_set(&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(&map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
fn bool HashMap.has_key(&map, Key key)
{
return @ok(map.get_ref(key));
}
fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!map.allocator)
{
map.init(allocator::heap());
}
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(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
}
fn void HashMap.clear(&map)
{
if (!map.count) return;
foreach (Entry** &entry_ref : map.table)
{
Entry* entry = *entry_ref;
if (!entry) continue;
Entry *next = entry.next;
while (next)
{
Entry *to_delete = next;
next = next.next;
map.free_entry(to_delete);
}
map.free_entry(entry);
*entry_ref = null;
}
map.count = 0;
}
fn void HashMap.free(&map)
{
if (!map.allocator) return;
map.clear();
map.free_internal(map.table.ptr);
map.table = {};
}
fn Key[] HashMap.tcopy_keys(&map)
{
return map.copy_keys(allocator::temp()) @inline;
}
fn Key[] HashMap.key_tlist(&map) @deprecated("Use 'tcopy_keys'")
{
return map.copy_keys(allocator::temp()) @inline;
}
<*
@deprecated "use copy_keys"
*>
fn Key[] HashMap.key_new_list(&map, Allocator allocator = allocator::heap())
{
return map.copy_keys(allocator) @inline;
}
fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
$if COPY_KEYS:
list[index++] = entry.key.copy(allocator);
$else
list[index++] = entry.key;
$endif
entry = entry.next;
}
}
return list;
}
macro HashMap.@each(map; @body(key, value))
{
map.@each_entry(; Entry* entry) {
@body(entry.key, entry.value);
};
}
macro HashMap.@each_entry(map; @body(entry))
{
if (map.count)
{
foreach (Entry* entry : map.table)
{
while (entry)
{
@body(entry);
entry = entry.next;
}
}
}
}
<*
@deprecated `use tcopy_values`
*>
fn Value[] HashMap.value_tlist(&map)
{
return map.copy_values(allocator::temp()) @inline;
}
fn Value[] HashMap.tcopy_values(&map)
{
return map.copy_values(allocator::temp()) @inline;
}
<*
@deprecated `use copy_values`
*>
fn Value[] HashMap.value_new_list(&map, Allocator allocator = allocator::heap())
{
return map.copy_values(allocator);
}
fn Value[] HashMap.copy_values(&map, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.value;
entry = entry.next;
}
}
return list;
}
fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
{
if (!map.count) return false;
foreach (Entry* entry : map.table)
{
while (entry)
{
if (equals(v, entry.value)) return true;
entry = entry.next;
}
}
return false;
}
fn HashMapIterator HashMap.iter(&self)
{
return { .map = self, .index = -1 };
}
fn HashMapValueIterator HashMap.value_iter(&self)
{
return { .map = self, .index = -1 };
}
fn HashMapKeyIterator HashMap.key_iter(&self)
{
return { .map = self, .index = -1 };
}
// --- private methods
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(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(&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 = allocator::new_array(map.allocator, Entry*, new_capacity);
map.transfer(new_table);
map.table = new_table;
map.free_internal(old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
fn usz! HashMap.to_format(&self, Formatter* f) @dynamic
{
usz len;
len += f.print("{ ")!;
self.@each_entry(; Entry* entry)
{
if (len > 2) len += f.print(", ")!;
len += f.printf("%s: %s", entry.key, entry.value)!;
};
return len + f.print(" }");
}
fn void HashMap.transfer(&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(&map, HashMap* other_map) @private
{
if (!other_map.count) return;
foreach (Entry *e : other_map.table)
{
while (e)
{
map.put_for_create(e.key, e.value);
e = e.next;
}
}
}
fn void HashMap.put_for_create(&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(&map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
{
if (!map.count) return false;
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_entry(e);
return true;
}
prev = e;
e = next;
}
return false;
}
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
fn void HashMap.free_entry(&self, Entry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
$endif
self.free_internal(entry);
}
struct HashMapIterator
{
HashMap* map;
int top_index;
int index;
Entry* current_entry;
}
distinct HashMapValueIterator = HashMapIterator;
distinct HashMapKeyIterator = HashMapIterator;
<*
@require idx < self.map.count
*>
fn Entry HashMapIterator.get(&self, usz idx) @operator([])
{
if (idx < self.index)
{
self.top_index = 0;
self.current_entry = null;
self.index = -1;
}
while (self.index != idx)
{
if (self.current_entry)
{
self.current_entry = self.current_entry.next;
if (self.current_entry) self.index++;
continue;
}
self.current_entry = self.map.table[self.top_index++];
if (self.current_entry) self.index++;
}
return *self.current_entry;
}
fn Value HashMapValueIterator.get(&self, usz idx) @operator([])
{
return ((HashMapIterator*)self).get(idx).value;
}
fn Key HashMapKeyIterator.get(&self, usz idx) @operator([])
{
return ((HashMapIterator*)self).get(idx).key;
}
fn usz HashMapValueIterator.len(self) @operator(len) => self.map.count;
fn usz HashMapKeyIterator.len(self) @operator(len) => self.map.count;
fn usz HashMapIterator.len(self) @operator(len) => self.map.count;

View File

@@ -0,0 +1,348 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self 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>);
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
struct Node @private
{
Node *next;
Node *prev;
Type value;
}
struct LinkedList
{
Allocator allocator;
usz size;
Node *_first;
Node *_last;
}
<*
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
@return "the initialized list"
*>
fn LinkedList* LinkedList.init(&self, Allocator allocator)
{
*self = { .allocator = allocator };
return self;
}
<*
@return "the initialized list"
*>
fn LinkedList* LinkedList.new_init(&self) @deprecated("Use init(mem)")
{
return self.init(allocator::heap()) @inline;
}
fn LinkedList* LinkedList.temp_init(&self) @deprecated("Use tinit()")
{
return self.init(allocator::temp()) @inline;
}
fn LinkedList* LinkedList.tinit(&self)
{
return self.init(allocator::temp()) @inline;
}
<*
@require self.allocator != null
*>
macro void LinkedList.free_node(&self, Node* node) @private
{
allocator::free(self.allocator, node);
}
macro Node* LinkedList.alloc_node(&self) @private
{
if (!self.allocator) self.allocator = allocator::heap();
return allocator::alloc(self.allocator, Node);
}
fn void LinkedList.push_front(&self, Type value)
{
Node *first = self._first;
Node *new_node = self.alloc_node();
*new_node = { .next = first, .value = value };
self._first = new_node;
if (!first)
{
self._last = new_node;
}
else
{
first.prev = new_node;
}
self.size++;
}
fn void LinkedList.push(&self, Type value)
{
Node *last = self._last;
Node *new_node = self.alloc_node();
*new_node = { .prev = last, .value = value };
self._last = new_node;
if (!last)
{
self._first = new_node;
}
else
{
last.next = new_node;
}
self.size++;
}
fn Type! LinkedList.peek(&self) => self.first() @inline;
fn Type! LinkedList.peek_last(&self) => self.last() @inline;
fn Type! LinkedList.first(&self)
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
return self._first.value;
}
fn Type! LinkedList.last(&self)
{
if (!self._last) return IteratorResult.NO_MORE_ELEMENT?;
return self._last.value;
}
fn void LinkedList.free(&self) => self.clear() @inline;
fn void LinkedList.clear(&self)
{
for (Node* node = self._first; node != null;)
{
Node* next = node.next;
self.free_node(node);
node = next;
}
self._first = null;
self._last = null;
self.size = 0;
}
fn usz LinkedList.len(&self) @inline => self.size;
<*
@require index < self.size
*>
macro Node* LinkedList.node_at_index(&self, usz index)
{
if (index * 2 >= self.size)
{
Node* node = self._last;
index = self.size - index - 1;
while (index--) node = node.prev;
return node;
}
Node* node = self._first;
while (index--) node = node.next;
return node;
}
<*
@require index < self.size
*>
fn Type LinkedList.get(&self, usz index)
{
return self.node_at_index(index).value;
}
<*
@require index < self.size
*>
fn void LinkedList.set(&self, usz index, Type element)
{
self.node_at_index(index).value = element;
}
<*
@require index < self.size
*>
fn void LinkedList.remove_at(&self, usz index)
{
self.unlink(self.node_at_index(index));
}
<*
@require index <= self.size
*>
fn void LinkedList.insert_at(&self, usz index, Type element)
{
switch (index)
{
case 0:
self.push_front(element);
case self.size:
self.push(element);
default:
self.link_before(self.node_at_index(index), element);
}
}
<*
@require succ != null
*>
fn void LinkedList.link_before(&self, Node *succ, Type value) @private
{
Node* pred = succ.prev;
Node* new_node = self.alloc_node();
*new_node = { .prev = pred, .next = succ, .value = value };
succ.prev = new_node;
if (!pred)
{
self._first = new_node;
}
else
{
pred.next = new_node;
}
self.size++;
}
<*
@require self._first != null
*>
fn void LinkedList.unlink_first(&self) @private
{
Node* f = self._first;
Node* next = f.next;
self.free_node(f);
self._first = next;
if (!next)
{
self._last = null;
}
else
{
next.prev = null;
}
self.size--;
}
fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
usz start = self.size;
Node* node = self._first;
while (node)
{
switch
{
case equals(node.value, t):
Node* next = node.next;
self.unlink(node);
node = next;
default:
node = node.next;
}
}
return start - self.size;
}
fn Type! LinkedList.pop(&self)
{
if (!self._last) return IteratorResult.NO_MORE_ELEMENT?;
defer self.unlink_last();
return self._last.value;
}
fn bool LinkedList.is_empty(&self)
{
return !self._first;
}
fn Type! LinkedList.pop_front(&self)
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
defer self.unlink_first();
return self._first.value;
}
fn void! LinkedList.remove_last(&self) @maydiscard
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
self.unlink_last();
}
fn void! LinkedList.remove_first(&self) @maydiscard
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
self.unlink_first();
}
fn bool LinkedList.remove_first_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
for (Node* node = self._first; node != null; node = node.next)
{
if (node.value == t)
{
self.unlink(node);
return true;
}
}
return false;
}
fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
for (Node* node = self._last; node != null; node = node.prev)
{
if (node.value == t)
{
self.unlink(node);
return true;
}
}
return false;
}
<*
@require self._last != null
*>
fn void LinkedList.unlink_last(&self) @inline @private
{
Node* l = self._last;
Node* prev = l.prev;
self._last = prev;
self.free_node(l);
if (!prev)
{
self._first = null;
}
else
{
prev.next = null;
}
self.size--;
}
<*
@require x != null
*>
fn void LinkedList.unlink(&self, Node* x) @private
{
Node* next = x.next;
Node* prev = x.prev;
if (!prev)
{
self._first = next;
}
else
{
prev.next = next;
}
if (!next)
{
self._last = prev;
}
else
{
next.prev = prev;
}
self.free_node(x);
self.size--;
}

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

@@ -0,0 +1,627 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self 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::io, std::math, std::collections::list_common;
def ElementPredicate = fn bool(Type *type);
def ElementTest = fn bool(Type *type, any context);
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
struct List (Printable)
{
usz size;
usz capacity;
Allocator allocator;
Type *entries;
}
<*
@param initial_capacity "The initial capacity to reserve"
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
*>
fn List* List.init(&self, Allocator allocator, usz initial_capacity = 16)
{
self.allocator = allocator;
self.size = 0;
self.capacity = 0;
self.entries = null;
self.reserve(initial_capacity);
return self;
}
<*
@param initial_capacity "The initial capacity to reserve"
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
*>
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
self.allocator = allocator;
self.size = 0;
self.capacity = 0;
self.entries = null;
self.reserve(initial_capacity);
return self;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn List* List.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn List* List.tinit(&self, usz initial_capacity = 16)
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
<*
Initialize a new list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
*>
fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap()) @deprecated("Use init_with_array(mem)")
{
return self.init_with_array(allocator, values);
}
<*
Initialize a new list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
*>
fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
{
self.init(allocator, values.len) @inline;
self.add_array(values) @inline;
return self;
}
<*
Initialize a temporary list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
*>
fn List* List.temp_init_with_array(&self, Type[] values) @deprecated("Use tinit_with_array()")
{
self.tinit(values.len) @inline;
self.add_array(values) @inline;
return self;
}
<*
Initialize a temporary list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
*>
fn List* List.tinit_with_array(&self, Type[] values)
{
self.tinit(values.len) @inline;
self.add_array(values) @inline;
return self;
}
<*
@require self.capacity == 0 "The List must not be allocated"
*>
fn void List.init_wrapping_array(&self, Type[] types, Allocator allocator = allocator::heap())
{
self.allocator = allocator;
self.capacity = types.len;
self.entries = types.ptr;
self.set_size(types.len);
}
fn usz! List.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
case 0:
return formatter.print("[]")!;
case 1:
return formatter.printf("[%s]", self.entries[0])!;
default:
usz n = formatter.print("[")!;
foreach (i, element : self.entries[:self.size])
{
if (i != 0) formatter.print(", ")!;
n += formatter.printf("%s", element)!;
}
n += formatter.print("]")!;
return n;
}
}
fn String List.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String List.to_tstring(&self)
{
return string::tformat("%s", *self);
}
fn void List.push(&self, Type element) @inline
{
self.reserve(1);
self.entries[self.set_size(self.size + 1)] = element;
}
fn Type! List.pop(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.set_size(self.size - 1);
return self.entries[self.size - 1];
}
fn void List.clear(&self)
{
self.set_size(0);
}
fn Type! List.pop_first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
<*
@require index < self.size `Removed element out of bounds`
*>
fn void List.remove_at(&self, usz index)
{
self.set_size(self.size - 1);
if (!self.size || index == self.size) return;
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
fn void List.add_all(&self, List* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
usz index = self.set_size(self.size + other_list.size);
foreach (&value : other_list)
{
self.entries[index++] = *value;
}
}
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
fn Type[] List.to_new_aligned_array(&self, Allocator allocator = allocator::heap())
{
return list_common::list_to_new_aligned_array(Type, self, allocator);
}
<*
@require !type_is_overaligned() : "This function is not available on overaligned types"
*>
macro Type[] List.to_new_array(&self, Allocator allocator = allocator::heap())
{
return list_common::list_to_new_array(Type, self, allocator);
}
fn Type[] List.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_new_aligned_array(allocator::temp());
$else
return self.to_new_array(allocator::temp());
$endif;
}
<*
Reverse the elements in a list.
*>
fn void List.reverse(&self)
{
list_common::list_reverse(self);
}
fn Type[] List.array_view(&self)
{
return self.entries[:self.size];
}
<*
Add the values of an array to this list.
@param [in] array
@ensure self.size >= array.len
*>
fn void List.add_array(&self, Type[] array)
{
if (!array.len) return;
self.reserve(array.len);
usz index = self.set_size(self.size + array.len);
self.entries[index : array.len] = array[..];
}
fn void List.push_front(&self, Type type) @inline
{
self.insert_at(0, type);
}
<*
@require index <= self.size `Insert was out of bounds`
*>
fn void List.insert_at(&self, usz index, Type type)
{
self.reserve(1);
self.set_size(self.size + 1);
for (isz i = self.size - 1; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.entries[index] = type;
}
<*
@require index < self.size
*>
fn void List.set_at(&self, usz index, Type type)
{
self.entries[index] = type;
}
fn void! List.remove_last(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.set_size(self.size - 1);
}
fn void! List.remove_first(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.remove_at(0);
}
fn Type! List.first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[0];
}
fn Type! List.last(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[self.size - 1];
}
fn bool List.is_empty(&self) @inline
{
return !self.size;
}
fn usz List.byte_size(&self) @inline
{
return Type.sizeof * self.size;
}
fn usz List.len(&self) @operator(len) @inline
{
return self.size;
}
<*
@require index < self.size `Access out of bounds`
*>
fn Type List.get(&self, usz index) @inline
{
return self.entries[index];
}
fn void List.free(&self)
{
if (!self.allocator || !self.capacity) return;
self.pre_free(); // Remove sanitizer annotation
$if type_is_overaligned():
allocator::free_aligned(self.allocator, self.entries);
$else
allocator::free(self.allocator, self.entries);
$endif;
self.capacity = 0;
self.size = 0;
self.entries = null;
}
<*
@require i < self.size && j < self.size `Access out of bounds`
*>
fn void List.swap(&self, usz i, usz j)
{
@swap(self.entries[i], self.entries[j]);
}
<*
@param filter "The function to determine if it should be removed or not"
@return "the number of deleted elements"
*>
fn usz List.remove_if(&self, ElementPredicate filter)
{
return list_common::list_remove_if(self, filter, false);
}
<*
@param selection "The function to determine if it should be kept or not"
@return "the number of deleted elements"
*>
fn usz List.retain_if(&self, ElementPredicate selection)
{
return list_common::list_remove_if(self, selection, true);
}
fn usz List.remove_using_test(&self, ElementTest filter, any context)
{
usz old_size = self.size;
defer
{
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_remove_using_test(self, filter, false, context);
}
fn usz List.retain_using_test(&self, ElementTest filter, any context)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_remove_using_test(self, filter, true, context);
}
fn void List.ensure_capacity(&self, usz min_capacity) @local
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
self.pre_free(); // Remove sanitizer annotation
min_capacity = math::next_power_of_2(min_capacity);
$if type_is_overaligned():
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, alignment: Type[1].alignof)!!;
$else
self.entries = allocator::realloc(self.allocator, self.entries, Type.sizeof * min_capacity);
$endif;
self.capacity = min_capacity;
self.post_alloc(); // Add sanitizer annotation
}
<*
@require index < self.size `Access out of bounds`
*>
macro Type List.@item_at(&self, usz index) @operator([])
{
return self.entries[index];
}
<*
@require index < self.size `Access out of bounds`
*>
fn Type* List.get_ref(&self, usz index) @operator(&[]) @inline
{
return &self.entries[index];
}
<*
@require index < self.size `Access out of bounds`
*>
fn void List.set(&self, usz index, Type value) @operator([]=)
{
self.entries[index] = value;
}
fn void List.reserve(&self, usz added)
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.ensure_capacity(new_capacity);
}
fn void List._update_size_change(&self,usz old_size, usz new_size)
{
if (old_size == new_size) return;
sanitizer::annotate_contiguous_container(self.entries,
&self.entries[self.capacity],
&self.entries[old_size],
&self.entries[new_size]);
}
<*
@require new_size == 0 || self.capacity != 0
*>
fn usz List.set_size(&self, usz new_size) @inline @private
{
usz old_size = self.size;
self._update_size_change(old_size, new_size);
self.size = new_size;
return old_size;
}
macro void List.pre_free(&self) @private
{
if (!self.capacity) return;
self._update_size_change(self.size, self.capacity);
}
<*
@require self.capacity > 0
*>
macro void List.post_alloc(&self) @private
{
self._update_size_change(self.capacity, self.size);
}
// Functions for equatable types
fn usz! List.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
}
fn usz! List.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach_r (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
}
fn bool List.equals(&self, List other_list) @if(ELEMENT_IS_EQUATABLE)
{
if (self.size != other_list.size) return false;
foreach (i, v : self)
{
if (!equals(v, other_list.entries[i])) return false;
}
return true;
}
<*
Check for presence of a value in a list.
@param [&in] self "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(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
foreach (i, v : self)
{
if (equals(v, value)) return true;
}
return false;
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "true if the value was found"
*>
fn bool List.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return @ok(self.remove_at(self.rindex_of(value)));
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "true if the value was found"
*>
fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return @ok(self.remove_at(self.index_of(value)));
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "the number of deleted elements."
*>
fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_remove_item(self, value);
}
fn void List.remove_all_from(&self, List* other_list) @if(ELEMENT_IS_EQUATABLE)
{
if (!other_list.size) return;
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
foreach (v : other_list) self.remove_item(v);
}
<*
@param [&in] self
@return "The number non-null values in the list"
*>
fn usz List.compact_count(&self) @if(ELEMENT_IS_POINTER)
{
usz vals = 0;
foreach (v : self) if (v) vals++;
return vals;
}
fn usz List.compact(&self) @if(ELEMENT_IS_POINTER)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_compact(self);
}
// --> Deprecated
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "true if the value was found"
*>
fn bool List.remove_last_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
{
return self.remove_last_item(value) @inline;
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "true if the value was found"
*>
fn bool List.remove_first_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
{
return self.remove_first_item(value) @inline;
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "the number of deleted elements."
*>
fn usz List.remove_all_matches(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
{
return self.remove_item(value) @inline;
}

View File

@@ -0,0 +1,112 @@
module std::collections::list_common;
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
macro list_to_new_aligned_array($Type, self, Allocator allocator)
{
if (!self.size) return ($Type[]){};
$Type[] result = allocator::alloc_array_aligned(allocator, $Type, self.size);
result[..] = self.entries[:self.size];
return result;
}
macro list_to_new_array($Type, self, Allocator allocator)
{
if (!self.size) return ($Type[]){};
$Type[] result = allocator::alloc_array(allocator, $Type, self.size);
result[..] = self.entries[:self.size];
return result;
}
macro void list_reverse(self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
usz end = self.size - 1;
for (usz i = 0; i < half; i++)
{
@swap(self.entries[i], self.entries[end - i]);
}
}
macro usz list_remove_using_test(self, filter, bool $invert, ctx)
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$endif
}
return size - self.size;
}
macro usz list_compact(self)
{
usz size = self.size;
for (usz i = size; i > 0; i--)
{
if (self.entries[i - 1]) continue;
for (usz j = i; j < size; j++)
{
self.entries[j - 1] = self.entries[j];
}
self.size--;
}
return size - self.size;
}
macro usz list_remove_item(self, value)
{
usz size = self.size;
for (usz i = size; i > 0; i--)
{
if (!equals(self.entries[i - 1], value)) continue;
for (usz j = i; j < self.size; j++)
{
self.entries[j - 1] = self.entries[j];
}
self.size--;
}
return size - self.size;
}
macro usz list_remove_if(self, filter, bool $invert)
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && filter(&self.entries[i - 1])) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$endif
}
return size - self.size;
}

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

@@ -0,0 +1,508 @@
// 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;
const VALUE_IS_EQUATABLE = Value.is_eq;
const bool COPY_KEYS = types::implements_copy(Key);
distinct Map = void*;
struct MapImpl
{
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 capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Map is deprecated")
{
MapImpl* map = allocator::alloc(allocator, MapImpl);
_init(map, capacity, load_factor, allocator);
return (Map)map;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map temp(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
MapImpl* map = mem::temp_alloc(MapImpl);
_init(map, capacity, load_factor, allocator::temp());
return (Map)map;
}
<*
@param [&inout] allocator "The allocator to use"
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro Map new_init_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
Map map = new(capacity, load_factor, allocator);
$for (var $i = 0; $i < $vacount; $i += 2)
map.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return map;
}
<*
@param [in] keys "Array of keys for the Map entries"
@param [in] values "Array of values for the Map entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map new_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
Map map = new(capacity, load_factor, allocator);
for (usz i = 0; i < keys.len; i++)
{
map.set(keys[i], values[i]);
}
return map;
}
<*
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro Map temp_new_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
Map map = temp(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
map.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return map;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map temp_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
Map map = temp(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
map.set(keys[i], values[i]);
}
return map;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn Map new_from_map(Map other_map, Allocator allocator = null)
{
MapImpl* other_map_impl = (MapImpl*)other_map;
if (!other_map_impl)
{
if (allocator) return new(allocator: allocator);
return null;
}
MapImpl* map = (MapImpl*)new(other_map_impl.table.len, other_map_impl.load_factor, allocator ?: allocator::heap());
if (!other_map_impl.count) return (Map)map;
foreach (Entry *e : other_map_impl.table)
{
while (e)
{
map._put_for_create(e.key, e.value);
e = e.next;
}
}
return (Map)map;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn Map temp_from_map(Map other_map)
{
return new_from_map(other_map, allocator::temp());
}
fn bool Map.is_empty(map) @inline
{
return !map || !((MapImpl*)map).count;
}
fn usz Map.len(map) @inline
{
return map ? ((MapImpl*)map).count : 0;
}
fn Value*! Map.get_ref(self, Key key)
{
MapImpl *map = (MapImpl*)self;
if (!map || !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*! Map.get_entry(map, Key key)
{
MapImpl *map_impl = (MapImpl*)map;
if (!map_impl || !map_impl.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map_impl.table[index_for(hash, map_impl.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
@require $assignable(#expr, Value)
*>
macro Value Map.@get_or_set(&self, Key key, Value #expr)
{
MapImpl *map = (MapImpl*)*self;
if (!map || !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! Map.get(map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
fn bool Map.has_key(map, Key key)
{
return @ok(map.get_ref(key));
}
macro Value Map.set_value_return(&map, Key key, Value value) @operator([]=)
{
map.set(key, value);
return value;
}
fn bool Map.set(&self, Key key, Value value)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!*self) *self = new();
MapImpl* map = (MapImpl*)*self;
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! Map.remove(map, Key key) @maydiscard
{
if (!map || !((MapImpl*)map)._remove_entry_for_key(key)) return SearchResult.MISSING?;
}
fn void Map.clear(self)
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return;
foreach (Entry** &entry_ref : map.table)
{
Entry* entry = *entry_ref;
if (!entry) continue;
Entry *next = entry.next;
while (next)
{
Entry *to_delete = next;
next = next.next;
map._free_entry(to_delete);
}
map._free_entry(entry);
*entry_ref = null;
}
map.count = 0;
}
fn void Map.free(self)
{
if (!self) return;
MapImpl* map = (MapImpl*)self;
self.clear();
map._free_internal(map.table.ptr);
map.table = {};
allocator::free(map.allocator, map);
}
fn Key[] Map.temp_keys_list(map)
{
return map.new_keys_list(allocator::temp()) @inline;
}
fn Key[] Map.new_keys_list(self, Allocator allocator = allocator::heap())
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.key;
entry = entry.next;
}
}
return list;
}
macro Map.@each(map; @body(key, value))
{
map.@each_entry(; Entry* entry) {
@body(entry.key, entry.value);
};
}
macro Map.@each_entry(self; @body(entry))
{
MapImpl *map = (MapImpl*)self;
if (!map || !map.count) return;
foreach (Entry* entry : map.table)
{
while (entry)
{
@body(entry);
entry = entry.next;
}
}
}
fn Value[] Map.temp_values_list(map)
{
return map.new_values_list(allocator::temp()) @inline;
}
fn Value[] Map.new_values_list(self, Allocator allocator = allocator::heap())
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.value;
entry = entry.next;
}
}
return list;
}
fn bool Map.has_value(self, Value v) @if(VALUE_IS_EQUATABLE)
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return false;
foreach (Entry* entry : map.table)
{
while (entry)
{
if (equals(v, entry.value)) return true;
entry = entry.next;
}
}
return false;
}
// --- private methods
fn void MapImpl._add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(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 MapImpl._resize(&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 = allocator::new_array(map.allocator, Entry*, new_capacity);
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 MapImpl._transfer(&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 _init(MapImpl* impl, uint capacity, float load_factor, Allocator allocator) @private
{
capacity = math::next_power_of_2(capacity);
*impl = {
.allocator = allocator,
.load_factor = load_factor,
.threshold = (uint)(capacity * load_factor),
.table = allocator::new_array(allocator, Entry*, capacity)
};
}
fn void MapImpl._put_for_create(&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 MapImpl._free_internal(&map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool MapImpl._remove_entry_for_key(&map, Key key) @private
{
if (!map.count) return false;
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_entry(e);
return true;
}
prev = e;
e = next;
}
return false;
}
fn void MapImpl._create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
fn void MapImpl._free_entry(&self, Entry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
$endif
self._free_internal(entry);
}
struct Entry
{
uint hash;
Key key;
Value value;
Entry* next;
}

View File

@@ -0,0 +1,46 @@
module std::collections::maybe(<Type>);
import std::io;
struct Maybe (Printable)
{
Type value;
bool has_value;
}
fn usz! Maybe.to_format(&self, Formatter* f) @dynamic
{
if (self.has_value) return f.printf("[%s]", self.value);
return f.printf("[EMPTY]");
}
fn void Maybe.set(&self, Type val)
{
*self = { .value = val, .has_value = true };
}
fn void Maybe.reset(&self)
{
*self = {};
}
fn Maybe value(Type val)
{
return { .value = val, .has_value = true };
}
fn Maybe Maybe.with_value(Type val) @deprecated("Use maybe::value instead.") @operator(construct)
{
return { .value = val, .has_value = true };
}
fn Maybe Maybe.empty() @deprecated("Use maybe::EMPTY instead.") @operator(construct)
{
return { };
}
const Maybe EMPTY = { };
macro Type! Maybe.get(self)
{
return self.has_value ? self.value : SearchResult.MISSING?;
}

View File

@@ -0,0 +1,468 @@
// 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, std::collections::list, 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 (Printable)
{
typeid type;
Allocator allocator;
union
{
uint128 i;
double f;
bool b;
String s;
void* other;
ObjectInternalList array;
ObjectInternalMap map;
}
}
fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.type)
{
case void:
return formatter.printf("{}")!;
case void*:
return formatter.printf("null")!;
case String:
return formatter.printf(`"%s"`, self.s)!;
case bool:
return formatter.printf(self.b ? "true" : "false")!;
case ObjectInternalList:
usz n = formatter.printf("[")!;
foreach (i, ol : self.array)
{
if (i > 0) n += formatter.printf(",")!;
n += ol.to_format(formatter)!;
}
n += formatter.printf("]")!;
return n;
case ObjectInternalMap:
usz n = formatter.printf("{")!;
@stack_mem(1024; Allocator mem)
{
foreach (i, key : self.map.copy_keys(mem))
{
if (i > 0) n += formatter.printf(",")!;
n += formatter.printf(`"%s":`, key)!;
n += self.map.get(key).to_format(formatter)!;
}
};
n += formatter.printf("}")!;
return n;
default:
switch (self.type.kindof)
{
case SIGNED_INT:
return formatter.printf("%d", (int128)self.i)!;
case UNSIGNED_INT:
return formatter.printf("%d", (uint128)self.i)!;
case FLOAT:
return formatter.printf("%g", self.f)!;
case ENUM:
return formatter.printf("%d", self.i)!;
default:
return formatter.printf("<>")!;
}
}
}
fn Object* new_obj(Allocator allocator)
{
return allocator::new(allocator, Object, { .allocator = allocator, .type = void.typeid });
}
fn Object* new_null()
{
return &NULL_OBJECT;
}
fn Object* new_int(int128 i, Allocator allocator)
{
return allocator::new(allocator, Object, { .i = i, .allocator = allocator, .type = int128.typeid });
}
macro Object* new_enum(e, Allocator allocator)
{
return allocator::new(allocator, Object, { .i = (int128)e, .allocator = allocator, .type = @typeid(e) });
}
fn Object* new_float(double f, Allocator allocator)
{
return allocator::new(allocator, Object, { .f = f, .allocator = allocator, .type = double.typeid });
}
fn Object* new_string(String s, Allocator allocator)
{
return allocator::new(allocator, Object, { .s = s.copy(allocator), .allocator = allocator, .type = String.typeid });
}
fn Object* new_bool(bool b)
{
return b ? &TRUE_OBJECT : &FALSE_OBJECT;
}
fn void Object.free(&self)
{
switch (self.type)
{
case void:
break;
case String:
allocator::free(self.allocator, self.s);
case ObjectInternalList:
foreach (ol : self.array)
{
ol.free();
}
self.array.free();
case ObjectInternalMap:
self.map.@each_entry(; ObjectInternalMapEntry* entry) {
entry.value.free();
};
self.map.free();
default:
break;
}
if (self.allocator) allocator::free(self.allocator, self);
}
fn bool Object.is_null(&self) @inline => self == &NULL_OBJECT;
fn bool Object.is_empty(&self) @inline => self.type == void.typeid;
fn bool Object.is_map(&self) @inline => self.type == ObjectInternalMap.typeid;
fn bool Object.is_array(&self) @inline => self.type == ObjectInternalList.typeid;
fn bool Object.is_bool(&self) @inline => self.type == bool.typeid;
fn bool Object.is_string(&self) @inline => self.type == String.typeid;
fn bool Object.is_float(&self) @inline => self.type == double.typeid;
fn bool Object.is_int(&self) @inline => self.type == int128.typeid;
fn bool Object.is_keyable(&self) => self.is_empty() || self.is_map();
fn bool Object.is_indexable(&self) => self.is_empty() || self.is_array();
<*
@require self.is_keyable()
*>
fn void Object.init_map_if_needed(&self) @private
{
if (self.is_empty())
{
self.type = ObjectInternalMap.typeid;
self.map.init(self.allocator);
}
}
<*
@require self.is_indexable()
*>
fn void Object.init_array_if_needed(&self) @private
{
if (self.is_empty())
{
self.type = ObjectInternalList.typeid;
self.array.init(self.allocator);
}
}
<*
@require self.is_keyable()
*>
fn void Object.set_object(&self, String key, Object* new_object) @private
{
self.init_map_if_needed();
ObjectInternalMapEntry*! entry = self.map.get_entry(key);
defer
{
(void)entry.value.free();
}
self.map.set(key, new_object);
}
macro Object* Object.object_from_value(&self, value) @private
{
var $Type = $typeof(value);
$switch
$case types::is_int($Type):
return new_int(value, self.allocator);
$case types::is_float($Type):
return new_float(value, self.allocator);
$case $Type.typeid == String.typeid:
return new_string(value, self.allocator);
$case $Type.typeid == bool.typeid:
return new_bool(value);
$case $Type.typeid == Object*.typeid:
return value;
$case $Type.typeid == void*.typeid:
if (value != null) return CastResult.TYPE_MISMATCH?;
return &NULL_OBJECT;
$case $assignable(value, String):
return new_string(value, self.allocator);
$default:
$error "Unsupported object type.";
$endswitch
}
macro Object* Object.set(&self, String key, value)
{
Object* val = self.object_from_value(value);
self.set_object(key, val);
return val;
}
<*
@require self.is_indexable()
*>
macro Object* Object.set_at(&self, usz index, String key, value)
{
Object* val = self.object_from_value(value);
self.set_object_at(key, index, val);
return val;
}
<*
@require self.is_indexable()
@ensure return != null
*>
macro Object* Object.push(&self, value)
{
Object* val = self.object_from_value(value);
self.push_object(val);
return val;
}
<*
@require self.is_keyable()
*>
fn Object*! Object.get(&self, String key) => self.is_empty() ? SearchResult.MISSING? : self.map.get(key);
fn bool Object.has_key(&self, String key) => self.is_map() && self.map.has_key(key);
<*
@require self.is_indexable()
*>
fn Object* Object.get_at(&self, usz index)
{
return self.array.get(index);
}
<*
@require self.is_indexable()
*>
fn usz Object.get_len(&self)
{
return self.array.len();
}
<*
@require self.is_indexable()
*>
fn void Object.push_object(&self, Object* to_append)
{
self.init_array_if_needed();
self.array.push(to_append);
}
<*
@require self.is_indexable()
*>
fn void Object.set_object_at(&self, usz index, Object* to_set)
{
self.init_array_if_needed();
while (self.array.len() < index)
{
self.array.push(&NULL_OBJECT);
}
if (self.array.len() == index)
{
self.array.push(to_set);
return;
}
self.array.get(index).free();
self.array.set_at(index, to_set);
}
<*
@require $Type.kindof.is_int() "Expected an integer type."
*>
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 self.is_indexable()
@require $Type.kindof.is_int() : "Expected an integer type"
*>
macro Object.get_integer_at(&self, $Type, usz index) @private
{
return get_integer_value(self.get_at(index), $Type);
}
<*
@require self.is_keyable()
@require $Type.kindof.is_int() : "Expected an integer type"
*>
macro Object.get_integer(&self, $Type, String key) @private
{
return get_integer_value(self.get(key), $Type);
}
fn ichar! Object.get_ichar(&self, String key) => self.get_integer(ichar, key);
fn short! Object.get_short(&self, String key) => self.get_integer(short, key);
fn int! Object.get_int(&self, String key) => self.get_integer(int, key);
fn long! Object.get_long(&self, String key) => self.get_integer(long, key);
fn int128! Object.get_int128(&self, String key) => self.get_integer(int128, key);
fn ichar! Object.get_ichar_at(&self, usz index) => self.get_integer_at(ichar, index);
fn short! Object.get_short_at(&self, usz index) => self.get_integer_at(short, index);
fn int! Object.get_int_at(&self, usz index) => self.get_integer_at(int, index);
fn long! Object.get_long_at(&self, usz index) => self.get_integer_at(long, index);
fn int128! Object.get_int128_at(&self, usz index) => self.get_integer_at(int128, index);
fn char! Object.get_char(&self, String key) => self.get_integer(ichar, key);
fn short! Object.get_ushort(&self, String key) => self.get_integer(ushort, key);
fn uint! Object.get_uint(&self, String key) => self.get_integer(uint, key);
fn ulong! Object.get_ulong(&self, String key) => self.get_integer(ulong, key);
fn uint128! Object.get_uint128(&self, String key) => self.get_integer(uint128, key);
fn char! Object.get_char_at(&self, usz index) => self.get_integer_at(char, index);
fn ushort! Object.get_ushort_at(&self, usz index) => self.get_integer_at(ushort, index);
fn uint! Object.get_uint_at(&self, usz index) => self.get_integer_at(uint, index);
fn ulong! Object.get_ulong_at(&self, usz index) => self.get_integer_at(ulong, index);
fn uint128! Object.get_uint128_at(&self, usz index) => self.get_integer_at(uint128, index);
<*
@require self.is_keyable()
*>
fn String! Object.get_string(&self, String key)
{
Object* value = self.get(key)!;
if (!value.is_string()) return CastResult.TYPE_MISMATCH?;
return value.s;
}
<*
@require self.is_indexable()
*>
fn String! Object.get_string_at(&self, usz index)
{
Object* value = self.get_at(index);
if (!value.is_string()) return CastResult.TYPE_MISMATCH?;
return value.s;
}
<*
@require self.is_keyable()
*>
macro String! Object.get_enum(&self, $EnumType, String key)
{
Object value = self.get(key)!;
if ($EnumType.typeid != value.type) return CastResult.TYPE_MISMATCH?;
return ($EnumType)value.i;
}
<*
@require self.is_indexable()
*>
macro String! Object.get_enum_at(&self, $EnumType, usz index)
{
Object value = self.get_at(index);
if ($EnumType.typeid != value.type) return CastResult.TYPE_MISMATCH?;
return ($EnumType)value.i;
}
<*
@require self.is_keyable()
*>
fn bool! Object.get_bool(&self, String key)
{
Object* value = self.get(key)!;
if (!value.is_bool()) return CastResult.TYPE_MISMATCH?;
return value.b;
}
<*
@require self.is_indexable()
*>
fn bool! Object.get_bool_at(&self, usz index)
{
Object* value = self.get_at(index);
if (!value.is_bool()) return CastResult.TYPE_MISMATCH?;
return value.b;
}
<*
@require self.is_keyable()
*>
fn double! Object.get_float(&self, String key)
{
Object* value = self.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:
return CastResult.TYPE_MISMATCH?;
}
}
<*
@require self.is_indexable()
*>
fn double! Object.get_float_at(&self, usz index)
{
Object* value = self.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:
return CastResult.TYPE_MISMATCH?;
}
}
fn Object* Object.get_or_create_obj(&self, String key)
{
if (try obj = self.get(key) && !obj.is_null()) return obj;
Object* container = new_obj(self.allocator);
self.set(key, container);
return container;
}
def ObjectInternalMap = HashMap(<String, Object*>) @private;
def ObjectInternalList = List(<Object*>) @private;
def ObjectInternalMapEntry = Entry(<String, Object*>) @private;

View File

@@ -0,0 +1,163 @@
// 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::priorityqueue::private;
distinct PriorityQueue = inline PrivatePriorityQueue(<Type, false>);
distinct PriorityQueueMax = inline PrivatePriorityQueue(<Type, true>);
module std::collections::priorityqueue::private(<Type, MAX>);
import std::collections::list, std::io;
def Heap = List(<Type>);
struct PrivatePriorityQueue (Printable)
{
Heap heap;
}
fn void PrivatePriorityQueue.init(&self, Allocator allocator, usz initial_capacity = 16, ) @inline
{
self.heap.new_init(initial_capacity, allocator);
}
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @inline
{
self.heap.new_init(initial_capacity, allocator);
}
fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline
{
self.heap.new_init(initial_capacity, allocator::temp()) @inline;
}
fn void PrivatePriorityQueue.push(&self, Type element)
{
self.heap.push(element);
usz i = self.heap.len() - 1;
while (i > 0)
{
usz parent = (i - 1) / 2;
Type item = self.heap[i];
Type parent_item = self.heap[parent];
$if MAX:
bool ok = greater(item, parent_item);
$else
bool ok = less(item, parent_item);
$endif
if (!ok) break;
self.heap.swap(i, parent);
i = parent;
}
}
fn void PrivatePriorityQueue.remove_at(&self, usz index)
{
if (index == 0)
{
self.pop()!!;
return;
}
self.heap.remove_at(index);
}
<*
@require self != null
*>
fn Type! PrivatePriorityQueue.pop(&self)
{
usz i = 0;
usz len = self.heap.len();
if (!len) return IteratorResult.NO_MORE_ELEMENT?;
usz new_count = len - 1;
self.heap.swap(0, new_count);
while OUTER: ((2 * i + 1) < new_count)
{
usz j = 2 * i + 1;
Type left = self.heap[j];
Type item = self.heap[i];
switch
{
case j + 1 < new_count:
Type right = self.heap[j + 1];
$if MAX:
if (!greater(right, left)) nextcase;
if (!greater(right, item)) break OUTER;
$else
if (!greater(left, right)) nextcase;
if (!greater(item, right)) break OUTER;
$endif
j++;
default:
$if MAX:
if (!greater(left, item)) break OUTER;
$else
if (!greater(item, left)) break OUTER;
$endif
}
self.heap.swap(i, j);
i = j;
}
return self.heap.pop();
}
fn Type! PrivatePriorityQueue.first(&self)
{
if (!self.len()) return IteratorResult.NO_MORE_ELEMENT?;
return self.heap.get(0);
}
fn void PrivatePriorityQueue.free(&self)
{
self.heap.free();
}
fn usz PrivatePriorityQueue.len(&self) @operator(len)
{
return self.heap.len();
}
fn bool PrivatePriorityQueue.is_empty(&self)
{
return self.heap.is_empty();
}
<*
@require index < self.len()
*>
fn Type PrivatePriorityQueue.get(&self, usz index) @operator([])
{
return self.heap[index];
}
fn usz! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic
{
return self.heap.to_format(formatter);
}
fn String PrivatePriorityQueue.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return self.heap.to_new_string(allocator);
}

View File

@@ -0,0 +1,95 @@
<*
@require Type.is_ordered : "The type must be ordered"
*>
module std::collections::range(<Type>);
import std::io;
struct Range (Printable)
{
Type start;
Type end;
}
fn usz Range.len(&self) @operator(len)
{
if (self.end < self.start) return 0;
return (usz)(self.end - self.start) + 1;
}
fn bool Range.contains(&self, Type value) @inline
{
return value >= self.start && value <= self.end;
}
<*
@require index < self.len() : "Can't index into an empty range"
*>
fn Type Range.get(&self, usz index) @operator([])
{
return (Type)(self.start + (usz)index);
}
fn String Range.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic @deprecated
{
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
}
fn String Range.to_string(&self, Allocator allocator) @dynamic
{
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
}
fn String Range.to_tstring(&self)
{
return self.to_string(allocator::temp());
}
fn usz! Range.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.printf("[%s..%s]", self.start, self.end)!;
}
struct ExclusiveRange (Printable)
{
Type start;
Type end;
}
fn usz ExclusiveRange.len(&self) @operator(len)
{
if (self.end < self.start) return 0;
return (usz)(self.end - self.start);
}
fn bool ExclusiveRange.contains(&self, Type value) @inline
{
return value >= self.start && value < self.end;
}
fn usz! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.printf("[%s..<%s]", self.start, self.end)!;
}
fn String ExclusiveRange.to_new_string(&self, Allocator allocator = null) @dynamic
{
return self.to_string(allocator ?: allocator::heap());
}
fn String ExclusiveRange.to_string(&self, Allocator allocator) @dynamic
{
return string::format("[%s..<%s]", self.start, self.end, allocator: allocator);
}
fn String ExclusiveRange.to_tstring(&self)
{
return self.to_new_string(allocator::temp());
}
<*
@require index < self.len() : "Can't index into an empty range"
*>
fn Type ExclusiveRange.get(&self, usz index) @operator([])
{
return (Type)(self.start + index);
}

View File

@@ -0,0 +1,106 @@
<*
@require values::@is_int(SIZE) &&& SIZE > 0 "The size must be positive integer"
*>
module std::collections::ringbuffer(<Type, SIZE>);
struct RingBuffer
{
Type[SIZE] buf;
usz written;
usz head;
}
fn void RingBuffer.init(&self) @inline
{
*self = {};
}
fn void RingBuffer.push(&self, Type c)
{
if (self.written < SIZE)
{
self.buf[self.written] = c;
self.written++;
}
else
{
self.buf[self.head] = c;
self.head = (self.head + 1) % SIZE;
}
}
fn Type RingBuffer.get(&self, usz index) @operator([])
{
index %= SIZE;
usz avail = SIZE - self.head;
if (index < avail)
{
return self.buf[self.head + index];
}
return self.buf[index - avail];
}
fn Type! RingBuffer.pop(&self)
{
switch
{
case self.written == 0:
return SearchResult.MISSING?;
case self.written < SIZE:
self.written--;
return self.buf[self.written];
default:
self.head = (self.head - 1) % SIZE;
return self.buf[self.head];
}
}
fn usz RingBuffer.read(&self, usz index, Type[] buffer)
{
index %= SIZE;
if (self.written < SIZE)
{
if (index >= self.written) return 0;
usz end = self.written - index;
usz n = min(end, buffer.len);
buffer[:n] = self.buf[index:n];
return n;
}
usz end = SIZE - self.head;
if (index >= end)
{
index -= end;
if (index >= self.head) return 0;
usz n = min(self.head - index, buffer.len);
buffer[:n] = self.buf[index:n];
return n;
}
if (buffer.len <= SIZE - index)
{
usz n = buffer.len;
buffer[:n] = self.buf[self.head + index:n];
return n;
}
usz n1 = SIZE - index;
buffer[:n1] = self.buf[self.head + index:n1];
buffer = buffer[n1..];
index -= n1;
usz n2 = min(self.head - index, buffer.len);
buffer[:n2] = self.buf[index:n2];
return n1 + n2;
}
fn void RingBuffer.write(&self, Type[] buffer)
{
usz i;
while (self.written < SIZE && i < buffer.len)
{
self.buf[self.written] = buffer[i++];
self.written++;
}
foreach (c : buffer[i..])
{
self.buf[self.head] = c;
self.head = (self.head + 1) % SIZE;
}
}

View File

@@ -0,0 +1,16 @@
module std::collections::tuple(<Type1, Type2>);
struct Tuple
{
Type1 first;
Type2 second;
}
module std::collections::triple(<Type1, Type2, Type3>);
struct Triple
{
Type1 first;
Type2 second;
Type3 third;
}

495
lib/std/compression/qoi.c3 Normal file
View File

@@ -0,0 +1,495 @@
module std::compression::qoi;
const uint PIXELS_MAX = 400000000;
<*
Colorspace.
Purely informative. It will be saved to the file header,
but does not affect how chunks are en-/decoded.
*>
enum QOIColorspace : char (char id)
{
SRGB = 0, // sRGB with linear alpha
LINEAR = 1 // all channels linear
}
<*
Channels.
The channels used in an image.
AUTO can be used when decoding to automatically determine
the channels from the file's header.
*>
enum QOIChannels : char (char id)
{
AUTO = 0,
RGB = 3,
RGBA = 4
}
<*
Descriptor.
Contains information about an image.
*>
struct QOIDesc
{
uint width;
uint height;
QOIChannels channels;
QOIColorspace colorspace;
}
<*
QOI Errors.
These are all the possible bad outcomes.
*>
fault QOIError
{
INVALID_PARAMETERS,
FILE_OPEN_FAILED,
FILE_WRITE_FAILED,
INVALID_DATA,
TOO_MANY_PIXELS
}
// Let the user decide if they want to use std::io
module std::compression::qoi @if(!$feature(QOI_NO_STDIO));
import std::io;
<*
Encode raw RGB or RGBA pixels into a QOI image and write it to the
file system.
The desc struct must be filled with the image width, height, the
used channels (QOIChannels.RGB or RGBA) and the colorspace
(QOIColorspace.SRGB or LINEAR).
The function returns an optional, which can either be a QOIError
or the number of bytes written on success.
@param [in] filename `The file's name to write the image to`
@param [in] input `The raw RGB or RGBA pixels to encode`
@param [&in] desc `The descriptor of the image`
*>
fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool()
{
// encode data
char[] output = new_encode(input, desc, allocator: allocator::temp())!;
// open file
File! f = file::open(filename, "wb");
if (catch f) return QOIError.FILE_OPEN_FAILED?;
// write data to file and close it
usz! written = f.write(output);
if (catch written) return QOIError.FILE_WRITE_FAILED?;
if (catch f.close()) return QOIError.FILE_WRITE_FAILED?;
return written;
}
<*
Read and decode a QOI image from the file system.
If channels is set to QOIChannels.AUTO, the function will
automatically determine the channels from the file's header.
However, if channels is RGB or RGBA, the output format will be
forced into this number of channels.
The desc struct will be filled with the width, height,
channels and colorspace of the image.
The function returns an optional, which can either be a QOIError
or a char[] pointing to the decoded pixels on success.
The returned pixel data should be free()d after use, or the decoding
and use of the data should be wrapped in a @pool() { ... }; block.
@param [in] filename `The file's name to read the image from`
@param [&out] desc `The descriptor to fill with the image's info`
@param channels `The channels to be used`
*>
fn char[]! new_read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) => @pool(allocator)
{
// read file
char[] data = file::load_temp(filename) ?? QOIError.FILE_OPEN_FAILED?!;
// pass data to decode function
return new_decode(data, desc, channels, allocator);
}
fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @deprecated("Use new_read")
{
return new_read(filename, desc, channels, allocator);
}
// Back to basic non-stdio mode
module std::compression::qoi;
import std::bits;
fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @deprecated("use encode_new")
{
return new_encode(input, desc, allocator);
}
<*
Encode raw RGB or RGBA pixels into a QOI image in memory.
The function returns an optional, which can either be a QOIError
or a char[] pointing to the encoded data on success.
The returned qoi data should be free()d after use, or the encoding
and use of the data should be wrapped in a @pool() { ... }; block.
See the write() function for an example.
@param [in] input `The raw RGB or RGBA pixels to encode`
@param [&in] desc `The descriptor of the image`
*>
fn char[]! new_encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @nodiscard
{
// check info in desc
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?;
if (desc.channels == AUTO) return QOIError.INVALID_PARAMETERS?;
uint pixels = desc.width * desc.height;
if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?;
// check input data size
uint image_size = pixels * desc.channels.id;
if (image_size != input.len) return QOIError.INVALID_DATA?;
// allocate memory for encoded data (output)
// header + chunk tag and RGB(A) data for each pixel + end of stream
uint max_size = Header.sizeof + pixels + image_size + END_OF_STREAM.len;
char[] output = allocator::alloc_array(allocator, char, max_size); // no need to init
defer catch allocator::free(allocator, output);
// write header
*(Header*)output.ptr = {
.be_magic = bswap('qoif'),
.be_width = bswap(desc.width),
.be_height = bswap(desc.height),
.channels = desc.channels.id,
.colorspace = desc.colorspace.id
};
uint pos = Header.sizeof; // Current position in output
uint loc; // Current position in image (top-left corner)
uint loc_end = image_size - desc.channels.id; // End of image data
char run_length = 0; // Length of the current run
Pixel[64] palette; // Zero-initialized by default
Pixel prev = { 0, 0, 0, 255 };
Pixel p = { 0, 0, 0, 255 };
ichar[<3>] diff; // pre-allocate for diff
ichar[<3>] luma; // ...and luma
// write chunks
for (loc = 0; loc < image_size; loc += desc.channels.id)
{
// set previous pixel
prev = p;
// get current pixel
p[:3] = input[loc:3]; // cutesy slices :3
if (desc.channels == RGBA) p.a = input[loc + 3];
// check if we can run the previous pixel
if (prev == p)
{
run_length++;
if (run_length == 62 || loc == loc_end)
{
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
run_length = 0;
}
continue;
}
// end last run if there was one
if (run_length > 0)
{
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
run_length = 0;
}
switch
{
// check if we can index the palette
case (palette[p.hash()] == p):
*@extract(OpIndex, output, &pos) = {
OP_INDEX,
p.hash()
};
// check if we can use diff or luma
case (prev != p && prev.a == p.a):
// diff the pixels
diff = p.rgb - prev.rgb;
if (diff.r > -3 && diff.r < 2
&& diff.g > -3 && diff.g < 2
&& diff.b > -3 && diff.b < 2)
{
*@extract(OpDiff, output, &pos) = {
OP_DIFF,
(char)diff.r + 2,
(char)diff.g + 2,
(char)diff.b + 2
};
palette[p.hash()] = p;
break;
}
// check luma eligibility
luma = { diff.r - diff.g, diff.g, diff.b - diff.g };
if (luma.r >= -8 && luma.r <= 7
&& luma.g >= -32 && luma.g <= 31
&& luma.b >= -8 && luma.b <= 7)
{
*@extract(OpLuma, output, &pos) = {
OP_LUMA,
(char)luma.g + 32,
(char)luma.r + 8,
(char)luma.b + 8
};
palette[p.hash()] = p;
break;
}
nextcase;
// worst case scenario: just encode the raw pixel
default:
if (prev.a != p.a)
{
*@extract(OpRGBA, output, &pos) = { OP_RGBA, p.r, p.g, p.b, p.a };
}
else
{
*@extract(OpRGB, output, &pos) = { OP_RGB, p.r, p.g, p.b };
}
palette[p.hash()] = p;
}
}
// write end of stream
output[pos:END_OF_STREAM.len] = END_OF_STREAM;
pos += END_OF_STREAM.len;
return output[:pos];
}
fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
{
return new_decode(data, desc, channels, allocator);
}
<*
Decode a QOI image from memory.
If channels is set to QOIChannels.AUTO, the function will
automatically determine the channels from the file's header.
However, if channels is RGB or RGBA, the output format will be
forced into this number of channels.
The desc struct will be filled with the width, height,
channels and colorspace of the image.
The function returns an optional, which can either be a QOIError
or a char[] pointing to the decoded pixels on success.
The returned pixel data should be free()d after use, or the decoding
and use of the data should be wrapped in a @pool() { ... }; block.
@param [in] data `The QOI image data to decode`
@param [&out] desc `The descriptor to fill with the image's info`
@param channels `The channels to be used`
*>
fn char[]! new_decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @nodiscard
{
// check input data
if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?;
// get header
Header* header = (Header*)data.ptr;
// check magic bytes (FourCC)
if (bswap(header.be_magic) != 'qoif') return QOIError.INVALID_DATA?;
// copy header data to desc
desc.width = bswap(header.be_width);
desc.height = bswap(header.be_height);
desc.channels = @enumcast(QOIChannels, header.channels)!; // Rethrow if invalid
desc.colorspace = @enumcast(QOIColorspace, header.colorspace)!; // Rethrow if invalid
if (desc.channels == AUTO) return QOIError.INVALID_DATA?; // Channels must be specified in the header
// check width and height
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_DATA?;
// check pixel count
ulong pixels = (ulong)desc.width * (ulong)desc.height;
if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?;
uint pos = Header.sizeof; // Current position in data
uint loc; // Current position in image (top-left corner)
char run_length = 0; // Length of the current run
char tag; // Current chunk tag
Pixel[64] palette; // Zero-initialized by default
Pixel p = { 0, 0, 0, 255 };
if (channels == AUTO) channels = desc.channels;
// allocate memory for image data
usz image_size = (usz)pixels * channels.id;
char[] image = allocator::alloc_array(allocator, char, image_size);
defer catch allocator::free(allocator, image);
for (loc = 0; loc < image_size; loc += channels.id)
{
// get chunk tag
tag = data[pos];
// check for chunk type
switch
{
case run_length > 0:
run_length--;
case tag == OP_RGB:
OpRGB* op = @extract(OpRGB, data, &pos);
p = { op.red, op.green, op.blue, p.a };
palette[p.hash()] = p;
case tag == OP_RGBA:
OpRGBA* op = @extract(OpRGBA, data, &pos);
p = { op.red, op.green, op.blue, op.alpha };
palette[p.hash()] = p;
case tag >> 6 == OP_INDEX:
OpIndex* op = @extract(OpIndex, data, &pos);
p = palette[op.index];
case tag >> 6 == OP_DIFF:
OpDiff* op = @extract(OpDiff, data, &pos);
p.r += op.diff_red - 2;
p.g += op.diff_green - 2;
p.b += op.diff_blue - 2;
palette[p.hash()] = p;
case tag >> 6 == OP_LUMA:
OpLuma* op = @extract(OpLuma, data, &pos);
int diff_green = op.diff_green - 32;
p.r += (char)(op.diff_red_minus_green - 8 + diff_green);
p.g += (char)(diff_green);
p.b += (char)(op.diff_blue_minus_green - 8 + diff_green);
palette[p.hash()] = p;
case tag >> 6 == OP_RUN:
OpRun* op = @extract(OpRun, data, &pos);
run_length = op.run;
}
// draw the pixel
if (channels == RGBA) { image[loc:4] = p.rgba; } else { image[loc:3] = p.rgb; }
}
return image;
}
// ***************************************************************************
// *** ***
// *** Main functions are at the top to make the file more readable. ***
// *** From here on, helper functions and types are defined. ***
// *** ***
// ***************************************************************************
module std::compression::qoi @private;
// 8-bit opcodes
const OP_RGB = 0b11111110;
const OP_RGBA = 0b11111111;
// 2-bit opcodes
const OP_INDEX = 0b00;
const OP_DIFF = 0b01;
const OP_LUMA = 0b10;
const OP_RUN = 0b11;
struct Header @packed
{
uint be_magic; // magic bytes "qoif"
uint be_width; // image width in pixels (BE)
uint be_height; // image height in pixels (BE)
// informative fields
char channels; // 3 = RGB, 4 = RGB
char colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
}
const char[?] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1};
// inefficient, but it's only run once at a time
macro @enumcast($Type, raw)
{
foreach (value : $Type.values)
{
if (value.id == raw) return value;
}
return QOIError.INVALID_DATA?;
}
distinct Pixel = inline char[<4>];
macro char Pixel.hash(Pixel p)
{
return (p.r * 3 + p.g * 5 + p.b * 7 + p.a * 11) % 64;
}
struct OpRGB // No need to use @packed here, the alignment is 1 anyways.
{
char tag;
char red;
char green;
char blue;
}
struct OpRGBA @packed
{
char tag;
char red;
char green;
char blue;
char alpha;
}
bitstruct OpIndex : char
{
char tag : 6..7;
char index : 0..5;
}
bitstruct OpDiff : char
{
char tag : 6..7;
char diff_red : 4..5;
char diff_green : 2..3;
char diff_blue : 0..1;
}
bitstruct OpLuma : ushort @align(1)
{
char tag : 6..7;
char diff_green : 0..5;
char diff_red_minus_green : 12..15;
char diff_blue_minus_green : 8..11;
}
bitstruct OpRun : char
{
char tag : 6..7;
char run : 0..5;
}
// Macro used to locate chunks in data buffers.
// Can be used both for reading and writing.
macro @extract($Type, char[] data, uint* pos)
{
// slice data, then double cast
$Type* chunk = ($Type*)data[*pos : $Type.sizeof].ptr;
*pos += $Type.sizeof;
return chunk;
}

View File

@@ -0,0 +1,112 @@
// 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;
import std::math;
struct ArenaAllocator (Allocator)
{
char[] data;
usz used;
}
<*
Initialize a memory arena for use using the provided bytes.
*>
fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
{
self.data = data;
self.used = 0;
return self;
}
fn void ArenaAllocator.clear(&self)
{
self.used = 0;
}
struct ArenaAllocatorHeader @local
{
usz size;
char[?] data;
}
macro ArenaAllocator* wrap(char[] bytes)
{
return (ArenaAllocator){}.init(bytes);
}
<*
@require ptr != null
*>
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator.");
ArenaAllocatorHeader* header = ptr - ArenaAllocatorHeader.sizeof;
// Reclaim memory if it's the last element.
if (ptr + header.size == &self.data[self.used])
{
self.used -= header.size + ArenaAllocatorHeader.sizeof;
}
}
fn usz ArenaAllocator.mark(&self) @dynamic => self.used;
fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
<*
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require size > 0
*>
fn void*! ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
usz total_len = self.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
void* start_mem = self.data.ptr;
void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(mem - self.data.ptr) + size;
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
self.used = end;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size;
if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
<*
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require old_pointer != null
@require size > 0
*>
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
usz total_len = self.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 (&self.data[self.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment))
{
if (old_size >= size)
{
self.used -= old_size - size;
}
else
{
usz new_used = self.used + size - old_size;
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY?;
self.used = new_used;
}
header.size = size;
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -0,0 +1,209 @@
// Copyright (c) 2021-2024 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::math;
struct DynamicArenaAllocator (Allocator)
{
Allocator backing_allocator;
DynamicArenaPage* page;
DynamicArenaPage* unused_page;
usz page_size;
}
<*
@param [&inout] allocator
@require page_size >= 128
*>
fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator allocator)
{
self.page = null;
self.unused_page = null;
self.page_size = page_size;
self.backing_allocator = allocator;
}
fn void DynamicArenaAllocator.free(&self)
{
DynamicArenaPage* page = self.page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
allocator::free(self.backing_allocator, page.memory);
allocator::free(self.backing_allocator, page);
page = next_page;
}
page = self.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
allocator::free(self.backing_allocator, page.memory);
allocator::free(self.backing_allocator, page);
page = next_page;
}
self.page = null;
self.unused_page = null;
}
struct DynamicArenaPage @local
{
void* memory;
void* prev_arena;
usz total;
usz used;
void* current_stack_ptr;
}
struct DynamicArenaChunk @local
{
usz size;
}
<*
@require ptr != null
@require self.page != null `tried to free pointer on invalid allocator`
*>
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
DynamicArenaPage* current_page = self.page;
if (ptr == current_page.current_stack_ptr)
{
current_page.used = (usz)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory);
}
current_page.current_stack_ptr = null;
}
<*
@require size > 0 `Resize doesn't support zeroing`
@require old_pointer != null `Resize doesn't handle null pointers`
@require self.page != null `tried to realloc pointer on invalid allocator`
*>
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
DynamicArenaPage* current_page = self.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.current_stack_ptr == old_pointer)
{
current_page.used = (usz)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory);
}
return old_pointer;
}
if REUSE: (current_page.current_stack_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 = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
{
assert(mark == 0, "Unexpectedly reset dynamic arena allocator with mark %d", mark);
DynamicArenaPage* page = self.page;
DynamicArenaPage** unused_page_ptr = &self.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;
}
self.page = page;
}
<*
@require math::is_power_of_2(alignment)
@require size > 0
*>
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
assert(page_size > size + DynamicArenaChunk.sizeof);
// Grab the page without alignment (we do it ourselves)
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
DynamicArenaPage*! page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
if (catch err = page)
{
allocator::free(self.backing_allocator, mem);
return err?;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);
assert(mem_start + size < mem + page_size);
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
chunk.size = size;
page.prev_arena = self.page;
page.total = page_size;
page.used = mem_start + size - page.memory;
self.page = page;
page.current_stack_ptr = mem_start;
return mem_start;
}
<*
@require size > 0 `acquire expects size > 0`
@require !alignment || math::is_power_of_2(alignment)
*>
fn void*! DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = self.page;
void* ptr @noinit;
do SET_DONE:
{
if (!page && self.unused_page)
{
self.page = page = self.unused_page;
self.unused_page = page.prev_arena;
page.prev_arena = null;
}
if (!page)
{
ptr = self._alloc_new(size, alignment)!;
break SET_DONE;
}
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
usz new_used = start - page.memory + size;
if ALLOCATE_NEW: (new_used > page.total)
{
if ((page = self.unused_page))
{
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
new_used = start + size - page.memory;
if (page.total >= new_used)
{
self.unused_page = page.prev_arena;
page.prev_arena = self.page;
self.page = page;
break ALLOCATE_NEW;
}
}
ptr = self._alloc_new(size, alignment)!;
break SET_DONE;
}
page.used = new_used;
assert(start + size == page.memory + page.used);
ptr = start;
DynamicArenaChunk* chunk = (DynamicArenaChunk*)ptr - 1;
chunk.size = size;
};
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
return ptr;
}

View File

@@ -0,0 +1,209 @@
// Copyright (c) 2021-2024 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::math;
struct SimpleHeapAllocator (Allocator)
{
MemoryAllocFn alloc_fn;
Header* free_list;
}
<*
@require allocator != null "An underlying memory provider must be given"
@require !self.free_list "The allocator may not be already initialized"
*>
fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
{
self.alloc_fn = allocator;
self.free_list = null;
}
fn void*! SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
{
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
}
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
}
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
return alignment > 0
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
: self._realloc(old_pointer, size);
}
fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
if (aligned)
{
@aligned_free(self._free, old_pointer)!!;
}
else
{
self._free(old_pointer);
}
}
<*
@require old_pointer && bytes > 0
*>
fn void*! SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @local
{
// Find the block header.
Header* block = (Header*)old_pointer - 1;
if (block.size >= bytes) return old_pointer;
void* new = self._alloc(bytes)!;
usz max_to_copy = math::min(block.size, bytes);
mem::copy(new, old_pointer, max_to_copy);
self._free(old_pointer);
return new;
}
fn void*! SimpleHeapAllocator._calloc(&self, usz bytes) @local
{
void* data = self._alloc(bytes)!;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
fn void*! SimpleHeapAllocator._alloc(&self, usz bytes) @local
{
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
if (!self.free_list)
{
self.add_block(aligned_bytes)!;
}
Header* current = self.free_list;
Header* previous = current;
while (current)
{
switch
{
case current.size >= aligned_bytes && current.size <= aligned_bytes + Header.sizeof + 64:
if (current == previous)
{
self.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 - Header.sizeof;
unallocated.next = current.next;
if (current == self.free_list)
{
self.free_list = unallocated;
}
else
{
previous.next = unallocated;
}
current.size = aligned_bytes;
current.next = null;
return current + 1;
default:
previous = current;
current = current.next;
}
}
self.add_block(aligned_bytes)!;
return self._alloc(aligned_bytes);
}
fn void! SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local
{
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
char[] result = self.alloc_fn(aligned_bytes + Header.sizeof)!;
Header* new_block = (Header*)result.ptr;
new_block.size = result.len - Header.sizeof;
new_block.next = null;
self._free(new_block + 1);
}
fn void SimpleHeapAllocator._free(&self, 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 self.
if (!self.free_list)
{
self.free_list = block;
return;
}
// Find where in the list it should be inserted.
Header* current = self.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
self.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 @local
{
struct
{
Header* next;
usz size;
}
usz align;
}

View File

@@ -0,0 +1,161 @@
// Copyright (c) 2021-2024 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 @if(env::LIBC);
import std::io;
import libc;
const LibcAllocator LIBC_ALLOCATOR = {};
distinct LibcAllocator (Allocator, Printable) = uptr;
fn String LibcAllocator.to_string(&self, Allocator allocator) @dynamic => "Libc allocator".copy(allocator);
fn usz! LibcAllocator.to_format(&self, Formatter *format) @dynamic => format.print("Libc allocator");
module std::core::mem::allocator @if(env::POSIX);
import std::os;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
{
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
else
{
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
}
else
{
if (!(data = libc::malloc(bytes))) return AllocationFailure.OUT_OF_MEMORY?;
}
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
return data;
}
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
void* new_ptr;
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return AllocationFailure.OUT_OF_MEMORY?;
$switch
$case env::DARWIN:
usz old_usable_size = darwin::malloc_size(old_ptr);
$case env::LINUX:
usz old_usable_size = linux::malloc_usable_size(old_ptr);
$default:
usz old_usable_size = new_bytes;
$endswitch
usz copy_size = new_bytes < old_usable_size ? new_bytes : old_usable_size;
mem::copy(new_ptr, old_ptr, copy_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
libc::free(old_ptr);
return new_ptr;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
libc::free(old_ptr);
}
module std::core::mem::allocator @if(env::WIN32);
import std::os::win32;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
{
if (alignment > 0)
{
return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
void* data = alignment > 0 ? win32::_aligned_malloc(bytes, alignment) : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
return data;
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (alignment)
{
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
if (aligned)
{
win32::_aligned_free(old_ptr);
return;
}
libc::free(old_ptr);
}
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX && env::LIBC);
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
{
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
return data ?: AllocationFailure.OUT_OF_MEMORY?;
}
else
{
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment)!! : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
return data;
}
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (alignment)
{
void* data = @aligned_realloc(fn void*(usz bytes) => libc::malloc(bytes), libc::free, old_ptr, new_bytes, alignment)!!;
return data ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
if (aligned)
{
@aligned_free(libc::free, old_ptr)!!;
}
else
{
libc::free(old_ptr);
}
}

View File

@@ -0,0 +1,150 @@
module std::core::mem::allocator;
struct OnStackAllocator (Allocator)
{
Allocator backing_allocator;
char[] data;
usz used;
OnStackAllocatorExtraChunk* chunk;
}
struct OnStackAllocatorExtraChunk @local
{
bool is_aligned;
OnStackAllocatorExtraChunk* prev;
void* data;
}
<*
Initialize a memory arena for use using the provided bytes.
@param [&inout] allocator
*>
fn void OnStackAllocator.init(&self, char[] data, Allocator allocator)
{
self.data = data;
self.backing_allocator = allocator;
self.used = 0;
}
fn void OnStackAllocator.free(&self)
{
OnStackAllocatorExtraChunk* chunk = self.chunk;
while (chunk)
{
if (chunk.is_aligned)
{
allocator::free_aligned(self.backing_allocator, chunk.data);
}
else
{
allocator::free(self.backing_allocator, chunk.data);
}
void* old = chunk;
chunk = chunk.prev;
allocator::free(self.backing_allocator, old);
}
self.chunk = null;
self.used = 0;
}
struct OnStackAllocatorHeader
{
usz size;
char[?] data;
}
<*
@require old_pointer != null
*>
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
if (allocation_in_stack_mem(self, old_pointer)) return;
on_stack_allocator_remove_chunk(self, old_pointer);
self.backing_allocator.release(old_pointer, aligned);
}
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;
allocator::free(a.backing_allocator, 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 old_pointer != null
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
*>
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
if (!allocation_in_stack_mem(self, old_pointer))
{
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
<*
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require size > 0
*>
fn void*! OnStackAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
bool aligned = alignment > 0;
alignment = alignment_for_allocation(alignment);
usz total_len = self.data.len;
void* start_mem = self.data.ptr;
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof ;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(mem - self.data.ptr) + size;
Allocator backing_allocator = self.backing_allocator;
if (end > total_len)
{
OnStackAllocatorExtraChunk* chunk = allocator::alloc_try(backing_allocator, OnStackAllocatorExtraChunk)!;
defer catch allocator::free(backing_allocator, chunk);
defer try self.chunk = chunk;
*chunk = { .prev = self.chunk, .is_aligned = aligned };
return chunk.data = backing_allocator.acquire(size, init_type, aligned ? alignment : 0)!;
}
self.used = end;
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
header.size = size;
return mem;
}

View File

@@ -0,0 +1,230 @@
module std::core::mem::allocator;
import std::io, std::math;
struct TempAllocatorChunk @local
{
usz size;
char[?] data;
}
struct TempAllocator (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(&self) => self.size & ~PAGE_IS_ALIGNED;
macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED;
<*
@require size >= 16
*>
fn TempAllocator*! new_temp_allocator(usz size, Allocator allocator)
{
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
temp.last_page = null;
temp.backing_allocator = allocator;
temp.used = 0;
temp.capacity = size;
return temp;
}
fn void TempAllocator.destroy(&self)
{
self.reset(0);
if (self.last_page) (void)self._free_page(self.last_page);
allocator::free(self.backing_allocator, self);
}
fn usz TempAllocator.mark(&self) @dynamic => self.used;
fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
{
usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX);
if (old_pointer + old_size == &self.data[self.used])
{
self.used -= old_size;
asan::poison_memory_region(&self.data[self.used], old_size);
}
}
fn void TempAllocator.reset(&self, usz mark) @dynamic
{
TempAllocatorPage *last_page = self.last_page;
while (last_page && last_page.mark > mark)
{
self.used = last_page.mark;
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
self._free_page(to_free)!!;
}
self.last_page = last_page;
$if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER:
if (!last_page)
{
usz cleaned = self.used - mark;
if (cleaned > 0)
{
$if env::COMPILER_SAFE_MODE && !env::ADDRESS_SANITIZER:
self.data[mark : cleaned] = 0xAA;
$endif
asan::poison_memory_region(&self.data[mark], cleaned);
}
}
$endif
self.used = mark;
}
fn void! TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @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 = &self.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 = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
self.backing_allocator.release(real_pointer, page.is_aligned());
return data;
}
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
{
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1)
{
assert(self.last_page, "Realloc of non temp pointer");
// First grab the page
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
return self._realloc_page(page, size, alignment);
}
TempAllocatorChunk* data = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
<*
@require size > 0
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
*>
fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
void* start_mem = &self.data;
void* starting_ptr = start_mem + self.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, alignment);
}
usz new_usage = (usz)(mem - start_mem) + size;
// Arena allocation, simple!
if (new_usage <= self.capacity)
{
asan::unpoison_memory_region(starting_ptr, new_usage - self.used);
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
chunk_start.size = size;
self.used = new_usage;
if (init_type == ZERO) 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)
{
// This is actually simpler, since it will create the offset for us.
usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment);
if (init_type == ZERO)
{
mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
}
else
{
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
}
void* start = mem;
mem += mem::aligned_offset(TempAllocatorPage.sizeof, alignment);
page = (TempAllocatorPage*)mem - 1;
page.start = start;
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 = self.backing_allocator.acquire(total_alloc_size, init_type, 0)!;
// 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 = ++self.used;
// Hook up the page.
page.prev_page = self.last_page;
self.last_page = page;
return &page.data[0];
}
fn void! TempAllocator.print_pages(&self, File* f)
{
TempAllocatorPage *last_page = self.last_page;
if (!last_page)
{
io::fprintf(f, "No pages.\n")!;
return;
}
io::fprintf(f, "---Pages----\n")!;
uint index = 0;
while (last_page)
{
bool is_not_aligned = !(last_page.size & (1u64 << 63));
io::fprintf(f, "%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,215 @@
// Copyright (c) 2021-2024 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, std::io, std::os::backtrace;
const MAX_BACKTRACE = 16;
struct Allocation
{
void* ptr;
usz size;
void*[MAX_BACKTRACE] backtrace;
}
def AllocMap = HashMap(<uptr, Allocation>);
// A simple tracking allocator.
// It tracks allocations using a hash map but
// is not compatible with allocators that uses mark()
struct TrackingAllocator (Allocator)
{
Allocator inner_allocator;
AllocMap map;
usz mem_total;
usz allocs_total;
}
<*
Initialize a tracking allocator to wrap (and track) another allocator.
@param [&inout] allocator "The allocator to track"
*>
fn void TrackingAllocator.init(&self, Allocator allocator)
{
*self = { .inner_allocator = allocator };
self.map.init(allocator);
}
<*
Free this tracking allocator.
*>
fn void TrackingAllocator.free(&self)
{
self.map.free();
*self = {};
}
<*
@return "the total allocated memory not yet freed."
*>
fn usz TrackingAllocator.allocated(&self) => @pool()
{
usz allocated = 0;
foreach (&allocation : self.map.value_tlist()) allocated += allocation.size;
return allocated;
}
<*
@return "the total memory allocated (freed or not)."
*>
fn usz TrackingAllocator.total_allocated(&self) => self.mem_total;
<*
@return "the total number of allocations (freed or not)."
*>
fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total;
fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator)
{
return self.map.value_tlist();
}
<*
@return "the number of non-freed allocations."
*>
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
fn void*! TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
void* data = self.inner_allocator.acquire(size, init_type, alignment)!;
self.allocs_total++;
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
return data;
}
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
void* data = self.inner_allocator.resize(old_pointer, size, alignment)!;
self.map.remove((uptr)old_pointer);
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
return data;
}
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
{
if (catch self.map.remove((uptr)old_pointer))
{
unreachable("Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
}
self.inner_allocator.release(old_pointer, is_aligned);
}
fn void TrackingAllocator.clear(&self)
{
self.map.clear();
}
fn bool TrackingAllocator.has_leaks(&self)
{
return self.map.len() > 0;
}
fn void TrackingAllocator.print_report(&self) => self.fprint_report(io::stdout())!!;
fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
{
usz total = 0;
usz entries = 0;
bool leaks = false;
Allocation[] allocs = self.map.value_tlist();
if (allocs.len)
{
if (!allocs[0].backtrace[0])
{
io::fprintn(out, "======== Memory Report ========")!;
io::fprintn(out, "Size in bytes Address")!;
foreach (i, &allocation : allocs)
{
entries++;
total += allocation.size;
io::fprintfn(out, "%13s %p", allocation.size, allocation.ptr)!;
}
io::fprintn(out, "===============================")!;
}
else
{
io::fprintn(out, "================================== Memory Report ==================================")!;
io::fprintn(out, "Size in bytes Address Function ")!;
foreach (i, &allocation : allocs)
{
entries++;
total += allocation.size;
BacktraceList backtraces = {};
Backtrace trace = backtrace::BACKTRACE_UNKNOWN;
if (allocation.backtrace[3])
{
trace = backtrace::symbolize_backtrace(allocation.backtrace[3:1], allocator::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
}
if (trace.function.len) leaks = true;
io::fprintfn(out, "%13s %p %s:%d", allocation.size,
allocation.ptr, trace.function.len ? trace.function : "???",
trace.line ? trace.line : 0)!;
}
io::fprintn(out, "===================================================================================")!;
}
}
else
{
io::fprintn(out, "* NO ALLOCATIONS FOUND *")!;
}
io::fprintfn(out, "- Total currently allocated memory: %d", total)!;
io::fprintfn(out, "- Total current allocations: %d", entries)!;
io::fprintfn(out, "- Total allocations (freed and retained): %d", self.allocs_total)!;
io::fprintfn(out, "- Total allocated memory (freed and retained): %d", self.mem_total)!;
if (leaks)
{
io::fprintn(out)!;
io::fprintn(out, "Full leak report:")!;
foreach (i, &allocation : allocs)
{
if (!allocation.backtrace[3])
{
io::fprintfn(out, "Allocation %d (%d bytes) - no backtrace available.", i + 1, allocation.size)!;
continue;
}
BacktraceList backtraces = {};
usz end = MAX_BACKTRACE;
foreach (j, val : allocation.backtrace)
{
if (!val)
{
end = j;
break;
}
}
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], allocator::temp())!;
io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
foreach (trace : list)
{
if (trace.has_file())
{
io::fprintfn(out, " %s (in %s:%d)", trace.function, trace.file, trace.line);
continue;
}
if (trace.is_unknown())
{
io::fprintfn(out, " ??? (in unknown)");
continue;
}
io::fprintfn(out, " %s (source unavailable)", trace.function);
}
}
}
}

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

@@ -0,0 +1,193 @@
module std::core::array;
import std::core::array::slice;
<*
@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?;
}
<*
@require @typekind(array_ptr) == POINTER
@require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY
@require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY
*>
macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
{
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
var $ElementType = $typeof((*array_ptr)[0][0]);
return Slice2d(<$ElementType>) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
}
<*
@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 slices, returning a slice containing the concatenation of them.
@param [in] arr1
@param [in] arr2
@param [&inout] allocator "The allocator to use, default is the heap allocator"
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
@require @typekind(arr2) == SLICE || @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 allocator) @nodiscard
{
var $Type = $typeof(arr1[0]);
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
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 slices, returning a slice containing the concatenation of them.
@param [in] arr1
@param [in] arr2
@param [&inout] allocator "The allocator to use, default is the heap allocator"
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@ensure return.len == arr1.len + arr2.len
*>
macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
{
return concat(arr1, arr2, allocator);
}
<*
Concatenate two arrays or slices, returning a slice containing the concatenation of them,
allocated using the temp allocator.
@param [in] arr1
@param [in] arr2
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@ensure return.len == arr1.len + arr2.len
*>
macro tconcat(arr1, arr2) @nodiscard => concat(arr1, arr2, allocator::temp());
module std::core::array::slice(<Type>);
struct Slice2d
{
Type* ptr;
usz inner_len;
usz ystart;
usz ylen;
usz xstart;
usz xlen;
}
fn usz Slice2d.len(&self) @operator(len)
{
return self.ylen;
}
fn usz Slice2d.count(&self)
{
return self.ylen * self.xlen;
}
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
{
foreach (y, line : *self)
{
foreach (x, val : line)
{
@body({ x, y }, val);
}
}
}
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
{
foreach (y, line : *self)
{
foreach (x, &val : line)
{
@body({ x, y }, val);
}
}
}
<*
@require idy >= 0 && idy < self.ylen
*>
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
{
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
}
macro Type Slice2d.get_coord(self, usz[<2>] coord)
{
return *self.get_coord_ref(coord);
}
macro Type Slice2d.get_xy(self, x, y)
{
return *self.get_xy_ref(x, y);
}
macro Type* Slice2d.get_xy_ref(self, x, y)
{
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
}
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
{
return self.get_xy_ref(coord.x, coord.y);
}
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
{
*self.get_coord_ref(coord) = value;
}
macro void Slice2d.set_xy(self, x, y, Type value)
{
*self.get_xy_ref(x, y) = value;
}
<*
@require y >= 0 && y < self.ylen
@require x >= 0 && x < self.xlen
*>
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
{
if (xlen < 1) xlen = self.xlen + xlen;
if (ylen < 1) ylen = self.ylen + ylen;
return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
}

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

@@ -0,0 +1,180 @@
// 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;
}
<*
@require is_array_or_slice_of_char(bytes) "argument must be an array, a pointer to an array or a slice of char"
@require is_bitorder($Type) "type must be a bitorder integer"
*>
macro read(bytes, $Type)
{
char[] s;
$switch (@typekind(bytes))
$case POINTER:
s = (*bytes)[:$Type.sizeof];
$default:
s = bytes[:$Type.sizeof];
$endswitch
return bitcast(*(char[$Type.sizeof]*)s.ptr, $Type).val;
}
<*
@require is_arrayptr_or_slice_of_char(bytes) "argument must be a pointer to an array or a slice of char"
@require is_bitorder($Type) "type must be a bitorder integer"
*>
macro write(x, bytes, $Type)
{
char[] s;
$switch (@typekind(bytes))
$case POINTER:
s = (*bytes)[:$Type.sizeof];
$default:
s = bytes[:$Type.sizeof];
$endswitch
*($typeof(x)*)s.ptr = bitcast(x, $Type).val;
}
macro is_bitorder($Type)
{
$switch ($Type)
$case UShortLE:
$case ShortLE:
$case UIntLE:
$case IntLE:
$case ULongLE:
$case LongLE:
$case UInt128LE:
$case Int128LE:
$case UShortBE:
$case ShortBE:
$case UIntBE:
$case IntBE:
$case ULongBE:
$case LongBE:
$case UInt128BE:
$case Int128BE:
return true;
$default:
return false;
$endswitch
}
macro bool is_array_or_slice_of_char(bytes)
{
$switch (@typekind(bytes))
$case POINTER:
var $Inner = $typefrom($typeof(bytes).inner);
$if $Inner.kindof == ARRAY:
var $Inner2 = $typefrom($Inner.inner);
return $Inner2.typeid == char.typeid;
$endif
$case ARRAY:
$case SLICE:
var $Inner = $typefrom($typeof(bytes).inner);
return $Inner.typeid == char.typeid;
$default:
return false;
$endswitch
}
macro bool is_arrayptr_or_slice_of_char(bytes)
{
$switch (@typekind(bytes))
$case POINTER:
var $Inner = $typefrom($typeof(bytes).inner);
$if $Inner.kindof == ARRAY:
var $Inner2 = $typefrom($Inner.inner);
return $Inner2.typeid == char.typeid;
$endif
$case SLICE:
var $Inner = $typefrom($typeof(bytes).inner);
return $Inner.typeid == char.typeid;
$default:
return false;
$endswitch
}

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

@@ -0,0 +1,758 @@
// Copyright (c) 2021-2024 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, std::hash, std::io, std::os::backtrace;
/*
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 }
def VoidFn = fn void();
<*
Stores a variable on the stack, then restores it at the end of the
macro scope.
@param #variable `the variable to store and restore`
@require values::@is_lvalue(#variable)
*>
macro void @scope(#variable; @body) @builtin
{
var temp = #variable;
defer #variable = temp;
@body();
}
<*
Swap two variables
@require $defined(#a = #b, #b = #a) `The values must be mutually assignable`
*>
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;
}
fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIVE_STACKTRACE) => @pool()
{
void*[256] buffer;
void*[] backtraces = backtrace::capture_current(&buffer);
backtraces_to_ignore++;
BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, allocator::temp());
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '");
io::eprint(message);
io::eprintn("'");
foreach (i, &trace : backtrace)
{
if (i < backtraces_to_ignore) continue;
String inline_suffix = trace.is_inline ? " [inline]" : "";
if (trace.is_unknown())
{
io::eprintfn(" in ???%s", inline_suffix);
continue;
}
if (trace.has_file())
{
io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix);
continue;
}
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
}
return true;
}
fn void default_panic(String message, String file, String function, uint line) @if(env::NATIVE_STACKTRACE)
{
$if $defined(io::stderr):
if (!print_backtrace(message, 2))
{
io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
}
$endif
$$trap();
}
macro void abort(String string = "Unrecoverable error reached", ...) @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
$$trap();
}
bool in_panic @local = false;
fn void default_panic(String message, String file, String function, uint line) @if(!env::NATIVE_STACKTRACE)
{
if (in_panic)
{
io::eprintn("Panic inside of panic.");
return;
}
in_panic = true;
$if $defined(io::stderr):
io::eprint("\nERROR: '");
io::eprint(message);
io::eprintfn("', in %s (%s:%d)", function, file, line);
$endif
in_panic = false;
$$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...)
{
if (in_panic)
{
io::eprint("Panic inside of panic: ");
io::eprintn(fmt);
return;
}
in_panic = true;
@stack_mem(512; Allocator allocator)
{
DString s;
s.init(allocator);
s.appendf(fmt, ...args);
in_panic = false;
panic(s.str_view(), 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
{
$if env::COMPILER_SAFE_MODE:
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
$endif;
$$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();
}
<*
Unconditionally break into an attached debugger when reached.
*>
macro void breakpoint() @builtin
{
$$breakpoint();
}
macro any_make(void* ptr, typeid type) @builtin
{
return $$any_make(ptr, type);
}
macro any.retype_to(&self, typeid type)
{
return $$any_make(self.ptr, type);
}
macro any.as_inner(&self)
{
return $$any_make(self.ptr, self.type.inner);
}
<*
@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(return, $Type)
*>
macro bitcast(expr, $Type) @builtin
{
$if $Type.alignof <= $alignof(expr):
return *($Type*)&expr;
$else
$Type x @noinit;
$$memcpy(&x, &expr, $sizeof(expr), false, $Type.alignof, $alignof(expr));
return x;
$endif
}
<*
@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.from_ordinal(i);
}
return SearchResult.MISSING?;
}
<*
@param $Type `The type of the enum`
@require $Type.kindof == ENUM `Only enums may be used`
@require $defined($Type.#value) `Expected '#value' to match an enum associated value`
@require $assignable(value, $typeof(($Type){}.#value)) `Expected the value to match the type of the associated value`
@ensure @typeis(return, $Type)
@return! SearchResult.MISSING
*>
macro @enum_from_value($Type, #value, value) @builtin
{
usz elements = $Type.elements;
foreach (e : $Type.values)
{
if (e.#value == value) return e;
}
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
{
$switch
$case env::BUILTIN_EXPECT_IS_DISABLED:
return #value;
$case $probability == 1.0:
return $$expect(#value, true);
$default:
return $$expect_with_probability(#value, true, $probability);
$endswitch
}
<*
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
{
$switch
$case env::BUILTIN_EXPECT_IS_DISABLED:
return #value;
$case $probability == 1.0:
return $$expect(#value, false);
$default:
return $$expect_with_probability(#value, false, $probability);
$endswitch
}
<*
@require values::@is_int(#value) || values::@is_bool(#value)
@require $assignable(expected, $typeof(#value))
@require $probability >= 0 && $probability <= 1.0
*>
macro @expect(#value, expected, $probability = 1.0) @builtin
{
$switch
$case env::BUILTIN_EXPECT_IS_DISABLED:
return #value == expected;
$case $probability == 1.0:
return $$expect(#value, ($typeof(#value))expected);
$default:
return $$expect_with_probability(#value, expected, $probability);
$endswitch
}
<*
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
{
$if !env::BUILTIN_PREFETCH_IS_DISABLED:
$$prefetch(ptr, $write ? 1 : 0, $locality.ordinal);
$endif
}
macro swizzle(v, ...) @builtin
{
return $$swizzle(v, $vasplat);
}
macro swizzle2(v, v2, ...) @builtin
{
return $$swizzle2(v, v2, $vasplat);
}
<*
Return the excuse in the Optional if it is Empty, otherwise
return a null fault.
@require @typekind(#expr) == OPTIONAL : `@catch expects an Optional value`
*>
macro anyfault @catch(#expr) @builtin
{
if (catch f = #expr) return f;
return {};
}
<*
Check if an Optional expression holds a value or is empty, returning true
if it has a value.
@require @typekind(#expr) == OPTIONAL : `@ok expects an Optional value`
*>
macro bool @ok(#expr) @builtin
{
if (catch #expr) return false;
return true;
}
<*
@require $defined(&#value, (char*)&#value) "This must be a value that can be viewed as a char array"
*>
macro char[] @as_char_view(#value) @builtin
{
return ((char*)&#value)[:$sizeof(#value)];
}
macro isz @str_find(String $string, String $needle) @builtin => $$str_find($string, $needle);
macro String @str_upper(String $str) @builtin => $$str_upper($str);
macro String @str_lower(String $str) @builtin => $$str_lower($str);
macro uint @str_hash(String $str) @builtin => $$str_hash($str);
macro @generic_hash_core(h, value)
{
h ^= (uint)value; // insert lowest 32 bits
h *= 0x96f59e5b; // diffuse them up
h ^= h >> 16; // diffuse them down
return h;
}
macro @generic_hash(value)
{
uint h = @generic_hash_core((uint)0x3efd4391, value);
$for (var $cnt = 4; $cnt < $sizeof(value); $cnt += 4)
value >>= 32; // reduce value
h = @generic_hash_core(h, value);
$endfor
return h;
}
macro uint int.hash(int i) => @generic_hash(i);
macro uint uint.hash(uint i) => @generic_hash(i);
macro uint short.hash(short s) => @generic_hash(s);
macro uint ushort.hash(ushort s) => @generic_hash(s);
macro uint char.hash(char c) => @generic_hash(c);
macro uint ichar.hash(ichar c) => @generic_hash(c);
macro uint long.hash(long i) => @generic_hash(i);
macro uint ulong.hash(ulong i) => @generic_hash(i);
macro uint int128.hash(int128 i) => @generic_hash(i);
macro uint uint128.hash(uint128 i) => @generic_hash(i);
macro uint bool.hash(bool b) => @generic_hash(b);
macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
distinct EmptySlot = void*;
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
macro @is_empty_macro_slot(#arg) @builtin => @typeis(#arg, EmptySlot);
macro @is_valid_macro_slot(#arg) @builtin => !@typeis(#arg, EmptySlot);
const MAX_FRAMEADDRESS = 128;
<*
@require n >= 0
*>
macro void* get_frameaddress(int n)
{
if (n > MAX_FRAMEADDRESS) return null;
switch (n)
{
case 0: return $$frameaddress(0);
case 1: return $$frameaddress(1);
case 2: return $$frameaddress(2);
case 3: return $$frameaddress(3);
case 4: return $$frameaddress(4);
case 5: return $$frameaddress(5);
case 6: return $$frameaddress(6);
case 7: return $$frameaddress(7);
case 8: return $$frameaddress(8);
case 9: return $$frameaddress(9);
case 10: return $$frameaddress(10);
case 11: return $$frameaddress(11);
case 12: return $$frameaddress(12);
case 13: return $$frameaddress(13);
case 14: return $$frameaddress(14);
case 15: return $$frameaddress(15);
case 16: return $$frameaddress(16);
case 17: return $$frameaddress(17);
case 18: return $$frameaddress(18);
case 19: return $$frameaddress(19);
case 20: return $$frameaddress(20);
case 21: return $$frameaddress(21);
case 22: return $$frameaddress(22);
case 23: return $$frameaddress(23);
case 24: return $$frameaddress(24);
case 25: return $$frameaddress(25);
case 26: return $$frameaddress(26);
case 27: return $$frameaddress(27);
case 28: return $$frameaddress(28);
case 29: return $$frameaddress(29);
case 30: return $$frameaddress(30);
case 31: return $$frameaddress(31);
case 32: return $$frameaddress(32);
case 33: return $$frameaddress(33);
case 34: return $$frameaddress(34);
case 35: return $$frameaddress(35);
case 36: return $$frameaddress(36);
case 37: return $$frameaddress(37);
case 38: return $$frameaddress(38);
case 39: return $$frameaddress(39);
case 40: return $$frameaddress(40);
case 41: return $$frameaddress(41);
case 42: return $$frameaddress(42);
case 43: return $$frameaddress(43);
case 44: return $$frameaddress(44);
case 45: return $$frameaddress(45);
case 46: return $$frameaddress(46);
case 47: return $$frameaddress(47);
case 48: return $$frameaddress(48);
case 49: return $$frameaddress(49);
case 50: return $$frameaddress(50);
case 51: return $$frameaddress(51);
case 52: return $$frameaddress(52);
case 53: return $$frameaddress(53);
case 54: return $$frameaddress(54);
case 55: return $$frameaddress(55);
case 56: return $$frameaddress(56);
case 57: return $$frameaddress(57);
case 58: return $$frameaddress(58);
case 59: return $$frameaddress(59);
case 60: return $$frameaddress(60);
case 61: return $$frameaddress(61);
case 62: return $$frameaddress(62);
case 63: return $$frameaddress(63);
case 64: return $$frameaddress(64);
case 65: return $$frameaddress(65);
case 66: return $$frameaddress(66);
case 67: return $$frameaddress(67);
case 68: return $$frameaddress(68);
case 69: return $$frameaddress(69);
case 70: return $$frameaddress(70);
case 71: return $$frameaddress(71);
case 72: return $$frameaddress(72);
case 73: return $$frameaddress(73);
case 74: return $$frameaddress(74);
case 75: return $$frameaddress(75);
case 76: return $$frameaddress(76);
case 77: return $$frameaddress(77);
case 78: return $$frameaddress(78);
case 79: return $$frameaddress(79);
case 80: return $$frameaddress(80);
case 81: return $$frameaddress(81);
case 82: return $$frameaddress(82);
case 83: return $$frameaddress(83);
case 84: return $$frameaddress(84);
case 85: return $$frameaddress(85);
case 86: return $$frameaddress(86);
case 87: return $$frameaddress(87);
case 88: return $$frameaddress(88);
case 89: return $$frameaddress(89);
case 90: return $$frameaddress(90);
case 91: return $$frameaddress(91);
case 92: return $$frameaddress(92);
case 93: return $$frameaddress(93);
case 94: return $$frameaddress(94);
case 95: return $$frameaddress(95);
case 96: return $$frameaddress(96);
case 97: return $$frameaddress(97);
case 98: return $$frameaddress(98);
case 99: return $$frameaddress(99);
case 100: return $$frameaddress(100);
case 101: return $$frameaddress(101);
case 102: return $$frameaddress(102);
case 103: return $$frameaddress(103);
case 104: return $$frameaddress(104);
case 105: return $$frameaddress(105);
case 106: return $$frameaddress(106);
case 107: return $$frameaddress(107);
case 108: return $$frameaddress(108);
case 109: return $$frameaddress(109);
case 110: return $$frameaddress(110);
case 111: return $$frameaddress(111);
case 112: return $$frameaddress(112);
case 113: return $$frameaddress(113);
case 114: return $$frameaddress(114);
case 115: return $$frameaddress(115);
case 116: return $$frameaddress(116);
case 117: return $$frameaddress(117);
case 118: return $$frameaddress(118);
case 119: return $$frameaddress(119);
case 120: return $$frameaddress(120);
case 121: return $$frameaddress(121);
case 122: return $$frameaddress(122);
case 123: return $$frameaddress(123);
case 124: return $$frameaddress(124);
case 125: return $$frameaddress(125);
case 126: return $$frameaddress(126);
case 127: return $$frameaddress(127);
case 128: return $$frameaddress(128);
default: unreachable();
}
}
<*
@require n >= 0
*>
macro void* get_returnaddress(int n)
{
if (n > MAX_FRAMEADDRESS) return null;
switch (n)
{
case 0: return $$returnaddress(0);
case 1: return $$returnaddress(1);
case 2: return $$returnaddress(2);
case 3: return $$returnaddress(3);
case 4: return $$returnaddress(4);
case 5: return $$returnaddress(5);
case 6: return $$returnaddress(6);
case 7: return $$returnaddress(7);
case 8: return $$returnaddress(8);
case 9: return $$returnaddress(9);
case 10: return $$returnaddress(10);
case 11: return $$returnaddress(11);
case 12: return $$returnaddress(12);
case 13: return $$returnaddress(13);
case 14: return $$returnaddress(14);
case 15: return $$returnaddress(15);
case 16: return $$returnaddress(16);
case 17: return $$returnaddress(17);
case 18: return $$returnaddress(18);
case 19: return $$returnaddress(19);
case 20: return $$returnaddress(20);
case 21: return $$returnaddress(21);
case 22: return $$returnaddress(22);
case 23: return $$returnaddress(23);
case 24: return $$returnaddress(24);
case 25: return $$returnaddress(25);
case 26: return $$returnaddress(26);
case 27: return $$returnaddress(27);
case 28: return $$returnaddress(28);
case 29: return $$returnaddress(29);
case 30: return $$returnaddress(30);
case 31: return $$returnaddress(31);
case 32: return $$returnaddress(32);
case 33: return $$returnaddress(33);
case 34: return $$returnaddress(34);
case 35: return $$returnaddress(35);
case 36: return $$returnaddress(36);
case 37: return $$returnaddress(37);
case 38: return $$returnaddress(38);
case 39: return $$returnaddress(39);
case 40: return $$returnaddress(40);
case 41: return $$returnaddress(41);
case 42: return $$returnaddress(42);
case 43: return $$returnaddress(43);
case 44: return $$returnaddress(44);
case 45: return $$returnaddress(45);
case 46: return $$returnaddress(46);
case 47: return $$returnaddress(47);
case 48: return $$returnaddress(48);
case 49: return $$returnaddress(49);
case 50: return $$returnaddress(50);
case 51: return $$returnaddress(51);
case 52: return $$returnaddress(52);
case 53: return $$returnaddress(53);
case 54: return $$returnaddress(54);
case 55: return $$returnaddress(55);
case 56: return $$returnaddress(56);
case 57: return $$returnaddress(57);
case 58: return $$returnaddress(58);
case 59: return $$returnaddress(59);
case 60: return $$returnaddress(60);
case 61: return $$returnaddress(61);
case 62: return $$returnaddress(62);
case 63: return $$returnaddress(63);
case 64: return $$returnaddress(64);
case 65: return $$returnaddress(65);
case 66: return $$returnaddress(66);
case 67: return $$returnaddress(67);
case 68: return $$returnaddress(68);
case 69: return $$returnaddress(69);
case 70: return $$returnaddress(70);
case 71: return $$returnaddress(71);
case 72: return $$returnaddress(72);
case 73: return $$returnaddress(73);
case 74: return $$returnaddress(74);
case 75: return $$returnaddress(75);
case 76: return $$returnaddress(76);
case 77: return $$returnaddress(77);
case 78: return $$returnaddress(78);
case 79: return $$returnaddress(79);
case 80: return $$returnaddress(80);
case 81: return $$returnaddress(81);
case 82: return $$returnaddress(82);
case 83: return $$returnaddress(83);
case 84: return $$returnaddress(84);
case 85: return $$returnaddress(85);
case 86: return $$returnaddress(86);
case 87: return $$returnaddress(87);
case 88: return $$returnaddress(88);
case 89: return $$returnaddress(89);
case 90: return $$returnaddress(90);
case 91: return $$returnaddress(91);
case 92: return $$returnaddress(92);
case 93: return $$returnaddress(93);
case 94: return $$returnaddress(94);
case 95: return $$returnaddress(95);
case 96: return $$returnaddress(96);
case 97: return $$returnaddress(97);
case 98: return $$returnaddress(98);
case 99: return $$returnaddress(99);
case 100: return $$returnaddress(100);
case 101: return $$returnaddress(101);
case 102: return $$returnaddress(102);
case 103: return $$returnaddress(103);
case 104: return $$returnaddress(104);
case 105: return $$returnaddress(105);
case 106: return $$returnaddress(106);
case 107: return $$returnaddress(107);
case 108: return $$returnaddress(108);
case 109: return $$returnaddress(109);
case 110: return $$returnaddress(110);
case 111: return $$returnaddress(111);
case 112: return $$returnaddress(112);
case 113: return $$returnaddress(113);
case 114: return $$returnaddress(114);
case 115: return $$returnaddress(115);
case 116: return $$returnaddress(116);
case 117: return $$returnaddress(117);
case 118: return $$returnaddress(118);
case 119: return $$returnaddress(119);
case 120: return $$returnaddress(120);
case 121: return $$returnaddress(121);
case 122: return $$returnaddress(122);
case 123: return $$returnaddress(123);
case 124: return $$returnaddress(124);
case 125: return $$returnaddress(125);
case 126: return $$returnaddress(126);
case 127: return $$returnaddress(127);
case 128: return $$returnaddress(128);
default: unreachable();
}
}
module std::core::builtin @if((env::LINUX || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
import libc, std::io;
fn void sig_panic(String message)
{
default_panic(message, "???", "???", 0);
}
SignalFunction old_bus_error;
SignalFunction old_segmentation_fault;
fn void sig_bus_error(CInt i)
{
$if !env::NATIVE_STACKTRACE:
sig_panic("Illegal memory access.");
$else
$if $defined(io::stderr):
if (!print_backtrace("Illegal memory access.", 1))
{
io::eprintn("\nERROR: 'Illegal memory access'.");
}
$endif
$endif
$$trap();
}
fn void sig_segmentation_fault(CInt i)
{
$if !env::NATIVE_STACKTRACE:
sig_panic("Out of bounds memory access.");
$else
$if $defined(io::stderr):
if (!print_backtrace("Out of bounds memory access.", 1))
{
io::eprintn("\nERROR: Memory error without backtrace, possible stack overflow.");
}
$endif
$endif
$$trap();
}
fn void install_signal_handler(CInt signal, SignalFunction func) @local
{
SignalFunction old = libc::signal(signal, func);
// Restore
if ((iptr)old > 1024) libc::signal(signal, old);
}
// Clean this up
fn void install_signal_handlers() @init(101) @local @if(env::BACKTRACE)
{
install_signal_handler(libc::SIGBUS, &sig_bus_error);
install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
}

View File

@@ -0,0 +1,128 @@
// Copyright (c) 2021-2024 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::@comparable_value(a) && types::@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::@comparable_value(a) && types::@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::@comparable_value(a) && types::@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::@comparable_value(a) && types::@comparable_value(b)
*>
macro int compare_to(a, b) @builtin
{
$switch
$case $defined(a.compare_to):
return a.compare_to(b);
$case $defined(a.less):
return (int)b.less(a) - (int)a.less(b);
$default:
return (int)(a > b) - (int)(a < b);
$endswitch
}
<*
@require types::@comparable_value(a) && types::@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::@equatable_value(a) && types::@equatable_value(b) `values must be equatable`
*>
macro bool equals(a, b) @builtin
{
$switch
$case $defined(a.equals, a.equals(b)):
return a.equals(b);
$case $defined(a.compare_to, a.compare_to(b)):
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
}

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

@@ -0,0 +1,61 @@
// Copyright (c) 2021-2024 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;
def CShort = $typefrom(signed_int_from_bitsize($$C_SHORT_SIZE));
def CUShort = $typefrom(unsigned_int_from_bitsize($$C_SHORT_SIZE));
def CInt = $typefrom(signed_int_from_bitsize($$C_INT_SIZE));
def CUInt = $typefrom(unsigned_int_from_bitsize($$C_INT_SIZE));
def CLong = $typefrom(signed_int_from_bitsize($$C_LONG_SIZE));
def CULong = $typefrom(unsigned_int_from_bitsize($$C_LONG_SIZE));
def CLongLong = $typefrom(signed_int_from_bitsize($$C_LONG_LONG_SIZE));
def CULongLong = $typefrom(unsigned_int_from_bitsize($$C_LONG_LONG_SIZE));
def CSChar = ichar;
def CUChar = char;
def CChar = $typefrom($$C_CHAR_IS_SIGNED ? ichar.typeid : char.typeid);
enum CBool : char
{
FALSE,
TRUE
}
// Helper macros
macro typeid signed_int_from_bitsize(usz $bitsize) @private
{
$switch ($bitsize)
$case 128: return int128.typeid;
$case 64: return long.typeid;
$case 32: return int.typeid;
$case 16: return short.typeid;
$case 8: return ichar.typeid;
$default: $error("Invalid bitsize");
$endswitch
}
macro typeid unsigned_int_from_bitsize(usz $bitsize) @private
{
$switch ($bitsize)
$case 128: return uint128.typeid;
$case 64: return ulong.typeid;
$case 32: return uint.typeid;
$case 16: return ushort.typeid;
$case 8: return char.typeid;
$default: $error("Invalid bitsize");
$endswitch
}

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

@@ -0,0 +1,416 @@
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`
*>
fn usz! char32_to_utf8(Char32 c, char[] output)
{
if (!output.len) return UnicodeResult.CONVERSION_FAILED?;
switch (true)
{
case c <= 0x7f:
output[0] = (char)c;
return 1;
case c <= 0x7ff:
if (output.len < 2) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xC0 | c >> 6);
output[1] = (char)(0x80 | (c & 0x3F));
return 2;
case c <= 0xffff:
if (output.len < 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 (output.len < 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 usz char32_to_utf8_unsafe(Char32 c, char** output)
{
switch
{
case c <= 0x7f:
(*output)++[0] = (char)c;
return 1;
case c <= 0x7ff:
(*output)++[0] = (char)(0xC0 | c >> 6);
(*output)++[0] = (char)(0x80 | (c & 0x3F));
return 2;
case c <= 0xffff:
(*output)++[0] = (char)(0xE0 | c >> 12);
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
(*output)++[0] = (char)(0x80 | (c & 0x3F));
return 3;
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));
return 4;
}
}
<*
@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, char[] utf8_buffer)
{
char[] buffer = utf8_buffer;
foreach (uc : utf32)
{
usz used = char32_to_utf8(uc, buffer) @inline!;
buffer = buffer[used..];
}
// Zero terminate if there is space.
if (buffer.len > 0) buffer[0] = 0;
return utf8_buffer.len - buffer.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;
}
}

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

@@ -0,0 +1,695 @@
module std::core::dstring;
import std::io;
distinct DString (OutStream) = DStringOpaque*;
distinct DStringOpaque = void;
const usz MIN_CAPACITY @private = 16;
<*
@require !self.data() "String already initialized"
*>
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
data.allocator = allocator;
data.len = 0;
data.capacity = capacity;
return *self = (DString)data;
}
<*
@require !self.data() "String already initialized"
*>
fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
data.allocator = allocator;
data.len = 0;
data.capacity = capacity;
return *self = (DString)data;
}
<*
@require !self.data() "String already initialized"
*>
fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY) @deprecated("Use tinit()")
{
self.init(allocator::temp(), capacity) @inline;
return *self;
}
<*
@require !self.data() "String already initialized"
*>
fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY)
{
self.init(allocator::temp(), capacity) @inline;
return *self;
}
fn DString new_with_capacity(usz capacity, Allocator allocator = allocator::heap())
{
return (DString){}.init(allocator, capacity);
}
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline;
fn DString new(String c = "", Allocator allocator = allocator::heap())
{
usz len = c.len;
StringData* data = (StringData*)new_with_capacity(len, allocator);
if (len)
{
data.len = len;
mem::copy(&data.chars, c.ptr, len);
}
return (DString)data;
}
fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline;
fn void DString.replace_char(self, char ch, char replacement)
{
StringData* data = self.data();
foreach (&c : data.chars[:data.len])
{
if (*c == ch) *c = replacement;
}
}
fn void DString.replace(&self, String needle, String replacement)
{
StringData* data = self.data();
usz needle_len = needle.len;
if (!data || data.len < needle_len) return;
usz replace_len = replacement.len;
if (needle_len == 1 && replace_len == 1)
{
self.replace_char(needle[0], replacement[0]);
return;
}
@pool(data.allocator) {
String str = self.tcopy_str();
self.clear();
usz len = str.len;
usz match = 0;
foreach (i, c : str)
{
if (c == needle[match])
{
match++;
if (match == needle_len)
{
self.append_chars(replacement);
match = 0;
continue;
}
continue;
}
if (match > 0)
{
self.append_chars(str[i - match:match]);
match = 0;
}
self.append_char(c);
}
if (match > 0) self.append_chars(str[^match:match]);
};
}
fn DString DString.new_concat(self, DString b, Allocator allocator = allocator::heap()) @deprecated("Use concat(mem)")
{
DString string;
string.init(allocator, self.len() + b.len());
string.append(self);
string.append(b);
return string;
}
fn DString DString.concat(self, Allocator allocator, DString b)
{
DString string;
string.init(allocator, self.len() + b.len());
string.append(self);
string.append(b);
return string;
}
fn DString DString.temp_concat(self, DString b) => self.concat(allocator::temp(), b);
fn ZString DString.zstr_view(&self)
{
StringData* data = self.data();
if (!data) return "";
if (data.capacity == data.len)
{
self.reserve(1);
data = self.data();
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(self)
{
if (!self) return 0;
return self.data().capacity;
}
fn usz DString.len(&self) @dynamic @operator(len)
{
if (!*self) return 0;
return self.data().len;
}
<*
@require new_size <= self.len()
*>
fn void DString.chop(self, usz new_size)
{
if (!self) return;
self.data().len = new_size;
}
fn String DString.str_view(self)
{
StringData* data = self.data();
if (!data) return "";
return (String)data.chars[:data.len];
}
<*
@require index < self.len()
@require self.data() != null "Empty string"
*>
fn char DString.char_at(self, usz index) @operator([])
{
return self.data().chars[index];
}
<*
@require index < self.len()
@require self.data() != null "Empty string"
*>
fn char* DString.char_ref(&self, usz index) @operator(&[])
{
return &self.data().chars[index];
}
fn usz DString.append_utf32(&self, Char32[] chars)
{
self.reserve(chars.len);
usz end = self.len();
foreach (Char32 c : chars)
{
self.append_char32(c);
}
return self.data().len - end;
}
<*
@require index < self.len()
*>
fn void DString.set(self, usz index, char c) @operator([]=)
{
self.data().chars[index] = c;
}
fn void DString.append_repeat(&self, char c, usz times)
{
if (times == 0) return;
self.reserve(times);
StringData* data = self.data();
for (usz i = 0; i < times; i++)
{
data.chars[data.len++] = c;
}
}
<*
@require c <= 0x10ffff
*>
fn usz DString.append_char32(&self, Char32 c)
{
char[4] buffer @noinit;
char* p = &buffer;
usz n = conv::char32_to_utf8_unsafe(c, &p);
self.reserve(n);
StringData* data = self.data();
data.chars[data.len:n] = buffer[:n];
data.len += n;
return n;
}
fn DString DString.tcopy(&self) => self.copy(allocator::temp());
fn DString DString.copy(self, Allocator allocator = null)
{
if (!self)
{
if (allocator) return new_with_capacity(0, allocator);
return (DString)null;
}
StringData* data = self.data();
if (!allocator) allocator = allocator::heap();
DString new_string = new_with_capacity(data.capacity, allocator);
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
return new_string;
}
fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap())
{
usz str_len = self.len();
if (!str_len)
{
return (ZString)allocator::calloc(allocator, 1);
}
char* zstr = allocator::malloc(allocator, str_len + 1);
StringData* data = self.data();
mem::copy(zstr, &data.chars, str_len);
zstr[str_len] = 0;
return (ZString)zstr;
}
fn String DString.copy_str(self, Allocator allocator = allocator::heap())
{
return (String)self.copy_zstr(allocator)[:self.len()];
}
fn String DString.tcopy_str(self) => self.copy_str(allocator::temp()) @inline;
fn bool DString.equals(self, DString other_string)
{
StringData *str1 = self.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(&self)
{
if (!*self) return;
StringData* data = self.data();
if (!data) return;
allocator::free(data.allocator, data);
*self = (DString)null;
}
fn bool DString.less(self, DString other_string)
{
StringData* str1 = self.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(&self, String str)
{
usz other_len = str.len;
if (!other_len) return;
if (!*self)
{
*self = new(str);
return;
}
self.reserve(other_len);
StringData* data = self.data();
mem::copy(&data.chars[data.len], str.ptr, other_len);
data.len += other_len;
}
fn Char32[] DString.copy_utf32(&self, Allocator allocator = allocator::heap())
{
return self.str_view().to_utf32(allocator) @inline!!;
}
fn void DString.append_string(&self, DString str)
{
StringData* other = str.data();
if (!other) return;
self.append(str.str_view());
}
fn void DString.clear(self)
{
if (!self) return;
self.data().len = 0;
}
fn usz! DString.write(&self, char[] buffer) @dynamic
{
self.append_chars((String)buffer);
return buffer.len;
}
fn void! DString.write_byte(&self, char c) @dynamic
{
self.append_char(c);
}
fn void DString.append_char(&self, char c)
{
if (!*self)
{
*self = new_with_capacity(MIN_CAPACITY);
}
self.reserve(1);
StringData* data = self.data();
data.chars[data.len++] = c;
}
<*
@require start < self.len()
@require end < self.len()
@require end >= start "End must be same or equal to the start"
*>
fn void DString.delete_range(&self, usz start, usz end)
{
self.delete(start, end - start + 1);
}
<*
@require start < self.len()
@require start + len <= self.len()
*>
fn void DString.delete(&self, usz start, usz len = 1)
{
if (!len) return;
StringData* data = self.data();
usz new_len = data.len - len;
if (new_len == 0)
{
data.len = 0;
return;
}
usz len_after = data.len - start - len;
if (len_after > 0)
{
data.chars[start:len_after] = data.chars[start + len:len_after];
}
data.len = new_len;
}
macro void DString.append(&self, value)
{
var $Type = $typeof(value);
$switch ($Type)
$case char:
$case ichar:
self.append_char(value);
$case DString:
self.append_string(value);
$case String:
self.append_chars(value);
$case Char32:
self.append_char32(value);
$default:
$switch
$case $defined((Char32)value):
self.append_char32((Char32)value);
$case $defined((String)value):
self.append_chars((String)value);
$default:
$error "Unsupported type for append use appendf instead.";
$endswitch
$endswitch
}
<*
@require index <= self.len()
*>
fn void DString.insert_chars_at(&self, usz index, String s)
{
if (s.len == 0) return;
self.reserve(s.len);
StringData* data = self.data();
usz len = self.len();
if (data.chars[:len].ptr == s.ptr)
{
// Source and destination are the same: nothing to do.
return;
}
index = min(index, len);
data.len += s.len;
char* start = data.chars[index:s.len].ptr; // area to insert into
mem::move(start + s.len, start, len - index); // move existing data
switch
{
case s.ptr <= start && start < s.ptr + s.len:
// Overlapping areas.
foreach_r (i, c : s)
{
data.chars[index + i] = c;
}
case start <= s.ptr && s.ptr < start + len:
// Source has moved.
mem::move(start, s.ptr + s.len, s.len);
default:
mem::move(start, s, s.len);
}
}
<*
@require index <= self.len()
*>
fn void DString.insert_string_at(&self, usz index, DString str)
{
StringData* other = str.data();
if (!other) return;
self.insert_at(index, str.str_view());
}
<*
@require index <= self.len()
*>
fn void DString.insert_char_at(&self, usz index, char c)
{
self.reserve(1);
StringData* data = self.data();
char* start = &data.chars[index];
mem::move(start + 1, start, self.len() - index);
data.chars[index] = c;
data.len++;
}
<*
@require index <= self.len()
*>
fn usz DString.insert_char32_at(&self, usz index, Char32 c)
{
char[4] buffer @noinit;
char* p = &buffer;
usz n = conv::char32_to_utf8_unsafe(c, &p);
self.reserve(n);
StringData* data = self.data();
char* start = &data.chars[index];
mem::move(start + n, start, self.len() - index);
data.chars[index:n] = buffer[:n];
data.len += n;
return n;
}
<*
@require index <= self.len()
*>
fn usz DString.insert_utf32_at(&self, usz index, Char32[] chars)
{
usz n = conv::utf8len_for_utf32(chars);
self.reserve(n);
StringData* data = self.data();
char* start = &data.chars[index];
mem::move(start + n, start, self.len() - index);
char[4] buffer @noinit;
foreach(c : chars)
{
char* p = &buffer;
usz m = conv::char32_to_utf8_unsafe(c, &p);
data.chars[index:m] = buffer[:m];
index += m;
}
data.len += n;
return n;
}
macro void DString.insert_at(&self, usz index, value)
{
var $Type = $typeof(value);
$switch ($Type)
$case char:
$case ichar:
self.insert_char_at(index, value);
$case DString:
self.insert_string_at(index, value);
$case String:
self.insert_chars_at(index, value);
$case Char32:
self.insert_char32_at(index, value);
$default:
$switch
$case $defined((Char32)value):
self.insert_char32_at(index, (Char32)value);
$case $defined((String)value):
self.insert_chars_at(index, (String)value);
$default:
$error "Unsupported type for insert";
$endswitch
$endswitch
}
fn usz! DString.appendf(&self, String format, args...) @maydiscard
{
if (!self.data()) self.init(mem, format.len + 20);
@pool(self.data().allocator)
{
Formatter formatter;
formatter.init(&out_string_append_fn, self);
return formatter.vprintf(format, args);
};
}
fn usz! DString.appendfn(&self, String format, args...) @maydiscard
{
if (!self.data()) self.init(mem, format.len + 20);
@pool(self.data().allocator)
{
Formatter formatter;
formatter.init(&out_string_append_fn, self);
usz len = formatter.vprintf(format, args)!;
self.append('\n');
return len + 1;
};
}
fn DString new_join(String[] s, String joiner, Allocator allocator = allocator::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, allocator);
res.append(s[0]);
foreach (String* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res;
}
fn void! out_string_append_fn(void* data, char c) @private
{
DString* s = data;
s.append_char(c);
}
fn void DString.reverse(self)
{
StringData *data = self.data();
if (!data) return;
isz mid = data.len / 2;
for (isz i = 0; i < mid; i++)
{
char temp = data.chars[i];
isz reverse_index = data.len - 1 - i;
data.chars[i] = data.chars[reverse_index];
data.chars[reverse_index] = temp;
}
}
fn StringData* DString.data(self) @inline @private
{
return (StringData*)self;
}
fn void DString.reserve(&self, usz addition)
{
StringData* data = self.data();
if (!data)
{
*self = 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;
while (new_capacity < len) new_capacity *= 2;
data.capacity = new_capacity;
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
}
fn usz! DString.read_from_stream(&self, InStream reader)
{
if (&reader.available)
{
usz total_read = 0;
while (usz available = reader.available()!)
{
self.reserve(available);
StringData* data = self.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
self.reserve(16);
StringData* data = self.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;
}

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

@@ -0,0 +1,197 @@
// Copyright (c) 2021-2024 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;
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
XTENSA, // Xtensa
}
const String COMPILER_BUILD_HASH = $$BUILD_HASH;
const String COMPILER_BUILD_DATE = $$BUILD_DATE;
const OsType OS_TYPE = OsType.from_ordinal($$OS_TYPE);
const ArchType ARCH_TYPE = ArchType.from_ordinal($$ARCH_TYPE);
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE;
const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$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 REGISTER_SIZE = $$REGISTER_SIZE;
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
const bool DEBUG_SYMBOLS = $$DEBUG_SYMBOLS;
const bool BACKTRACE = $$BACKTRACE;
const usz LLVM_VERSION = $$LLVM_VERSION;
const bool BENCHMARKING = $$BENCHMARKING;
const bool TESTING = $$TESTING;
const MemoryEnvironment MEMORY_ENV = MemoryEnvironment.from_ordinal($$MEMORY_ENVIRONMENT);
const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING);
const bool X86_64 = ARCH_TYPE == X86_64;
const bool X86 = ARCH_TYPE == X86;
const bool AARCH64 = ARCH_TYPE == AARCH64;
const bool NATIVE_STACKTRACE = LINUX || DARWIN || WIN32;
const bool LINUX = LIBC && OS_TYPE == LINUX;
const bool DARWIN = LIBC && os_is_darwin();
const bool WIN32 = LIBC && OS_TYPE == WIN32;
const bool POSIX = LIBC && os_is_posix();
const bool OPENBSD = LIBC && OS_TYPE == OPENBSD;
const bool FREEBSD = LIBC && OS_TYPE == FREEBSD;
const bool NETBSD = LIBC && OS_TYPE == NETBSD;
const bool BSD_FAMILY = env::FREEBSD || env::OPENBSD || env::NETBSD;
const bool WASI = LIBC && OS_TYPE == WASI;
const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER;
const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
macro bool os_is_darwin() @const
{
$switch (OS_TYPE)
$case IOS:
$case MACOS:
$case TVOS:
$case WATCHOS:
return true;
$default:
return false;
$endswitch
}
macro bool os_is_posix() @const
{
$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
}
const BUILTIN_EXPECT_IS_DISABLED = $feature(DISABLE_BUILTIN_EXPECT);
const BUILTIN_PREFETCH_IS_DISABLED = $feature(DISABLE_BUILTIN_PREFETCH);

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

@@ -0,0 +1,911 @@
// 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;
import std::core::mem::allocator @public;
import std::math;
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
macro bool @constant_is_power_of_2($x) @const @private
{
return $x != 0 && ($x & ($x - 1)) == 0;
}
<*
Load a vector from memory according to a mask assuming default alignment.
@param ptr "The pointer address to load from."
@param mask "The mask for the load"
@param passthru "The value to use for non masked values"
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require passthru.len == mask.len : "Mask and passthru must have the same length"
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
*>
macro masked_load(ptr, bool[<?>] mask, passthru)
{
return $$masked_load(ptr, mask, passthru, 0);
}
<*
Load a vector from memory according to a mask.
@param ptr "The pointer address to load from."
@param mask "The mask for the load"
@param passthru "The value to use for non masked values"
@param $alignment "The alignment to assume for the pointer"
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require passthru.len == mask.len : "Mask and passthru must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
*>
macro @masked_load_aligned(ptr, bool[<?>] mask, passthru, usz $alignment)
{
return $$masked_load(ptr, mask, passthru, $alignment);
}
<*
Load values from a pointer vector, assuming default alignment.
@param ptrvec "The vector of pointers to load from."
@param mask "The mask for the load"
@param passthru "The value to use for non masked values"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
@require passthru.len == mask.len : "Mask and passthru must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
*>
macro gather(ptrvec, bool[<?>] mask, passthru)
{
return $$gather(ptrvec, mask, passthru, 0);
}
<*
Load values from a pointer vector.
@param ptrvec "The vector of pointers to load from."
@param mask "The mask for the load"
@param passthru "The value to use for non masked values"
@param $alignment "The alignment to assume for the pointers"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
@require passthru.len == mask.len : "Mask and passthru must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
*>
macro @gather_aligned(ptrvec, bool[<?>] mask, passthru, usz $alignment)
{
return $$gather(ptrvec, mask, passthru, $alignment);
}
<*
Store parts of a vector according to the mask, assuming default alignment.
@param ptr "The pointer address to store to."
@param value "The value to store masked"
@param mask "The mask for the store"
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require value.len == mask.len : "Mask and value must have the same length"
*>
macro masked_store(ptr, value, bool[<?>] mask)
{
return $$masked_store(ptr, value, mask, 0);
}
<*
@param ptr "The pointer address to store to."
@param value "The value to store masked"
@param mask "The mask for the store"
@param $alignment "The alignment of the pointer"
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require value.len == mask.len : "Mask and value must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
*>
macro @masked_store_aligned(ptr, value, bool[<?>] mask, usz $alignment)
{
return $$masked_store(ptr, value, mask, $alignment);
}
<*
@param ptrvec "The vector pointer containing the addresses to store to."
@param value "The value to store masked"
@param mask "The mask for the store"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
@require value.len == mask.len : "Mask and value must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
*>
macro scatter(ptrvec, value, bool[<?>] mask)
{
return $$scatter(ptrvec, value, mask, 0);
}
<*
@param ptrvec "The vector pointer containing the addresses to store to."
@param value "The value to store masked"
@param mask "The mask for the store"
@param $alignment "The alignment of the load"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
@require value.len == mask.len : "Mask and value must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
*>
macro @scatter_aligned(ptrvec, value, bool[<?>] mask, usz $alignment)
{
return $$scatter(ptrvec, value, mask, $alignment);
}
<*
@param #x "The variable or dereferenced pointer to load."
@param $alignment "The alignment to assume for the load"
@return "The value of the variable"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
*>
macro @unaligned_load(#x, usz $alignment) @builtin
{
return $$unaligned_load(&#x, $alignment);
}
<*
@param #x "The variable or dereferenced pointer to store to."
@param value "The value to store."
@param $alignment "The alignment to assume for the store"
@return "The value stored"
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
@require $defined(#x = value) : "The value doesn't match the variable"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
*>
macro @unaligned_store(#x, value, usz $alignment) @builtin
{
return $$unaligned_store(&#x, ($typeof(#x))value, $alignment);
}
<*
@param #x "The variable or dereferenced pointer to load."
@return "The value of the variable"
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
*>
macro @volatile_load(#x) @builtin
{
return $$volatile_load(&#x);
}
<*
@param #x "The variable or dereferenced pointer to store to."
@param value "The value to store."
@return "The value stored"
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
@require $defined(#x = value) : "The value doesn't match the variable"
*>
macro @volatile_store(#x, value) @builtin
{
return $$volatile_store(&#x, ($typeof(#x))value);
}
enum AtomicOrdering : int
{
NOT_ATOMIC, // Not atomic
UNORDERED, // No lock
RELAXED, // 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 #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 $defined(&#x) : "This must be a variable or dereferenced pointer"
@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, $ordering.ordinal);
}
<*
@param #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."
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
@require $defined(#x = value) : "The value doesn't match the variable"
*>
macro void @atomic_store(#x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
$$atomic_store(&#x, value, $volatile, $ordering.ordinal);
}
<*
@require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid."
*>
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);
}
<*
@require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid."
*>
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 zero_volatile(char[] data)
{
$$memset(data.ptr, (char)0, data.len, true, (usz)1);
}
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false)
{
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
}
macro void clear_inline(void* dst, usz $len, usz $dst_align = 0, bool $is_volatile = false)
{
$$memset_inline(dst, (char)0, $len, $is_volatile, $dst_align);
}
<*
Copy memory from src to dst efficiently, assuming the memory ranges do not overlap.
@param [&out] dst "The destination to copy to"
@param [&in] src "The source to copy from"
@param len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap"
*>
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
{
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
}
<*
Copy memory from src to dst efficiently, assuming the memory ranges do not overlap, it
will always be inlined and never call memcopy
@param [&out] dst "The destination to copy to"
@param [&in] src "The source to copy from"
@param $len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap"
*>
macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
{
$$memcpy_inline(dst, src, $len, $is_volatile, $dst_align, $src_align);
}
<*
Copy memory from src to dst but correctly handle the possibility of overlapping ranges.
@param [&out] dst "The destination to copy to"
@param [&in] src "The source to copy from"
@param len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
*>
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);
}
<*
Sets all memory in a region to that of the provided byte.
@param [&out] dst "The destination to copy to"
@param val "The value to copy into memory"
@param len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@ensure !len || (dst[0] == val && dst[len - 1] == val)
*>
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false)
{
$$memset(dst, val, len, $is_volatile, $dst_align);
}
<*
Sets all memory in a region to that of the provided byte. Never calls OS memset.
@param [&out] dst "The destination to copy to"
@param val "The value to copy into memory"
@param $len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@ensure !$len || (dst[0] == val && dst[$len - 1] == val)
*>
macro void set_inline(void* dst, char val, usz $len, usz $dst_align = 0, bool $is_volatile = false)
{
$$memset_inline(dst, val, $len, $is_volatile, $dst_align);
}
<*
Test if n elements are equal in a slice, pointed to by a pointer etc.
@require values::@inner_kind(a) == TypeKind.SLICE || values::@inner_kind(a) == TypeKind.POINTER
@require values::@inner_kind(b) == TypeKind.SLICE || values::@inner_kind(b) == TypeKind.POINTER
@require values::@inner_kind(a) != TypeKind.SLICE || len == -1
@require values::@inner_kind(a) != TypeKind.POINTER || len > -1
@require values::@assign_to(a, b) && values::@assign_to(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.SLICE:
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;
}
<*
Check if an allocation must be aligned given the type.
@return `true if the alignment of the type exceeds DEFAULT_MEM_ALIGNMENT.`
*>
macro bool type_alloc_must_be_aligned($Type)
{
return $Type.alignof > DEFAULT_MEM_ALIGNMENT;
}
<*
Run with a specific allocator inside of the macro body.
*>
macro void @scoped(Allocator allocator; @body())
{
Allocator old_allocator = allocator::thread_allocator;
allocator::thread_allocator = allocator;
defer allocator::thread_allocator = old_allocator;
@body();
}
<*
Run the tracking allocator in the scope, then
print out stats.
@param $enabled "Set to false to disable tracking"
*>
macro void @report_heap_allocs_in_scope($enabled = true; @body())
{
$if $enabled:
TrackingAllocator tracker;
tracker.init(allocator::thread_allocator);
Allocator old_allocator = allocator::thread_allocator;
allocator::thread_allocator = &tracker;
defer
{
allocator::thread_allocator = old_allocator;
tracker.print_report();
tracker.free();
}
$endif
@body();
}
<*
Assert on memory leak in the scope of the macro body.
@param $report "Set to false to disable memory report"
*>
macro void @assert_leak($report = true; @body()) @builtin
{
$if env::DEBUG_SYMBOLS || $feature(MEMORY_ASSERTS):
TrackingAllocator tracker;
tracker.init(allocator::thread_allocator);
Allocator old_allocator = allocator::thread_allocator;
allocator::thread_allocator = &tracker;
defer
{
allocator::thread_allocator = old_allocator;
defer tracker.free();
usz allocated = tracker.allocated();
if (allocated)
{
DString report = dstring::new();
defer report.free();
$if $report:
report.append_char('\n');
(void)tracker.fprint_report(&report);
$endif
assert(allocated == 0, "Memory leak detected"
" (%d bytes allocated).%s",
allocated, report.str_view());
}
}
$endif
@body();
}
<*
Allocate [size] bytes on the stack to use for allocation,
with the heap allocator as the backing allocator.
Release everything on scope exit.
@param $size `the size of the buffer`
*>
macro void @stack_mem(usz $size; @body(Allocator mem)) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, allocator::heap());
defer allocator.free();
@body(&allocator);
}
macro void @stack_pool(usz $size; @body) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, allocator::heap());
defer allocator.free();
mem::@scoped(&allocator)
{
@body();
};
}
struct TempState
{
TempAllocator* old;
TempAllocator* current;
usz mark;
}
<*
Push the current temp allocator. A push must always be balanced with a pop using the current state.
*>
fn TempState temp_push(TempAllocator* other = null)
{
TempAllocator* current = allocator::temp();
TempAllocator* old = current;
if (other == current)
{
current = allocator::temp_allocator_next();
}
return { old, current, current.used };
}
<*
Pop the current temp allocator. A pop must always be balanced with a push.
*>
fn void temp_pop(TempState old_state)
{
assert(allocator::thread_temp_allocator == old_state.current, "Tried to pop temp allocators out of order.");
assert(old_state.current.used >= old_state.mark, "Tried to pop temp allocators out of order.");
old_state.current.reset(old_state.mark);
allocator::thread_temp_allocator = old_state.old;
}
<*
@require @is_empty_macro_slot(#other_temp) ||| $assignable(#other_temp, Allocator) "Must be an allocator"
*>
macro void @pool(#other_temp = EMPTY_MACRO_SLOT; @body) @builtin
{
TempAllocator* current = allocator::temp();
$if @is_valid_macro_slot(#other_temp):
TempAllocator* original = current;
if (current == #other_temp.ptr) current = allocator::temp_allocator_next();
$endif
usz mark = current.used;
defer
{
current.reset(mark);
$if @is_valid_macro_slot(#other_temp):
allocator::thread_temp_allocator = original;
$endif;
}
@body();
}
import libc;
module std::core::mem @if(WASM_NOLIBC);
import std::core::mem::allocator @public;
SimpleHeapAllocator wasm_allocator @private;
extern int __heap_base;
fn void initialize_wasm_mem() @init(1024) @private
{
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));
allocator::thread_allocator = &wasm_allocator;
allocator::temp_base_allocator = &wasm_allocator;
allocator::init_default_temp_allocators();
}
module std::core::mem;
macro TrackingEnv* get_tracking_env()
{
$if env::TRACK_MEMORY:
return &&TrackingEnv { $$FILE, $$FUNC, $$LINE };
$else
return null;
$endif
}
macro @clone(value) @builtin @nodiscard
{
return allocator::clone(allocator::heap(), value);
}
macro @tclone(value) @builtin @nodiscard
{
return temp_new($typeof(value), value);
}
fn void* malloc(usz size) @builtin @inline @nodiscard
{
return allocator::malloc(allocator::heap(), size);
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
*>
fn void* malloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
{
return allocator::malloc_aligned(allocator::heap(), size, alignment)!!;
}
fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
{
if (!size) return null;
return allocator::temp().acquire(size, NO_ZERO, alignment)!!;
}
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*>
macro new($Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc($Type.sizeof);
$else
$Type* val = malloc($Type.sizeof);
*val = $vaexpr[0];
return val;
$endif
}
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*>
macro new_with_padding($Type, usz padding, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc($Type.sizeof + padding);
$else
$Type* val = malloc($Type.sizeof + padding);
*val = $vaexpr[0];
return val;
$endif
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro new_aligned($Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof);
$else
$Type* val = malloc_aligned($Type.sizeof, $Type.alignof);
*val = $vaexpr[0];
return val;
$endif
}
<*
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*>
macro alloc($Type) @nodiscard
{
return ($Type*)malloc($Type.sizeof);
}
<*
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*>
macro alloc_with_padding($Type, usz padding) @nodiscard
{
return ($Type*)malloc($Type.sizeof + padding);
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
*>
macro alloc_aligned($Type) @nodiscard
{
return ($Type*)malloc_aligned($Type.sizeof, $Type.alignof);
}
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro temp_new($Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)tcalloc($Type.sizeof) @inline;
$else
$Type* val = tmalloc($Type.sizeof) @inline;
*val = $vaexpr[0];
return val;
$endif
}
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro temp_new_with_padding($Type, usz padding, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)tcalloc($Type.sizeof + padding) @inline;
$else
$Type* val = tmalloc($Type.sizeof + padding) @inline;
*val = $vaexpr[0];
return val;
$endif
}
macro temp_alloc($Type) @nodiscard
{
return tmalloc($Type.sizeof);
}
macro temp_alloc_with_padding($Type, usz padding) @nodiscard
{
return tmalloc($Type.sizeof + padding);
}
<*
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
*>
macro new_array($Type, usz elements) @nodiscard
{
return allocator::new_array(allocator::heap(), $Type, elements);
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
*>
macro new_array_aligned($Type, usz elements) @nodiscard
{
return allocator::new_array_aligned(allocator::heap(), $Type, elements);
}
<*
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
*>
macro alloc_array($Type, usz elements) @nodiscard
{
return allocator::alloc_array(allocator::heap(), $Type, elements);
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
*>
macro alloc_array_aligned($Type, usz elements) @nodiscard
{
return allocator::alloc_array_aligned(allocator::heap(), $Type, elements);
}
macro temp_alloc_array($Type, usz elements) @nodiscard
{
return (($Type*)tmalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
macro temp_new_array($Type, usz elements) @nodiscard
{
return (($Type*)tcalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
fn void* calloc(usz size) @builtin @inline @nodiscard
{
return allocator::calloc(allocator::heap(), size);
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
*>
fn void* calloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
{
return allocator::calloc_aligned(allocator::heap(), size, alignment)!!;
}
fn void* tcalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
{
if (!size) return null;
return allocator::temp().acquire(size, ZERO, alignment)!!;
}
fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
{
return allocator::realloc(allocator::heap(), ptr, new_size);
}
fn void* realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline @nodiscard
{
return allocator::realloc_aligned(allocator::heap(), ptr, new_size, alignment)!!;
}
fn void free(void* ptr) @builtin @inline
{
return allocator::free(allocator::heap(), ptr);
}
fn void free_aligned(void* ptr) @builtin @inline
{
return allocator::free_aligned(allocator::heap(), ptr);
}
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline @nodiscard
{
if (!size) return null;
if (!ptr) return tmalloc(size, alignment);
return allocator::temp().resize(ptr, size, alignment)!!;
}
module std::core::mem @if(env::NO_LIBC);
fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp")
{
char* p1 = s1;
char* p2 = s2;
for (usz i = 0; i < n; i++, p1++, p2++)
{
char c1 = *p1;
char c2 = *p2;
if (c1 < c2) return -1;
if (c1 > c2) return 1;
}
return 0;
}
fn void* __memset(void* str, CInt c, usz n) @weak @export("memset")
{
char* p = str;
char cc = (char)c;
for (usz i = 0; i < n; i++, p++)
{
*p = cc;
}
return str;
}
fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy")
{
char* d = dst;
char* s = src;
for (usz i = 0; i < n; i++, d++, s++)
{
*d = *s;
}
return dst;
}

View File

@@ -0,0 +1,455 @@
module std::core::mem::allocator;
const DEFAULT_SIZE_PREFIX = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
struct TrackingEnv
{
String file;
String function;
uint line;
}
enum AllocInitType
{
NO_ZERO,
ZERO
}
interface Allocator
{
fn void reset(usz mark) @optional;
fn usz mark() @optional;
<*
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require size > 0
*>
fn void*! acquire(usz size, AllocInitType init_type, usz alignment = 0);
<*
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require ptr != null
@require new_size > 0
*>
fn void*! resize(void* ptr, usz new_size, usz alignment = 0);
<*
@require ptr != null
*>
fn void release(void* ptr, bool aligned);
}
def MemoryAllocFn = fn char[]!(usz);
fault AllocationFailure
{
OUT_OF_MEMORY,
CHUNK_TOO_LARGE,
}
fn usz alignment_for_allocation(usz alignment) @inline @private
{
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? mem::DEFAULT_MEM_ALIGNMENT : alignment;
}
macro void* malloc(Allocator allocator, usz size) @nodiscard
{
return malloc_try(allocator, size)!!;
}
macro void*! malloc_try(Allocator allocator, usz size) @nodiscard
{
if (!size) return null;
$if env::TESTING:
char* data = allocator.acquire(size, NO_ZERO)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return allocator.acquire(size, NO_ZERO);
$endif
}
macro void* calloc(Allocator allocator, usz size) @nodiscard
{
return calloc_try(allocator, size)!!;
}
macro void*! calloc_try(Allocator allocator, usz size) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, ZERO);
}
macro void* realloc(Allocator allocator, void* ptr, usz new_size) @nodiscard
{
return realloc_try(allocator, ptr, new_size)!!;
}
macro void*! realloc_try(Allocator allocator, void* ptr, usz new_size) @nodiscard
{
if (!new_size)
{
free(allocator, ptr);
return null;
}
if (!ptr) return allocator.acquire(new_size, NO_ZERO);
return allocator.resize(ptr, new_size);
}
macro void free(Allocator allocator, void* ptr)
{
if (!ptr) return;
$if env::TESTING:
((char*)ptr)[0] = 0xBA;
$endif
allocator.release(ptr, false);
}
macro void*! malloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
{
if (!size) return null;
$if env::TESTING:
char* data = allocator.acquire(size, NO_ZERO, alignment)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return allocator.acquire(size, NO_ZERO, alignment);
$endif
}
macro void*! calloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, ZERO, alignment);
}
macro void*! realloc_aligned(Allocator allocator, void* ptr, usz new_size, usz alignment) @nodiscard
{
if (!new_size)
{
free_aligned(allocator, ptr);
return null;
}
if (!ptr)
{
return malloc_aligned(allocator, new_size, alignment);
}
return allocator.resize(ptr, new_size, alignment);
}
macro void free_aligned(Allocator allocator, void* ptr)
{
if (!ptr) return;
$if env::TESTING:
((char*)ptr)[0] = 0xBA;
$endif
allocator.release(ptr, aligned: true);
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro new(Allocator allocator, $Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc(allocator, $Type.sizeof);
$else
$Type* val = malloc(allocator, $Type.sizeof);
*val = $vaexpr[0];
return val;
$endif
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro new_try(Allocator allocator, $Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc_try(allocator, $Type.sizeof);
$else
$Type* val = malloc_try(allocator, $Type.sizeof)!;
*val = $vaexpr[0];
return val;
$endif
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro new_aligned(Allocator allocator, $Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc_aligned(allocator, $Type.sizeof, $Type.alignof);
$else
$Type* val = malloc_aligned(allocator, $Type.sizeof, $Type.alignof)!;
*val = $vaexpr[0];
return val;
$endif
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT
*>
macro new_with_padding(Allocator allocator, $Type, usz padding) @nodiscard
{
return ($Type*)calloc_try(allocator, $Type.sizeof + padding);
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*>
macro alloc(Allocator allocator, $Type) @nodiscard
{
return ($Type*)malloc(allocator, $Type.sizeof);
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*>
macro alloc_try(Allocator allocator, $Type) @nodiscard
{
return ($Type*)malloc_try(allocator, $Type.sizeof);
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
*>
macro alloc_aligned(Allocator allocator, $Type) @nodiscard
{
return ($Type*)malloc_aligned(allocator, $Type.sizeof, $Type.alignof);
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT
*>
macro alloc_with_padding(Allocator allocator, $Type, usz padding) @nodiscard
{
return ($Type*)malloc_try(allocator, $Type.sizeof + padding);
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
*>
macro new_array(Allocator allocator, $Type, usz elements) @nodiscard
{
return new_array_try(allocator, $Type, elements)!!;
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
*>
macro new_array_try(Allocator allocator, $Type, usz elements) @nodiscard
{
return (($Type*)calloc_try(allocator, $Type.sizeof * elements))[:elements];
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
*>
macro new_array_aligned(Allocator allocator, $Type, usz elements) @nodiscard
{
return (($Type*)calloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
*>
macro alloc_array(Allocator allocator, $Type, usz elements) @nodiscard
{
return alloc_array_try(allocator, $Type, elements)!!;
}
<*
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
*>
macro alloc_array_aligned(Allocator allocator, $Type, usz elements) @nodiscard
{
return (($Type*)malloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
}
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
*>
macro alloc_array_try(Allocator allocator, $Type, usz elements) @nodiscard
{
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
}
macro clone(Allocator allocator, value) @nodiscard
{
return new(allocator, $typeof(value), value);
}
fn any clone_any(Allocator allocator, any value) @nodiscard
{
usz size = value.type.sizeof;
void* data = malloc(allocator, size);
mem::copy(data, value.ptr, size);
return any_make(data, value.type);
}
<*
@require bytes > 0
@require alignment > 0
@require bytes <= isz.max
*>
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
{
if (alignment < void*.alignof) alignment = void*.alignof;
usz header = AlignedBlock.sizeof + alignment;
usz alignsize = bytes + header;
$if @typekind(#alloc_fn(bytes)) == OPTIONAL:
void* data = #alloc_fn(alignsize)!;
$else
void* data = #alloc_fn(alignsize);
$endif
void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
*desc = { bytes, data };
return mem;
}
struct AlignedBlock
{
usz len;
void* start;
}
macro void! @aligned_free(#free_fn, void* old_pointer)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
$if @typekind(#free_fn(desc.start)) == OPTIONAL:
#free_fn(desc.start)!;
$else
#free_fn(desc.start);
$endif
}
<*
@require bytes > 0
@require alignment > 0
*>
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = @aligned_alloc(#calloc_fn, bytes, alignment)!;
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, 1, 1);
$if @typekind(#free_fn(data_start)) == OPTIONAL:
#free_fn(data_start)!;
$else
#free_fn(data_start);
$endif
return new_data;
}
// All allocators
def mem = thread_allocator @builtin;
tlocal Allocator thread_allocator @private = base_allocator();
Allocator temp_base_allocator @private = base_allocator();
tlocal TempAllocator* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private;
macro Allocator base_allocator() @private
{
$if env::LIBC:
return &allocator::LIBC_ALLOCATOR;
$else
return &allocator::NULL_ALLOCATOR;
$endif
}
macro TempAllocator* create_default_sized_temp_allocator(Allocator allocator) @local
{
$switch (env::MEMORY_ENV)
$case NORMAL:
return new_temp_allocator(1024 * 256, allocator)!!;
$case SMALL:
return new_temp_allocator(1024 * 16, allocator)!!;
$case TINY:
return new_temp_allocator(1024 * 2, allocator)!!;
$case NONE:
unreachable("Temp allocator must explicitly created when memory-env is set to 'none'.");
$endswitch
}
macro Allocator heap() => thread_allocator;
macro TempAllocator* temp()
{
if (!thread_temp_allocator)
{
init_default_temp_allocators();
}
return thread_temp_allocator;
}
fn void init_default_temp_allocators() @private
{
temp_allocator_pair[0] = create_default_sized_temp_allocator(temp_base_allocator);
temp_allocator_pair[1] = create_default_sized_temp_allocator(temp_base_allocator);
thread_temp_allocator = temp_allocator_pair[0];
}
fn void destroy_temp_allocators_after_exit() @finalizer(65535) @local @if(env::LIBC)
{
destroy_temp_allocators();
}
<*
Call this to destroy any memory used by the temp allocators. This will invalidate all temp memory.
*>
fn void destroy_temp_allocators()
{
if (!thread_temp_allocator) return;
temp_allocator_pair[0].destroy();
temp_allocator_pair[1].destroy();
temp_allocator_pair[..] = null;
thread_temp_allocator = null;
}
fn TempAllocator* temp_allocator_next() @private
{
if (!thread_temp_allocator)
{
init_default_temp_allocators();
return thread_temp_allocator;
}
usz index = thread_temp_allocator == temp_allocator_pair[0] ? 1 : 0;
return thread_temp_allocator = temp_allocator_pair[index];
}
const NullAllocator NULL_ALLOCATOR = {};
distinct NullAllocator (Allocator) = uptr;
fn void*! NullAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
return AllocationFailure.OUT_OF_MEMORY?;
}
fn void*! NullAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
return AllocationFailure.OUT_OF_MEMORY?;
}
fn void NullAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
}

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(&self, usz bytes)
{
if (!self.allocation)
{
self.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
}
isz bytes_required = bytes + self.use - self.allocation;
if (bytes_required <= 0)
{
defer self.use += bytes;
return ((char*)self.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?;
self.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
defer self.use += bytes;
return ((char*)self.use)[:bytes];
}

View File

@@ -0,0 +1,268 @@
module std::core::cpudetect @if(env::X86 || env::X86_64);
struct CpuId
{
uint eax, ebx, ecx, edx;
}
fn CpuId x86_cpuid(uint eax, uint ecx = 0)
{
int edx;
int ebx;
asm
{
movl $eax, eax;
movl $ecx, ecx;
cpuid;
movl eax, $eax;
movl ebx, $ebx;
movl ecx, $ecx;
movl edx, $edx;
}
return { eax, ebx, ecx, edx };
}
enum X86Feature
{
ADX,
AES,
AMX_AVX512,
AMX_FP8,
AMX_MOVRS,
AMX_TF32,
AMX_TRANSPOSE,
AMX_BF16,
AMX_COMPLEX,
AMX_FP16,
AMX_INT8,
AMX_TILE,
APXF,
AVX,
AVX10_1_256,
AVX10_1_512,
AVX10_2_256,
AVX10_2_512,
AVX2,
AVX5124FMAPS,
AVX5124VNNIW,
AVX512BF16,
AVX512BITALG,
AVX512BW,
AVX512CD,
AVX512DQ,
AVX512ER,
AVX512F,
AVX512FP16,
AVX512IFMA,
AVX512PF,
AVX512VBMI,
AVX512VBMI2,
AVX512VL,
AVX512VNNI,
AVX512VP2INTERSECT,
AVX512VPOPCNTDQ,
AVXIFMA,
AVXNECONVERT,
AVXVNNI,
AVXVNNIINT16,
AVXVNNIINT8,
BMI,
BMI2,
CLDEMOTE,
CLFLUSHOPT,
CLWB,
CLZERO,
CMOV,
CMPCCXADD,
CMPXCHG16B,
CX8,
ENQCMD,
F16C,
FMA,
FMA4,
FSGSBASE,
FXSR,
GFNI,
HRESET,
INVPCID,
KL,
LWP,
LZCNT,
MMX,
MOVBE,
MOVDIR64B,
MOVDIRI,
MOVRS,
MWAITX,
PCLMUL,
PCONFIG,
PKU,
POPCNT,
PREFETCHI,
PREFETCHWT1,
PRFCHW,
PTWRITE,
RAOINT,
RDPID,
RDPRU,
RDRND,
RDSEED,
RTM,
SAHF,
SERIALIZE,
SGX,
SHA,
SHA512,
SHSTK,
SM3,
SM4,
SSE,
SSE2,
SSE3,
SSE4_1,
SSE4_2,
SSE4_A,
SSSE3,
TBM,
TSXLDTRK,
UINTR,
USERMSR,
VAES,
VPCLMULQDQ,
WAITPKG,
WBNOINVD,
WIDEKL,
X87,
XOP,
XSAVE,
XSAVEC,
XSAVEOPT,
XSAVES,
}
uint128 x86_features;
fn void add_feature_if_bit(X86Feature feature, uint register, int bit)
{
if (register & 1U << bit) x86_features |= 1u128 << feature.ordinal;
}
fn void x86_initialize_cpu_features()
{
uint max_level = x86_cpuid(0).eax;
CpuId feat = x86_cpuid(1);
CpuId leaf7 = max_level >= 8 ? x86_cpuid(7) : {};
CpuId leaf7s1 = leaf7.eax >= 1 ? x86_cpuid(7, 1) : {};
CpuId ext1 = x86_cpuid(0x80000000).eax >= 0x80000001 ? x86_cpuid(0x80000001) : {};
CpuId ext8 = x86_cpuid(0x80000000).eax >= 0x80000008 ? x86_cpuid(0x80000008) : {};
CpuId leaf_d = max_level >= 0xd ? x86_cpuid(0xd, 0x1) : {};
CpuId leaf_14 = max_level >= 0x14 ? x86_cpuid(0x14) : {};
CpuId leaf_19 = max_level >= 0x19 ? x86_cpuid(0x19) : {};
CpuId leaf_24 = max_level >= 0x24 ? x86_cpuid(0x24) : {};
add_feature_if_bit(ADX, leaf7.ebx, 19);
add_feature_if_bit(AES, feat.ecx, 25);
add_feature_if_bit(AMX_BF16, leaf7.edx, 22);
add_feature_if_bit(AMX_COMPLEX, leaf7s1.edx, 8);
add_feature_if_bit(AMX_FP16, leaf7s1.eax, 21);
add_feature_if_bit(AMX_INT8, leaf7.edx, 25);
add_feature_if_bit(AMX_TILE, leaf7.edx, 24);
add_feature_if_bit(APXF, leaf7s1.edx, 21);
add_feature_if_bit(AVX, feat.ecx, 28);
add_feature_if_bit(AVX10_1_256, leaf7s1.edx, 19);
add_feature_if_bit(AVX10_1_512, leaf_24.ebx, 18);
add_feature_if_bit(AVX2, leaf7.ebx, 5);
add_feature_if_bit(AVX5124FMAPS, leaf7.edx, 3);
add_feature_if_bit(AVX5124VNNIW, leaf7.edx, 2);
add_feature_if_bit(AVX512BF16, leaf7s1.eax, 5);
add_feature_if_bit(AVX512BITALG, leaf7.ecx, 12);
add_feature_if_bit(AVX512BW, leaf7.ebx, 30);
add_feature_if_bit(AVX512CD, leaf7.ebx, 28);
add_feature_if_bit(AVX512DQ, leaf7.ebx, 17);
add_feature_if_bit(AVX512ER, leaf7.ebx, 27);
add_feature_if_bit(AVX512F, leaf7.ebx, 16);
add_feature_if_bit(AVX512FP16, leaf7.edx, 23);
add_feature_if_bit(AVX512IFMA, leaf7.ebx, 21);
add_feature_if_bit(AVX512PF, leaf7.ebx, 26);
add_feature_if_bit(AVX512VBMI, leaf7.ecx, 1);
add_feature_if_bit(AVX512VBMI2, leaf7.ecx, 6);
add_feature_if_bit(AVX512VL, leaf7.ebx, 31);
add_feature_if_bit(AVX512VNNI, leaf7.ecx, 11);
add_feature_if_bit(AVX512VP2INTERSECT, leaf7.edx, 8);
add_feature_if_bit(AVX512VPOPCNTDQ, leaf7.ecx, 14);
add_feature_if_bit(AVXIFMA, leaf7s1.eax, 23);
add_feature_if_bit(AVXNECONVERT, leaf7s1.edx, 5);
add_feature_if_bit(AVXVNNI, leaf7s1.eax, 4);
add_feature_if_bit(AVXVNNIINT16, leaf7s1.edx, 10);
add_feature_if_bit(AVXVNNIINT8, leaf7s1.edx, 4);
add_feature_if_bit(BMI, leaf7.ebx, 3);
add_feature_if_bit(BMI2, leaf7.ebx, 8);
add_feature_if_bit(CLDEMOTE, leaf7.ecx, 25);
add_feature_if_bit(CLFLUSHOPT, leaf7.ebx, 23);
add_feature_if_bit(CLWB, leaf7.ebx, 24);
add_feature_if_bit(CLZERO, ext8.ecx, 0);
add_feature_if_bit(CMOV, feat.edx, 15);
add_feature_if_bit(CMPCCXADD, leaf7s1.eax, 7);
add_feature_if_bit(CMPXCHG16B, feat.ecx, 12);
add_feature_if_bit(CX8, feat.edx, 8);
add_feature_if_bit(ENQCMD, leaf7.ecx, 29);
add_feature_if_bit(F16C, feat.ecx, 29);
add_feature_if_bit(FMA, feat.ecx, 12);
add_feature_if_bit(FMA4, ext1.ecx, 16);
add_feature_if_bit(FSGSBASE, leaf7.ebx, 0);
add_feature_if_bit(FXSR, feat.edx, 24);
add_feature_if_bit(GFNI, leaf7.ecx, 8);
add_feature_if_bit(HRESET, leaf7s1.eax, 22);
add_feature_if_bit(INVPCID, leaf7.ebx, 10);
add_feature_if_bit(KL, leaf7.ecx, 23);
add_feature_if_bit(LWP, ext1.ecx, 15);
add_feature_if_bit(LZCNT, ext1.ecx, 5);
add_feature_if_bit(MMX, feat.edx, 23);
add_feature_if_bit(MOVBE, feat.ecx, 22);
add_feature_if_bit(MOVDIR64B, leaf7.ecx, 28);
add_feature_if_bit(MOVDIRI, leaf7.ecx, 27);
add_feature_if_bit(MWAITX, ext1.ecx, 29);
add_feature_if_bit(PCLMUL, feat.ecx, 1);
add_feature_if_bit(PCONFIG, leaf7.edx, 18);
add_feature_if_bit(PKU, leaf7.ecx, 4);
add_feature_if_bit(POPCNT, feat.ecx, 23);
add_feature_if_bit(PREFETCHI, leaf7s1.edx, 14);
add_feature_if_bit(PREFETCHWT1, leaf7.ecx, 0);
add_feature_if_bit(PRFCHW, ext1.ecx, 8);
add_feature_if_bit(PTWRITE, leaf_14.ebx, 4);
add_feature_if_bit(RAOINT, leaf7s1.eax, 3);
add_feature_if_bit(RDPID, leaf7.ecx, 22);
add_feature_if_bit(RDPRU, ext8.ecx, 4);
add_feature_if_bit(RDRND, feat.ecx, 30);
add_feature_if_bit(RDSEED, leaf7.ebx, 18);
add_feature_if_bit(RTM, leaf7.ebx, 11);
add_feature_if_bit(SAHF, ext1.ecx, 0);
add_feature_if_bit(SERIALIZE, leaf7.edx, 14);
add_feature_if_bit(SGX, leaf7.ebx, 2);
add_feature_if_bit(SHA, leaf7.ebx, 29);
add_feature_if_bit(SHA512, leaf7s1.eax, 0);
add_feature_if_bit(SHSTK, leaf7.ecx, 7);
add_feature_if_bit(SM3, leaf7s1.eax, 1);
add_feature_if_bit(SM4, leaf7s1.eax, 2);
add_feature_if_bit(SSE, feat.edx, 25);
add_feature_if_bit(SSE2, feat.edx, 26);
add_feature_if_bit(SSE3, feat.ecx, 0);
add_feature_if_bit(SSE4_1, feat.ecx, 19);
add_feature_if_bit(SSE4_2, feat.ecx, 20);
add_feature_if_bit(SSE4_A, ext1.ecx, 6);
add_feature_if_bit(SSSE3, feat.ecx, 9);
add_feature_if_bit(TBM, ext1.ecx, 21);
add_feature_if_bit(TSXLDTRK, leaf7.edx, 16);
add_feature_if_bit(UINTR, leaf7.edx, 5);
add_feature_if_bit(USERMSR, leaf7s1.edx, 15);
add_feature_if_bit(VAES, leaf7.ecx, 9);
add_feature_if_bit(VPCLMULQDQ, leaf7.ecx, 10);
add_feature_if_bit(WAITPKG, leaf7.ecx, 5);
add_feature_if_bit(WBNOINVD, ext8.ecx, 9);
add_feature_if_bit(WIDEKL, leaf_19.ebx, 2);
add_feature_if_bit(X87, feat.edx, 0);
add_feature_if_bit(XOP, ext1.ecx, 11);
add_feature_if_bit(XSAVE, feat.ecx, 26);
add_feature_if_bit(XSAVEC, leaf_d.eax, 1);
add_feature_if_bit(XSAVEOPT, leaf_d.eax, 0);
add_feature_if_bit(XSAVES, leaf_d.eax, 3);
}

View File

@@ -0,0 +1,254 @@
module std::core::machoruntime @if(env::DARWIN) @private;
struct SegmentCommand64
{
uint cmd;
uint cmdsize;
char[16] segname;
ulong vmaddr;
ulong vmsize;
ulong fileoff;
ulong filesize;
uint maxprot;
uint initprot;
uint nsects;
uint flags;
}
struct LoadCommand
{
uint cmd;
uint cmdsize;
}
struct Section64
{
char[16] sectname;
char[16] segname;
ulong addr;
ulong size;
uint offset;
uint align;
uint reloff;
uint nreloc;
uint flags;
uint reserved1;
uint reserved2;
uint reserved3;
}
struct MachHeader
{
uint magic;
uint cputype;
uint cpusubtype;
uint filetype;
uint ncmds;
uint sizeofcmds;
uint flags;
}
struct MachHeader64
{
inline MachHeader header;
uint reserved;
}
const LC_SEGMENT_64 = 0x19;
fault MachoSearch
{
NOT_FOUND
}
fn bool name_cmp(char* a, char[16]* b)
{
for (usz i = 0; i < 16; i++)
{
if (a[i] != (*b)[i]) return false;
if (a[i] == '\0') return true;
}
return false;
}
fn SegmentCommand64*! find_segment(MachHeader* header, char* segname)
{
LoadCommand* command = (void*)header + MachHeader64.sizeof;
for (uint i = 0; i < header.ncmds; i++)
{
if (command.cmd == LC_SEGMENT_64)
{
SegmentCommand64* segment = (SegmentCommand64*)command;
if (name_cmp(segname, &segment.segname)) return segment;
}
command = (void*)command + command.cmdsize;
}
return MachoSearch.NOT_FOUND?;
}
fn Section64*! find_section(SegmentCommand64* command, char* sectname)
{
Section64* section = (void*)command + SegmentCommand64.sizeof;
for (uint i = 0; i < command.nsects; i++)
{
if (name_cmp(sectname, &section.sectname)) return section;
section++;
}
return MachoSearch.NOT_FOUND?;
}
macro find_segment_section_body(MachHeader* header, char* segname, char* sectname, $Type)
{
Section64*! section = find_section(find_segment(header, segname), sectname);
if (catch section)
{
return ($Type[]){};
}
$Type* ptr = (void*)header + section.offset;
return ptr[:section.size / $Type.sizeof];
}
def DyldCallback = fn void (MachHeader* mh, isz vmaddr_slide);
extern fn void _dyld_register_func_for_add_image(DyldCallback);
struct DlInfo
{
char* dli_fname;
void* dli_fbase;
char* dli_sname;
void* dli_saddr;
}
extern fn void printf(char*, ...);
extern fn int dladdr(MachHeader* mh, DlInfo* dlinfo);
extern fn void* realloc(void* ptr, usz size);
extern fn void* malloc(usz size);
extern fn void free(void* ptr);
def CallbackFn = fn void();
struct Callback
{
uint priority;
CallbackFn xtor;
Callback* next;
}
struct DynamicMethod
{
void* fn_ptr;
char* sel;
union
{
DynamicMethod* next;
TypeId* type;
}
}
enum StartupState
{
NOT_STARTED,
INIT,
RUN_CTORS,
READ_DYLIB,
RUN_DYLIB_CTORS,
RUN_DTORS,
SHUTDOWN
}
StartupState runtime_state = NOT_STARTED;
Callback* ctor_first;
Callback* dtor_first;
fn void runtime_startup() @public @export("__c3_runtime_startup")
{
if (runtime_state != NOT_STARTED) return;
runtime_state = INIT;
_dyld_register_func_for_add_image(&dl_reg_callback);
assert(runtime_state == INIT);
runtime_state = RUN_CTORS;
Callback* ctor = ctor_first;
while (ctor)
{
ctor.xtor();
ctor = ctor.next;
}
assert(runtime_state == RUN_CTORS);
runtime_state = READ_DYLIB;
ctor_first = null;
}
fn void runtime_finalize() @public @export("__c3_runtime_finalize")
{
if (runtime_state != READ_DYLIB) return;
runtime_state = RUN_DTORS;
Callback* dtor = dtor_first;
while (dtor)
{
dtor.xtor();
dtor = dtor.next;
}
assert(runtime_state == RUN_DTORS);
runtime_state = SHUTDOWN;
}
fn void append_xxlizer(Callback** ref, Callback* cb)
{
while (Callback* current = *ref, current)
{
if (current.priority > cb.priority)
{
cb.next = current;
break;
}
ref = &current.next;
}
*ref = cb;
}
struct TypeId
{
char type;
TypeId* parentof;
DynamicMethod* dtable;
usz sizeof;
TypeId* inner;
usz len;
typeid[?] additional;
}
fn void dl_reg_callback(MachHeader* mh, isz vmaddr_slide)
{
usz size = 0;
assert(runtime_state == INIT || runtime_state == READ_DYLIB, "State was %s", runtime_state);
foreach (&dm : find_segment_section_body(mh, "__DATA", "__c3_dynamic", DynamicMethod))
{
TypeId* type = dm.type;
dm.next = type.dtable;
type.dtable = dm;
DynamicMethod* m = dm;
while (m)
{
m = m.next;
}
}
foreach (&cb : find_segment_section_body(mh, "__DATA", "__c3dtor", Callback))
{
append_xxlizer(&dtor_first, cb);
}
foreach (&cb : find_segment_section_body(mh, "__DATA", "__c3ctor", Callback))
{
append_xxlizer(&ctor_first, cb);
}
if (runtime_state != READ_DYLIB) return;
runtime_state = RUN_DYLIB_CTORS;
Callback* ctor = ctor_first;
ctor_first = null;
while (ctor)
{
ctor.xtor();
ctor = ctor.next;
}
assert(runtime_state == RUN_DYLIB_CTORS);
runtime_state = READ_DYLIB;
}

View File

@@ -0,0 +1,180 @@
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 = mem::alloc_array(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_runner(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
return #m(list) ? 0 : 1;
}
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;
}
module std::core::main_stub @if(env::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 = mem::alloc_array(String, argc);
for (int i = 0; i < argc; i++)
{
Char16* arg = argv[i];
Char16[] argstring = arg[:_strlen(arg)];
list[i] = string::new_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, void* prev_handle, Char16* cmd_line, int show_cmd)
{
if (catch #m()) return 1;
return 0;
}
macro int @win_to_int_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) => #m();
macro int @win_to_void_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
#m();
return 0;
}
macro int @win_to_err_main_args(#m, void* handle, void* prev_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, void* prev_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, void* prev_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, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
if (catch #m(handle, prev_handle, args, show_cmd)) return 1;
return 0;
}
macro int @win_to_int_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(handle, prev_handle, args, show_cmd);
}
macro int @win_to_void_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(handle, prev_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_runner(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
return #m(args) ? 0 : 1;
}
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;
}

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

@@ -0,0 +1,33 @@
// 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;
import libc, std::time, std::io, std::sort;
struct ReflectedParam (Printable) @if(!$defined(ReflectedParam))
{
String name;
typeid type;
}
struct AnyRaw
{
void* ptr;
typeid type;
}
struct SliceRaw
{
void* ptr;
usz len;
}
module std::core::runtime @if(WASM_NOLIBC);
extern fn void __wasm_call_ctors();
fn void wasm_initialize() @extern("_initialize") @wasm
{
// The linker synthesizes this to call constructors.
__wasm_call_ctors();
}

View File

@@ -0,0 +1,174 @@
module std::core::runtime;
import libc, std::time, std::io, std::sort;
def BenchmarkFn = fn void!() @if($$OLD_TEST);
def BenchmarkFn = fn void() @if(!$$OLD_TEST);
struct BenchmarkUnit
{
String name;
BenchmarkFn func;
}
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
{
BenchmarkFn[] fns = $$BENCHMARK_FNS;
String[] names = $$BENCHMARK_NAMES;
BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, BenchmarkUnit, names.len);
foreach (i, benchmark : fns)
{
benchmarks[i] = { names[i], fns[i] };
}
return benchmarks;
}
const DEFAULT_BENCHMARK_WARMUP_ITERATIONS = 3;
const DEFAULT_BENCHMARK_MAX_ITERATIONS = 10000;
uint benchmark_warmup_iterations @private = DEFAULT_BENCHMARK_WARMUP_ITERATIONS;
uint benchmark_max_iterations @private = DEFAULT_BENCHMARK_MAX_ITERATIONS;
fn void set_benchmark_warmup_iterations(uint value) @builtin
{
benchmark_warmup_iterations = value;
}
fn void set_benchmark_max_iterations(uint value) @builtin
{
assert(value > 0);
benchmark_max_iterations = value;
}
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if($$OLD_TEST)
{
int benchmarks_passed = 0;
int benchmark_count = benchmarks.len;
usz max_name;
foreach (&unit : benchmarks)
{
if (max_name < unit.name.len) max_name = unit.name.len;
}
usz len = max_name + 9;
DString name = dstring::temp_with_capacity(64);
name.append_repeat('-', len / 2);
name.append(" BENCHMARKS ");
name.append_repeat('-', len - len / 2);
io::printn(name);
name.clear();
long sys_clock_started;
long sys_clock_finished;
long sys_clocks;
Clock clock;
anyfault err;
foreach(unit : benchmarks)
{
defer name.clear();
name.appendf("Benchmarking %s ", unit.name);
name.append_repeat('.', max_name - unit.name.len + 2);
io::printf("%s ", name.str_view());
for (uint i = 0; i < benchmark_warmup_iterations; i++)
{
err = @catch(unit.func()) @inline;
@volatile_load(err);
}
clock = std::time::clock::now();
sys_clock_started = $$sysclock();
for (uint i = 0; i < benchmark_max_iterations; i++)
{
err = @catch(unit.func()) @inline;
@volatile_load(err);
}
sys_clock_finished = $$sysclock();
NanoDuration nano_seconds = clock.mark();
sys_clocks = sys_clock_finished - sys_clock_started;
if (err)
{
io::printfn("[failed] Failed due to: %s", err);
continue;
}
io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
benchmarks_passed++;
}
io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : "");
io::printfn("Benchmarks Result: %s. %d passed, %d failed.",
benchmarks_passed < benchmark_count ? "FAILED" : "ok",
benchmarks_passed,
benchmark_count - benchmarks_passed);
return benchmark_count == benchmarks_passed;
}
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
{
usz max_name;
foreach (&unit : benchmarks)
{
if (max_name < unit.name.len) max_name = unit.name.len;
}
usz len = max_name + 9;
DString name = dstring::temp_with_capacity(64);
name.append_repeat('-', len / 2);
name.append(" BENCHMARKS ");
name.append_repeat('-', len - len / 2);
io::printn(name);
name.clear();
long sys_clock_started;
long sys_clock_finished;
long sys_clocks;
Clock clock;
foreach(unit : benchmarks)
{
defer name.clear();
name.appendf("Benchmarking %s ", unit.name);
name.append_repeat('.', max_name - unit.name.len + 2);
io::printf("%s ", name.str_view());
for (uint i = 0; i < benchmark_warmup_iterations; i++)
{
unit.func() @inline;
}
clock = std::time::clock::now();
sys_clock_started = $$sysclock();
for (uint i = 0; i < benchmark_max_iterations; i++)
{
unit.func() @inline;
}
sys_clock_finished = $$sysclock();
NanoDuration nano_seconds = clock.mark();
sys_clocks = sys_clock_finished - sys_clock_started;
io::printfn("[COMPLETE] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
}
io::printfn("\n%d benchmark%s run.\n", benchmarks.len, benchmarks.len > 1 ? "s" : "");
return true;
}
fn bool default_benchmark_runner(String[] args) => @pool()
{
return run_benchmarks(benchmark_collection_create(allocator::temp()));
}

View File

@@ -0,0 +1,342 @@
// Copyright (c) 2025 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;
import std::core::test @public;
import std::core::mem::allocator @public;
import libc, std::time, std::io, std::sort;
import std::os::env;
def TestFn = fn void!() @if($$OLD_TEST);
def TestFn = fn void() @if(!$$OLD_TEST);
TestContext* test_context @private;
struct TestContext
{
JmpBuf buf;
// Allows filtering test cased or modules by substring, e.g. 'foo::', 'foo::test_add'
String test_filter;
// Triggers debugger breakpoint when assert or test:: checks failed
bool breakpoint_on_assert;
// internal state
bool assert_print_backtrace;
bool has_ansi_codes;
bool is_in_panic;
bool is_quiet_mode;
bool is_no_capture;
String current_test_name;
TestFn setup_fn;
TestFn teardown_fn;
char* error_buffer;
usz error_buffer_capacity;
File fake_stdout;
struct stored
{
File stdout;
File stderr;
Allocator allocator;
}
}
struct TestUnit
{
String name;
TestFn func;
}
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
{
TestFn[] fns = $$TEST_FNS;
String[] names = $$TEST_NAMES;
TestUnit[] tests = allocator::alloc_array(allocator, TestUnit, names.len);
foreach (i, test : fns)
{
tests[i] = { names[i], fns[i] };
}
return tests;
}
// Sort the tests by their name in ascending order.
fn int cmp_test_unit(TestUnit a, TestUnit b)
{
usz an = a.name.len;
usz bn = b.name.len;
if (an > bn) @swap(a, b);
foreach (i, ac : a.name)
{
char bc = b.name[i];
if (ac != bc) return an > bn ? bc - ac : ac - bc;
}
return (int)(an - bn);
}
fn bool terminal_has_ansi_codes() @local => @pool()
{
if (try v = env::get_var_temp("TERM"))
{
if (v.contains("xterm") || v.contains("vt100") || v.contains("screen")) return true;
}
$if env::WIN32 || env::NO_LIBC:
return false;
$else
return io::stdout().isatty();
$endif
}
fn void test_panic(String message, String file, String function, uint line) @local
{
if (test_context.is_in_panic) return;
test_context.is_in_panic = true;
unmute_output(true);
(void)io::stdout().flush();
if (test_context.assert_print_backtrace)
{
$if env::NATIVE_STACKTRACE:
builtin::print_backtrace(message, 0);
$endif
}
io::printf("\nTest failed ^^^ ( %s:%s ) %s\n", file, line, message);
test_context.assert_print_backtrace = true;
if (test_context.breakpoint_on_assert)
{
breakpoint();
}
if (test_context.teardown_fn)
{
test_context.teardown_fn();
}
test_context.is_in_panic = false;
allocator::thread_allocator = test_context.stored.allocator;
libc::longjmp(&test_context.buf, 1);
}
fn void mute_output() @local
{
if (test_context.is_no_capture || !test_context.fake_stdout.file) return;
File* stdout = io::stdout();
File* stderr = io::stderr();
*stderr = test_context.fake_stdout;
*stdout = test_context.fake_stdout;
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
}
fn void unmute_output(bool has_error) @local
{
if (test_context.is_no_capture || !test_context.fake_stdout.file) return;
File* stdout = io::stdout();
File* stderr = io::stderr();
*stderr = test_context.stored.stderr;
*stdout = test_context.stored.stdout;
usz log_size = test_context.fake_stdout.seek(0, Seek.CURSOR)!!;
if (has_error)
{
io::printf("\nTesting %s ", test_context.current_test_name);
io::printn(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
}
if (has_error && log_size > 0)
{
test_context.fake_stdout.write_byte('\n')!!;
test_context.fake_stdout.write_byte('\0')!!;
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
io::printfn("\n========== TEST LOG ============");
io::printfn("%s\n", test_context.current_test_name);
while (try c = test_context.fake_stdout.read_byte())
{
if (@unlikely(c == '\0'))
{
// ignore junk from previous tests
break;
}
libc::putchar(c);
}
io::printf("========== TEST END ============");
}
(void)stdout.flush();
}
fn bool run_tests(String[] args, TestUnit[] tests) @private
{
usz max_name;
bool sort_tests = true;
bool check_leaks = true;
foreach (&unit : tests)
{
if (max_name < unit.name.len) max_name = unit.name.len;
}
TestContext context =
{
.assert_print_backtrace = true,
.breakpoint_on_assert = false,
.test_filter = "",
.has_ansi_codes = terminal_has_ansi_codes(),
.stored.allocator = allocator::heap(),
.stored.stderr = *io::stderr(),
.stored.stdout = *io::stdout(),
};
for (int i = 1; i < args.len; i++)
{
switch (args[i])
{
case "--test-breakpoint":
context.breakpoint_on_assert = true;
case "--test-nosort":
sort_tests = false;
case "--test-noleak":
check_leaks = false;
case "--test-nocapture":
context.is_no_capture = true;
case "--noansi":
context.has_ansi_codes = false;
case "--useansi":
context.has_ansi_codes = true;
case "--test-quiet":
context.is_quiet_mode = true;
case "--test-filter":
if (i == args.len - 1)
{
io::printn("Invalid arguments to test runner.");
return false;
}
context.test_filter = args[i + 1];
i++;
default:
io::printfn("Unknown argument: %s", args[i]);
}
}
test_context = &context;
if (sort_tests)
{
quicksort(tests, &cmp_test_unit);
}
// Buffer for hijacking the output
$if (!env::NO_LIBC):
context.fake_stdout.file = libc::tmpfile();
$endif
if (context.fake_stdout.file == null)
{
io::print("Failed to hijack stdout, tests will print everything");
}
PanicFn old_panic = builtin::panic;
defer builtin::panic = old_panic;
builtin::panic = &test_panic;
int tests_passed = 0;
int tests_skipped = 0;
int test_count = tests.len;
DString name = dstring::temp_with_capacity(64);
usz len = max_name + 9;
name.append_repeat('-', len / 2);
name.append(" TESTS ");
name.append_repeat('-', len - len / 2);
if (!context.is_quiet_mode) io::printn(name);
name.clear();
TempState temp_state = mem::temp_push();
defer mem::temp_pop(temp_state);
foreach(unit : tests)
{
mem::temp_pop(temp_state);
if (context.test_filter && !unit.name.contains(context.test_filter))
{
tests_skipped++;
continue;
}
context.setup_fn = null;
context.teardown_fn = null;
context.current_test_name = unit.name;
defer name.clear();
name.appendf("Testing %s ", unit.name);
name.append_repeat('.', max_name - unit.name.len + 2);
if (context.is_quiet_mode)
{
io::print(".");
}
else
{
io::printf("%s ", name.str_view());
}
(void)io::stdout().flush();
TrackingAllocator mem;
mem.init(context.stored.allocator);
if (libc::setjmp(&context.buf) == 0)
{
mute_output();
mem.clear();
if (check_leaks) allocator::thread_allocator = &mem;
$if(!$$OLD_TEST):
unit.func();
$else
if (catch err = unit.func())
{
io::printf("[FAIL] Failed due to: %s", err);
continue;
}
$endif
// track cleanup that may take place in teardown_fn
if (context.teardown_fn)
{
context.teardown_fn();
}
if (check_leaks) allocator::thread_allocator = context.stored.allocator;
unmute_output(false); // all good, discard output
if (mem.has_leaks())
{
if(context.is_quiet_mode) io::printf("\n%s ", context.current_test_name);
io::print(context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
io::printn(" LEAKS DETECTED!");
mem.print_report();
}
else
{
if (!context.is_quiet_mode)
{
io::printfn(context.has_ansi_codes ? "[\e[0;32mPASS\e[0m]" : "[PASS]");
}
tests_passed++;
}
}
mem.free();
}
io::printfn("\n%d test%s run.\n", test_count-tests_skipped, test_count > 1 ? "s" : "");
int n_failed = test_count - tests_passed - tests_skipped;
io::printf("Test Result: %s%s%s: ",
context.has_ansi_codes ? (n_failed ? "\e[0;31m" : "\e[0;32m") : "",
n_failed ? "FAILED" : "PASSED",
context.has_ansi_codes ? "\e[0m" : "",
);
io::printfn("%d passed, %d failed, %d skipped.",
tests_passed,
n_failed,
tests_skipped);
// cleanup fake_stdout file
if (context.fake_stdout.file) libc::fclose(context.fake_stdout.file);
context.fake_stdout.file = null;
return n_failed == 0;
}
fn bool default_test_runner(String[] args) => @pool()
{
assert(test_context == null, "test suite is already running");
return run_tests(args, test_collection_create(allocator::temp()));
}

View File

@@ -0,0 +1,127 @@
// Add this to your code to suppress leak detection or set other default options
// fn ZString __asan_default_options() @export("__asan_default_options") @if(env::ADDRESS_SANITIZER)
// {
// return "detect_leaks=0";
// }
// Add this to break on error
// asan::set_error_report_callback(fn (ZString err)
// {
// breakpoint();
// });
module std::core::sanitizer::asan;
def ErrorCallback = fn void (ZString);
<*
Marks a memory region ([addr, addr+size)) as unaddressable.
This memory must be previously allocated by your program. Instrumented
code is forbidden from accessing addresses in this region until it is
unpoisoned. This function is not guaranteed to poison the entire region -
it could poison only a subregion of [addr, addr+size) due to ASan
alignment restrictions.
NOTE This function is not thread-safe because no two threads can poison or
unpoison memory in the same memory region simultaneously.
@param addr "Start of memory region."
@param size "Size of memory region."
*>
macro poison_memory_region(void* addr, usz size)
{
$if env::ADDRESS_SANITIZER:
__asan_poison_memory_region(addr, size);
$endif
}
<*
Marks a memory region ([addr, addr+size)) as addressable.
This memory must be previously allocated by your program. Accessing
addresses in this region is allowed until this region is poisoned again.
This function could unpoison a super-region of [addr, addr+size) due
to ASan alignment restrictions.
NOTE This function is not thread-safe because no two threads can
poison or unpoison memory in the same memory region simultaneously.
@param addr "Start of memory region."
@param size "Size of memory region."
*>
macro unpoison_memory_region(void* addr, usz size)
{
$if env::ADDRESS_SANITIZER:
__asan_unpoison_memory_region(addr, size);
$endif
}
<*
Checks if an address is poisoned.
@return "True if 'addr' is poisoned (that is, 1-byte read/write access to this address would result in an error report from ASan). Otherwise returns false."
@param addr "Address to check."
*>
macro bool address_is_poisoned(void* addr)
{
$if env::ADDRESS_SANITIZER:
return (bool)__asan_address_is_poisoned(addr);
$else
return false;
$endif
}
<*
Checks if a region is poisoned.
If at least one byte in [beg, beg+size) is poisoned, returns the
address of the first such byte. Otherwise returns 0.
@param beg "Start of memory region."
@param size "Start of memory region."
@return "Address of first poisoned byte."
*>
macro void* region_is_poisoned(void* beg, usz size)
{
$if env::ADDRESS_SANITIZER:
return __asan_region_is_poisoned(beg, size);
$else
return null;
$endif
}
<*
Sets the callback function to be called during ASan error reporting.
*>
fn void set_error_report_callback(ErrorCallback callback)
{
$if env::ADDRESS_SANITIZER:
__asan_set_error_report_callback(callback);
$endif
}
module std::core::sanitizer::asan @if(env::ADDRESS_SANITIZER);
extern fn void __asan_poison_memory_region(void* addr, usz size);
extern fn void __asan_unpoison_memory_region(void* addr, usz size);
extern fn CInt __asan_address_is_poisoned(void* addr);
extern fn void* __asan_region_is_poisoned(void* beg, usz size);
extern fn void __asan_describe_address(void* addr);
extern fn CInt __asan_report_present();
extern fn void* __asan_get_report_pc();
extern fn void* __asan_get_report_bp();
extern fn void* __asan_get_report_sp();
extern fn void* __asan_get_report_address();
extern fn CInt __asan_get_report_access_type();
extern fn usz __asan_get_report_access_size();
extern fn ZString __asan_get_report_description();
extern fn ZString __asan_locate_address(void* addr, char* name, usz name_size, void** region_address, usz* region_size);
extern fn usz __asan_get_alloc_stack(void* addr, void** trace, usz size, CInt* thread_id);
extern fn usz __asan_get_free_stack(void* addr, void** trace, usz size, CInt* thread_id);
extern fn void __asan_get_shadow_mapping(usz* shadow_scale, usz* shadow_offset);
extern fn void __asan_set_error_report_callback(ErrorCallback callback);
extern fn void __asan_print_accumulated_stats();
extern fn void* __asan_get_current_fake_stack();
extern fn void* __asan_addr_is_in_fake_stack(void* fake_stack, void* addr, void** beg, void** end);
extern fn void __asan_handle_no_return();
extern fn CInt __asan_update_allocation_context(void* addr);

View File

@@ -0,0 +1,80 @@
module std::core::sanitizer;
macro void annotate_contiguous_container(void* beg, void* end, void* old_mid, void* new_mid)
{
$if env::ADDRESS_SANITIZER:
__sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid);
$endif
}
macro void annotate_double_ended_contiguous_container(void* storage_beg, void* storage_end, void* old_container_beg, void* old_container_end, void* new_container_beg, void* new_container_end)
{
$if env::ADDRESS_SANITIZER:
__sanitizer_annotate_double_ended_contiguous_container(storage_beg, storage_end, old_container_beg, old_container_end, new_container_beg, new_container_end);
$endif
}
macro void print_stack_trace()
{
$if env::ADDRESS_SANITIZER:
__sanitizer_print_stack_trace();
$endif
}
fn void set_death_callback(VoidFn callback)
{
$if env::ANY_SANITIZER:
__sanitizer_set_death_callback(callback);
$endif
}
module std::core::sanitizer @if (env::ANY_SANITIZER);
struct __Sanitizer_sandbox_arguments
{
CInt coverage_sandboxed;
iptr coverage_fd;
CUInt coverage_max_block_size;
}
extern fn void __sanitizer_set_report_path(ZString path);
extern fn void __sanitizer_set_report_fd(void* fd);
extern fn ZString __sanitizer_get_report_path();
extern fn void __sanitizer_sandbox_on_notify(__Sanitizer_sandbox_arguments* args);
extern fn void __sanitizer_report_error_summary(ZString error_summary);
extern fn ushort __sanitizer_unaligned_load16(void* p);
extern fn uint __sanitizer_unaligned_load32(void* p);
extern fn ulong __sanitizer_unaligned_load64(void* p);
extern fn void __sanitizer_unaligned_store16(void* p, ushort x);
extern fn void __sanitizer_unaligned_store32(void* p, uint x);
extern fn void __sanitizer_unaligned_store64(void* p, ulong x);
extern fn CInt __sanitizer_acquire_crash_state();
extern fn void __sanitizer_annotate_contiguous_container(void* beg, void* end, void* old_mid, void* new_mid);
extern fn void __sanitizer_annotate_double_ended_contiguous_container(void* storage_beg, void* storage_end,
void* old_container_beg, void* old_container_end,
void* new_container_beg, void* new_container_end);
extern fn CInt __sanitizer_verify_contiguous_container(void* beg, void* mid, void* end);
extern fn CInt __sanitizer_verify_double_ended_contiguous_container(
void* storage_beg, void* container_beg,
void* container_end, void* storage_end);
extern fn void* __sanitizer_contiguous_container_find_bad_address(void* beg, void* mid, void* end);
extern fn void* __sanitizer_double_ended_contiguous_container_find_bad_address(
void* storage_beg, void* container_beg,
void* container_end, void* storage_end);
extern fn void __sanitizer_print_stack_trace();
extern fn void __sanitizer_symbolize_pc(void* pc, ZString fmt, char* out_buf, usz out_buf_size);
extern fn void __sanitizer_symbolize_global(void* data_ptr, ZString fmt, char* out_buf, usz out_buf_size);
extern fn void __sanitizer_set_death_callback(VoidFn callback);
extern fn void __sanitizer_weak_hook_memcmp(void* called_pc, void* s1, void* s2, usz n, CInt result);
extern fn void __sanitizer_weak_hook_strncmp(void* called_pc, ZString s1, ZString s2, usz n, CInt result);
extern fn void __sanitizer_weak_hook_strncasecmp(void* called_pc, ZString s1, ZString s2, usz n, CInt result);
extern fn void __sanitizer_weak_hook_strcmp(void* called_pc, ZString s1, ZString s2, CInt result);
extern fn void __sanitizer_weak_hook_strcasecmp(void* called_pc, ZString s1, ZString s2, CInt result);
extern fn void __sanitizer_weak_hook_strstr(void* called_pc, ZString s1, ZString s2, char* result);
extern fn void __sanitizer_weak_hook_strcasestr(void* called_pc, ZString s1, ZString s2, char* result);
extern fn void __sanitizer_weak_hook_memmem(void* called_pc, void* s1, usz len1, void* s2, usz len2, void* result);
extern fn void __sanitizer_print_memory_profile(usz top_percent, usz max_number_of_contexts);
extern fn void __sanitizer_start_switch_fiber(void** fake_stack_save, void* bottom, usz size);
extern fn void __sanitizer_finish_switch_fiber(void* fake_stack_save, void** bottom_old, usz* size_old);
extern fn CInt __sanitizer_get_module_and_offset_for_pc(void* pc, char* module_path, usz module_path_len, void** pc_offset);

View File

@@ -0,0 +1,39 @@
module std::core::sanitizer::tsan;
distinct MutexFlags = inline CUInt;
const MutexFlags MUTEX_LINKER_INIT = 1 << 0;
const MutexFlags MUTEX_WRITE_REENTRANT = 1 << 1;
const MutexFlags MUTEX_READ_REENTRANT = 1 << 2;
const MutexFlags MUTEX_NOT_STATIC = 1 << 8;
const MutexFlags MUTEX_READ_LOCK = 1 << 3;
const MutexFlags MUTEX_TRY_LOCK = 1 << 4;
const MutexFlags MUTEX_TRY_LOCK_FAILED = 1 << 5;
const MutexFlags MUTEX_RECURSIVE_LOCK = 1 << 6;
const MutexFlags MUTEX_RECURSIVE_UNLOCK = 1 << 7;
const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK;
const MutexFlags MUTEX_TRY_READ_LOCK_FAILED = MUTEX_TRY_READ_LOCK | MUTEX_TRY_LOCK_FAILED;
macro void mutex_create(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_create(addr, flags); $endif }
macro void mutex_destroy(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_destroy(addr, flags); $endif }
macro void mutex_pre_lock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_lock(addr, flags); $endif }
macro void mutex_post_lock(void* addr, MutexFlags flags, CInt recursion) { $if env::THREAD_SANITIZER: __tsan_mutex_post_lock(addr, flags, recursion); $endif }
macro CInt mutex_pre_unlock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: return __tsan_mutex_pre_unlock(addr, flags); $else return 0; $endif }
macro void mutex_post_unlock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_unlock(addr, flags); $endif }
macro void mutex_pre_signal(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_signal(addr, flags); $endif }
macro void mutex_post_signal(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_signal(addr, flags); $endif }
macro void mutex_pre_divert(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_divert(addr, flags); $endif }
macro void mutex_post_divert(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_divert(addr, flags); $endif }
module std::core::sanitizer::tsan @if(env::THREAD_SANITIZER) @private;
extern fn void __tsan_mutex_create(void* addr, CUInt flags);
extern fn void __tsan_mutex_destroy(void* addr, CUInt flags);
extern fn void __tsan_mutex_pre_lock(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_lock(void* addr, CUInt flags, CInt recursion);
extern fn CInt __tsan_mutex_pre_unlock(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_unlock(void* addr, CUInt flags);
extern fn void __tsan_mutex_pre_signal(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_signal(void* addr, CUInt flags);
extern fn void __tsan_mutex_pre_divert(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_divert(void* addr, CUInt flags);

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

@@ -0,0 +1,879 @@
module std::core::string;
import std::ascii;
import std::io;
distinct String @if(!$defined(String)) = inline char[];
distinct ZString = inline char*;
distinct WString = inline Char16*;
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,
}
<*
Return a temporary ZString created using the formatting function.
@param [in] fmt `The formatting string`
*>
fn ZString tformat_zstr(String fmt, args...)
{
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.zstr_view();
}
<*
Return a new String created using the formatting function.
@param [inout] allocator `The allocator to use`
@param [in] fmt `The formatting string`
*>
fn String format(String fmt, args..., Allocator allocator) => @pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.copy_str(allocator);
}
<*
Return a heap allocated String created using the formatting function.
@param [in] fmt `The formatting string`
*>
fn String new_format(String fmt, args..., Allocator allocator = null) => format(fmt, ...args, allocator: allocator ?: allocator::heap());
<*
Return a temporary String created using the formatting function.
@param [in] fmt `The formatting string`
*>
fn String tformat(String fmt, args...)
{
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.str_view();
}
<*
Return a new ZString created using the formatting function.
@param [in] fmt `The formatting string`
@param [inout] allocator `The allocator to use`
*>
fn ZString new_format_zstr(String fmt, args..., Allocator allocator = allocator::heap()) => @pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.copy_zstr(allocator);
}
<*
Check if a character is in a set.
@param c `the character to check`
@param [in] set `The formatting string`
@pure
@return `True if a character is in the set`
*>
macro bool char_in_set(char c, String set)
{
foreach (ch : set) if (ch == c) return true;
return false;
}
fn String join_new(String[] s, String joiner, Allocator allocator = allocator::heap())
{
if (!s)
{
return (String)allocator::new_array(allocator, char, 2)[:0];
}
usz total_size = joiner.len * s.len;
foreach (String* &str : s)
{
total_size += str.len;
}
@pool(allocator)
{
DString res = dstring::temp_with_capacity(total_size);
res.append(s[0]);
foreach (String* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res.copy_str(allocator);
};
}
<*
Remove characters from the front and end of a string.
@param [in] string `The string to trim`
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
@pure
@return `a substring of the string passed in`
*>
fn String String.trim(string, String to_trim = "\t\n\r ")
{
return string.trim_left(to_trim).trim_right(to_trim);
}
<*
Remove characters from the front of a string.
@param [in] string `The string to trim`
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
@pure
@return `a substring of the string passed in`
*>
fn String String.trim_left(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];
return string[start..];
}
<*
Remove characters from the end of a string.
@param [in] string `The string to trim`
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
@pure
@return `a substring of the string passed in`
*>
fn String String.trim_right(string, String to_trim = "\t\n\r ")
{
usz len = string.len;
while (len > 0 && char_in_set(string[len - 1], to_trim)) len--;
return string[:len];
}
<*
Check if the String starts with the needle.
@param [in] string
@param [in] needle
@pure
@return `'true' if the string starts with the needle`
*>
fn bool String.starts_with(string, String needle)
{
if (needle.len > string.len) return false;
if (!needle.len) return true;
return string[:needle.len] == needle;
}
<*
Check if the String ends with the needle.
@param [in] string
@param [in] needle
@pure
@return `'true' if the string ends with the needle`
*>
fn bool String.ends_with(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
@pure
@return `the substring with the prefix removed`
*>
fn String String.strip(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
@pure
@return `the substring with the suffix removed`
*>
fn String String.strip_end(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 max "Max number of elements, 0 means no limit, defaults to 0"
@param skip_empty "True to skip empty elements"
@param [&inout] allocator "The allocator to use for the String[]"
@require needle.len > 0 "The needle must be at least 1 character long"
@ensure return.len > 0
*>
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap(), bool skip_empty = false)
{
usz capacity = 16;
usz i = 0;
String* holder = allocator::alloc_array(allocator, String, capacity);
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 (!res.len && skip_empty)
{
continue;
}
if (i == capacity)
{
capacity *= 2;
holder = allocator::realloc(allocator, holder, String.sizeof * capacity);
}
holder[i++] = res;
}
return holder[:i];
}
<*
Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }, using the heap allocator
to store the parts.
@param [in] s
@param [in] needle
@param max "Max number of elements, 0 means no limit, defaults to 0"
@param skip_empty "True to skip empty elements"
@require needle.len > 0 "The needle must be at least 1 character long"
@ensure return.len > 0
*>
fn String[] String.new_split(s, String needle, usz max = 0, bool skip_empty) => s.split(needle, max, allocator::heap(), skip_empty) @inline;
<*
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"
@param skip_empty "True to skip empty elements"
*>
fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(needle, max, allocator::temp(), skip_empty) @inline;
fault SplitResult { BUFFER_EXCEEDED }
<*
Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
@param [in] s
@param [in] needle
@param [inout] buffer
@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
@return! SplitResult.BUFFER_EXCEEDED `If there are more elements than would fit the buffer`
*>
fn String[]! String.split_to_buffer(s, String needle, String[] buffer, usz max = 0, bool skip_empty = false)
{
usz max_capacity = buffer.len;
usz i = 0;
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 (!res.len && skip_empty)
{
continue;
}
if (i == max_capacity)
{
return SplitResult.BUFFER_EXCEEDED?;
}
buffer[i++] = res;
}
return buffer[:i];
}
<*
Check if a substring is found in the string.
@param [in] s
@param [in] needle "The string to look for."
@pure
@return "true if the string contains the substring, false otherwise"
*>
fn bool String.contains(s, String needle)
{
return @ok(s.index_of(needle));
}
<*
Find the index of the first incidence of a string.
@param [in] s
@param needle "The character to look for"
@pure
@ensure return < s.len
@return "the index of the needle"
@return! SearchResult.MISSING "if the needle cannot be found"
*>
fn usz! String.index_of_char(s, char needle)
{
foreach (i, c : s)
{
if (c == needle) return i;
}
return SearchResult.MISSING?;
}
<*
Find the index of the first incidence of a one of the chars.
@param [in] s
@param [in] needle "The characters to look for"
@pure
@ensure return < s.len
@return "the index of the needle"
@return! SearchResult.MISSING "if the needle cannot be found"
*>
fn usz! String.index_of_chars(String s, char[] needle)
{
foreach (i, c : s)
{
foreach (j, pin : needle)
{
if (c == pin) return i;
}
}
return SearchResult.MISSING?;
}
<*
Find the index of the first incidence of a character.
@param [in] s
@param needle "The character to look for"
@param start_index "The index to start with, may exceed max index."
@pure
@ensure return < s.len
@return "the index of the needle"
@return! SearchResult.MISSING "if the needle cannot be found starting from the start_index"
*>
fn usz! String.index_of_char_from(s, char needle, usz start_index)
{
usz len = s.len;
if (len <= start_index) return SearchResult.MISSING?;
for (usz i = start_index; i < len; i++)
{
if (s[i] == needle) return i;
}
return SearchResult.MISSING?;
}
<*
Find the index of the first incidence of a character starting from the end.
@param [in] s
@param needle "the character to find"
@pure
@ensure return < s.len
@return "the index of the needle"
@return! SearchResult.MISSING "if the needle cannot be found"
*>
fn usz! String.rindex_of_char(s, char needle)
{
foreach_r (i, c : s)
{
if (c == needle) return i;
}
return SearchResult.MISSING?;
}
<*
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(s, String needle)
{
usz needed = needle.len;
if (needed > 0 && s.len >= needed)
{
char first = needle[0];
foreach (i, c: s[..^needed])
{
if (c == first && s[i:needed] == needle) return i;
}
}
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(s, String needle)
{
usz needed = needle.len;
if (needed > 0 && s.len >= needed)
{
char first = needle[0];
foreach_r (i, c: s[..^needed])
{
if (c == first && s[i:needed] == needle) return i;
}
}
return SearchResult.MISSING?;
}
fn String ZString.str_view(str)
{
return (String)(str[:str.len()]);
}
fn usz ZString.char_len(str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0])
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
fn usz ZString.len(str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0]) len++;
return len;
}
fn ZString String.zstr_copy(s, Allocator allocator = allocator::heap())
{
usz len = s.len;
char* str = allocator::malloc(allocator, len + 1);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn String String.concat(s1, String s2, Allocator allocator = allocator::heap())
{
usz full_len = s1.len + s2.len;
char* str = allocator::malloc(allocator, full_len + 1);
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(s1, String s2) => s1.concat(s2, allocator::temp());
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
<*
Copy this string, by duplicating the string, always adding a zero byte
sentinel, so that it safely can be converted to a ZString by a
cast.
*>
fn String String.copy(s, Allocator allocator = allocator::heap())
{
usz len = s.len;
char* str = allocator::malloc(allocator, len + 1);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (String)str[:len];
}
fn void String.free(&s, Allocator allocator = allocator::heap())
{
if (!s.ptr) return;
allocator::free(allocator, s.ptr);
*s = "";
}
fn String String.tcopy(s) => s.copy(allocator::temp()) @inline;
fn String ZString.copy(z, Allocator allocator = allocator::heap())
{
return z.str_view().copy(allocator) @inline;
}
fn String ZString.tcopy(z)
{
return z.str_view().copy(allocator::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_new_utf16(s, Allocator allocator = allocator::heap())
{
usz len16 = conv::utf16len_for_utf8(s);
Char16* data = allocator::alloc_array_try(allocator, Char16, len16 + 1)!;
conv::utf8to16_unsafe(s, data)!;
data[len16] = 0;
return data[:len16];
}
<*
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_temp_utf16(s)
{
return s.to_new_utf16(allocator::temp());
}
fn WString! String.to_wstring(s, Allocator allocator)
{
return (WString)s.to_new_utf16(allocator).ptr;
}
fn WString! String.to_temp_wstring(s) => s.to_wstring(allocator::temp());
fn WString! String.to_new_wstring(s) => s.to_wstring(allocator::heap());
fn Char32[]! String.to_utf32(s, Allocator allocator)
{
usz codepoints = conv::utf8_codepoints(s);
Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!;
conv::utf8to32_unsafe(s, data)!;
data[codepoints] = 0;
return data[:codepoints];
}
fn Char32[]! String.to_new_utf32(s) => s.to_utf32(allocator::heap()) @inline;
fn Char32[]! String.to_temp_utf32(s) => s.to_utf32(allocator::temp()) @inline;
<*
Convert a string to ASCII lower case.
@param [inout] s
@pure
*>
fn void String.convert_ascii_to_lower(s)
{
foreach (&c : s) if (c.is_upper() @pure) *c += 'a' - 'A';
}
fn String String.new_ascii_to_lower(s, Allocator allocator = allocator::heap())
{
String copy = s.copy(allocator);
copy.convert_ascii_to_lower();
return copy;
}
fn String String.temp_ascii_to_lower(s)
{
return s.new_ascii_to_lower(allocator::temp());
}
<*
Convert a string to ASCII upper case.
@param [inout] s
@pure
*>
fn void String.convert_ascii_to_upper(s)
{
foreach (&c : s) if (c.is_lower() @pure) *c -= 'a' - 'A';
}
<*
Returns a string converted to ASCII upper case.
@param [in] s
@param [inout] allocator
@return `a new String converted to ASCII upper case.`
*>
fn String String.new_ascii_to_upper(s, Allocator allocator = allocator::heap())
{
String copy = s.copy(allocator);
copy.convert_ascii_to_upper();
return copy;
}
fn StringIterator String.iterator(s)
{
return { s, 0 };
}
<*
@param [in] s
@return `a temporary String converted to ASCII upper case.`
*>
fn String String.temp_ascii_to_upper(s)
{
return s.new_ascii_to_upper(allocator::temp());
}
fn String! new_from_utf32(Char32[] utf32, Allocator allocator = allocator::heap())
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = allocator::malloc_try(allocator, len + 1)!;
defer catch allocator::free(allocator, data);
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return (String)data[:len];
}
fn String! new_from_utf16(Char16[] utf16, Allocator allocator = allocator::heap())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = allocator::malloc_try(allocator, len + 1)!;
defer catch allocator::free(allocator, data);
conv::utf16to8_unsafe(utf16, data)!;
data[len] = 0;
return (String)data[:len];
}
fn String! new_from_wstring(WString wstring, Allocator allocator = allocator::heap())
{
usz utf16_len;
while (wstring[utf16_len] != 0) utf16_len++;
Char16[] utf16 = wstring[:utf16_len];
return new_from_utf16(utf16, allocator);
}
fn String! temp_from_wstring(WString wstring) => new_from_wstring(wstring, allocator::temp()) @inline;
fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(utf16, allocator::temp()) @inline;
fn usz String.utf8_codepoints(s)
{
usz len = 0;
foreach (char c : s)
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
<*
@require (base <= 10 && base > 1) || base == 16 : "Unsupported base"
*>
macro String.to_integer(string, $Type, int base = 10)
{
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_used = ($Type)base;
if (string[index] == '0' && base == 10)
{
index++;
if (index == len) return ($Type)0;
switch (string[index])
{
case 'x':
case 'X':
base_used = 16;
index++;
case 'b':
case 'B':
base_used = 2;
index++;
case 'o':
case 'O':
base_used = 8;
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
}
$Type value = 0;
while (index != len)
{
char c = string[index++];
switch
{
case base_used != 16 || c < 'A': c -= '0';
case c <= 'F': c -= 'A' - 10;
case c < 'a' || c > 'f': return NumberConversion.MALFORMED_INTEGER?;
default: c -= 'a' - 10;
}
if (c >= base_used) return NumberConversion.MALFORMED_INTEGER?;
do
{
if (is_negative)
{
$Type new_value = value * base_used - c;
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
value = new_value;
break;
}
$Type new_value = value * base_used + c;
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
value = new_value;
};
}
return value;
}
fn int128! String.to_int128(s, int base = 10) => s.to_integer(int128, base);
fn long! String.to_long(s, int base = 10) => s.to_integer(long, base);
fn int! String.to_int(s, int base = 10) => s.to_integer(int, base);
fn short! String.to_short(s, int base = 10) => s.to_integer(short, base);
fn ichar! String.to_ichar(s, int base = 10) => s.to_integer(ichar, base);
fn uint128! String.to_uint128(s, int base = 10) => s.to_integer(uint128, base);
fn ulong! String.to_ulong(s, int base = 10) => s.to_integer(ulong, base);
fn uint! String.to_uint(s, int base = 10) => s.to_integer(uint, base);
fn ushort! String.to_ushort(s, int base = 10) => s.to_integer(ushort, base);
fn char! String.to_uchar(s, int base = 10) => s.to_integer(char, base);
fn double! String.to_double(s) => s.to_real(double);
fn float! String.to_float(s) => s.to_real(float);
fn Splitter String.splitter(self, String split)
{
return { .string = self, .split = split };
}
fn Splitter String.tokenize(self, String split)
{
return { .string = self, .split = split, .tokenize = true };
}
struct Splitter
{
String string;
String split;
usz current;
bool tokenize;
int last_index;
}
fn void Splitter.reset(&self)
{
self.current = 0;
}
fn String! Splitter.next(&self)
{
while (true)
{
usz len = self.string.len;
usz current = self.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
String remaining = self.string[current..];
usz! next = remaining.index_of(self.split);
if (try next)
{
self.current = current + next + self.split.len;
if (!next && self.tokenize) continue;
return remaining[:next];
}
self.current = len;
return remaining;
}
}
macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
{
DString s;
@stack_mem(512; Allocator mem)
{
s.init(mem);
io::fprint(&s, x)!!;
return s.copy_str(allocator);
};
}
macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp());

View File

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

View File

@@ -0,0 +1,484 @@
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 = c > '9' ? ((c | 32) + 10 - 'a') : (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(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);
}

223
lib/std/core/test.c3 Normal file
View File

@@ -0,0 +1,223 @@
<*
Unit test module
This module provides a toolset of macros for running unit test checks
Example:
```c3
module sample::m;
import std::io;
fault MathError
{
DIVISION_BY_ZERO
}
fn double! divide(int a, int b)
{
if (b == 0) return MathError.DIVISION_BY_ZERO?;
return (double)(a) / (double)(b);
}
fn void! test_div() @test
{
test::eq(2, divide(6, 3)!);
test::ne(1, 2);
test::ge(3, 3);
test::gt(2, divide(3, 3)!);
test::lt(2, 3);
test::le(2, 3);
test::eq_approx(m::divide(1, 3)!, 0.333, places: 3);
test::@check(2 == 2, "divide: %d", divide(6, 3)!);
test::@error(m::divide(3, 0), MathError.DIVISION_BY_ZERO);
}
```
*>
// Copyright (c) 2025 Alex Veden <i@alexveden.com>. 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::test;
import std::core::runtime @public;
import std::math, std::io, libc;
<*
Initializes test case context.
@param setup_fn `initializer function for test case`
@param teardown_fn `cleanup function for test context (may be null)`
@require runtime::test_context != null "Only allowed in @test functions"
@require setup_fn != null "setup_fn must always be set"
*>
macro @setup(TestFn setup_fn, TestFn teardown_fn = null)
{
runtime::test_context.setup_fn = setup_fn;
runtime::test_context.teardown_fn = teardown_fn;
runtime::test_context.setup_fn();
}
<*
Checks condition and fails assertion if not true
@param #condition `any boolean condition, will be expanded by text`
@param format `printf compatible format`
@param args `vargs for format`
@require runtime::test_context != null "Only allowed in @test functions"
*>
macro @check(#condition, String format = "", args...)
{
if (!#condition)
{
@stack_mem(512; Allocator allocator)
{
DString s;
s.init(allocator);
s.appendf("check `%s` failed. ", $stringify(#condition));
s.appendf(format, ...args);
print_panicf(s.str_view());
};
}
}
<*
Check if function returns specific error
@param #funcresult `result of function execution`
@param error_expected `expected error of function execution`
@require runtime::test_context != null "Only allowed in @test functions"
*>
macro @error(#funcresult, anyfault error_expected)
{
if (catch err = #funcresult)
{
if (err != error_expected)
{
print_panicf("`%s` expected to return error [%s], got [%s]",
$stringify(#funcresult), error_expected, err);
}
return;
}
print_panicf("`%s` error [%s] was not returned.", $stringify(#funcresult), error_expected);
}
<*
Check if left == right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
*>
macro eq(left, right)
{
if (!equals(left, right))
{
print_panicf("`%s` != `%s`", left, right);
}
}
<*
Check left floating point value is approximately equals to right value
@param places `number of decimal places to compare (default: 7)`
@param delta `minimal allowed difference (overrides places parameter)`
@param equal_nan `allows comparing nan values, if left and right both nans result is ok`
@require places > 0, places <= 20 "too many decimal places"
@require delta >= 0, delta <= 1 "delta must be a small number"
@require runtime::test_context != null "Only allowed in @test functions"
*>
macro void eq_approx(double left, double right, uint places = 7, double delta = 0, bool equal_nan = true)
{
double diff = left - right;
double eps = delta;
if (eps == 0) eps = 1.0 / math::pow(10.0, places);
if (!math::is_approx(left, right, eps))
{
if (equal_nan && math::is_nan(left) && math::is_nan(right)) return;
print_panicf("Not almost equal: `%s` !~~ `%s` delta=%e diff: %e", left, right, eps, diff);
}
}
<*
Check if left != right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
*>
macro void ne(left, right)
{
if (equals(left, right))
{
print_panicf("`%s` == `%s`", left, right);
}
}
<*
Check if left > right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
*>
macro gt(left, right)
{
if (!builtin::greater(left, right))
{
print_panicf("`%s` <= `%s`", left, right);
}
}
<*
Check if left >= right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
*>
macro ge(left, right)
{
if (!builtin::greater_eq(left, right))
{
print_panicf("`%s` < `%s`", left, right);
}
}
<*
Check if left < right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
*>
macro lt(left, right)
{
if (!builtin::less(left, right))
{
print_panicf("`%s` >= `%s`", left, right);
}
}
<*
Check if left <= right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
*>
macro le(left, right)
{
if (!builtin::less_eq(left, right))
{
print_panicf("`%s` > `%s`", left, right);
}
}
macro void print_panicf(format, ...) @local
{
runtime::test_context.assert_print_backtrace = false;
builtin::panicf(format, $$FILE, $$FUNC, $$LINE, $vasplat);
}

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

@@ -0,0 +1,380 @@
module std::core::types;
import libc;
fault ConversionResult
{
VALUE_OUT_OF_RANGE,
VALUE_OUT_OF_UNSIGNED_RANGE,
}
<*
@require $Type.kindof.is_int() "Type was not an integer"
@require v.type.kindof == ENUM "Value was not an enum"
*>
macro any_to_enum_ordinal(any v, $Type)
{
return any_to_int(v.as_inner(), $Type);
}
<*
@require $Type.kindof.is_int() "Type was not an integer"
@require v.type.kindof.is_int() "Value was not an integer"
*>
macro any_to_int(any v, $Type)
{
typeid any_type = v.type;
TypeKind 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();
}
}
fn bool typeid.is_subtype_of(self, typeid other)
{
while (self != void.typeid)
{
if (self == other) return true;
self = self.parentof;
}
return false;
}
macro bool is_subtype_of($Type, $OtherType)
{
var $typeid = $Type.typeid;
$switch ($Type)
$case $OtherType: return true;
$default: return false;
$endswitch
}
macro bool is_numerical($Type)
{
var $kind = $Type.kindof;
$if $kind == TypeKind.DISTINCT:
return is_numerical($typefrom($Type.inner));
$else
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|| $kind == TypeKind.VECTOR;
$endif
}
fn bool TypeKind.is_int(kind) @inline
{
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
}
macro bool is_slice_convertable($Type)
{
$switch ($Type.kindof)
$case SLICE:
return true;
$case POINTER:
return $Type.inner.kindof == TypeKind.ARRAY;
$default:
return false;
$endswitch
}
macro bool is_bool($Type) @const => $Type.kindof == TypeKind.BOOL;
macro bool is_int($Type) @const => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
<*
@require is_numerical($Type) "Expected a numerical type"
*>
macro bool is_signed($Type) @const
{
$switch (inner_kind($Type))
$case SIGNED_INT:
$case FLOAT:
return true;
$case VECTOR:
return is_signed($typefrom($Type.inner));
$default:
return false;
$endswitch
}
<*
@require is_numerical($Type) "Expected a numerical type"
*>
macro bool is_unsigned($Type) @const
{
$switch (inner_kind($Type))
$case UNSIGNED_INT:
return true;
$case VECTOR:
return is_unsigned($typefrom($Type.inner));
$default:
return false;
$endswitch
}
macro bool is_indexable($Type) @const
{
return $defined(($Type){}[0]);
}
macro bool is_ref_indexable($Type) @const
{
return $defined(&($Type){}[0]);
}
macro bool is_intlike($Type) @const
{
$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_underlying_int($Type) @const
{
$switch ($Type.kindof)
$case SIGNED_INT:
$case UNSIGNED_INT:
return true;
$case DISTINCT:
return is_underlying_int($typefrom($Type.inner));
$default:
return false;
$endswitch
}
macro bool is_float($Type) @const => $Type.kindof == TypeKind.FLOAT;
macro bool is_floatlike($Type) @const
{
$switch ($Type.kindof)
$case FLOAT:
return true;
$case VECTOR:
return $Type.inner.kindof == TypeKind.FLOAT;
$default:
return false;
$endswitch
}
macro bool is_vector($Type) @const
{
return $Type.kindof == TypeKind.VECTOR;
}
macro typeid inner_type($Type) @const
{
$if $Type.kindof == TypeKind.DISTINCT:
return inner_type($typefrom($Type.inner));
$else
return $Type.typeid;
$endif
}
macro TypeKind inner_kind($Type) @const
{
return inner_type($Type).kindof;
}
macro bool is_same($TypeA, $TypeB) @const
{
return $TypeA.typeid == $TypeB.typeid;
}
macro bool @has_same(#a, #b, ...) @const
{
var $type_a = @typeid(#a);
$if $type_a != @typeid(#b):
return false;
$endif
$for (var $i = 0; $i < $vacount; $i++)
$if @typeid($vaexpr[$i]) != $type_a:
return false;
$endif
$endfor
return true;
}
macro bool may_load_atomic($Type) @const
{
$switch ($Type.kindof)
$case BOOL:
$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 lower_to_atomic_compatible_type($Type) @const
{
$switch ($Type.kindof)
$case SIGNED_INT:
$case UNSIGNED_INT:
return $Type.typeid;
$case DISTINCT:
return lower_to_atomic_compatible_type($Type.inner);
$case FLOAT:
$switch ($Type)
$case float16:
return ushort.typeid;
$case float:
return uint.typeid;
$case double:
return ulong.typeid;
$case float128:
return uint128.typeid;
$default:
return void.typeid;
$endswitch
$default:
return void.typeid;
$endswitch
}
macro bool is_promotable_to_floatlike($Type) @const => types::is_floatlike($Type) || types::is_int($Type);
macro bool is_promotable_to_float($Type) @const => types::is_float($Type) || types::is_int($Type);
macro bool is_same_vector_type($Type1, $Type2) @const
{
$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) @const
{
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
return true;
$else
return $Type.is_eq;
$endif
}
<*
Checks if a type implements the copy protocol.
*>
macro bool implements_copy($Type) @const
{
return $defined($Type.copy) && $defined($Type.free);
}
macro bool is_equatable_value(value) @deprecated
{
return is_equatable_type($typeof(value));
}
macro bool @equatable_value(#value) @const
{
return is_equatable_type($typeof(#value));
}
macro bool @comparable_value(#value) @const
{
$if $defined(#value.less) || $defined(#value.compare_to):
return true;
$else
return $typeof(#value).is_ordered;
$endif
}
macro bool is_comparable_value(value) @deprecated
{
$if $defined(value.less) || $defined(value.compare_to):
return true;
$else
return $typeof(value).is_ordered;
$endif
}
enum TypeKind : char
{
VOID,
BOOL,
SIGNED_INT,
UNSIGNED_INT,
FLOAT,
TYPEID,
ANYFAULT,
ANY,
ENUM,
FAULT,
STRUCT,
UNION,
BITSTRUCT,
FUNC,
OPTIONAL,
ARRAY,
SLICE,
VECTOR,
DISTINCT,
POINTER,
INTERFACE,
}
struct TypeEnum
{
TypeKind type;
usz elements;
}

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

@@ -0,0 +1,66 @@
module std::core::values;
macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid;
macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof;
macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $Type.typeid;
<*
Return true if two values have the same type before any conversions.
*>
macro bool @is_same_type(#value1, #value2) @const => $typeof(#value1).typeid == $typeof(#value2).typeid;
macro bool @is_bool(#value) @const => types::is_bool($typeof(#value));
macro bool @is_int(#value) @const => types::is_int($typeof(#value));
macro bool @is_floatlike(#value) @const => types::is_floatlike($typeof(#value));
macro bool @is_float(#value) @const => types::is_float($typeof(#value));
macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to_floatlike($typeof(#value));
macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_float($typeof(#value));
macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
macro bool @is_lvalue(#value) => $defined(#value = #value);
macro promote_int(x)
{
$if @is_int(x):
return (double)x;
$else
return x;
$endif
}
<*
Select between two values at compile time,
the values do not have to be of the same type.
This acts like `$bool ? #value_1 : #value_2` but at compile time.
@param $bool `true for picking the first value, false for the other`
@param #value_1
@param #value_2
@returns `The selected value.`
*>
macro @select(bool $bool, #value_1, #value_2) @builtin
{
$if $bool:
return #value_1;
$else
return #value_2;
$endif
}
macro promote_int_same(x, y)
{
$if @is_int(x):
$switch
$case @is_vector(y) &&& $typeof(y).inner == float.typeid:
return (float)x;
$case $typeof(y).typeid == float.typeid:
return (float)x;
$default:
return (double)x;
$endswitch
$else
return x;
$endif
}
macro TypeKind @inner_kind(#value) @const => types::inner_kind($typeof(#value));

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

@@ -0,0 +1,12 @@
module std::crypto;
fn bool safe_compare(void* data1, void* data2, usz len)
{
char match = 0;
for (usz i = 0; i < len; i++)
{
match = match | (mem::@volatile_load(((char*)data1)[i]) ^ mem::@volatile_load(((char*)data2)[i]));
}
return match == 0;
}

12
lib/std/crypto/dh.c3 Normal file
View File

@@ -0,0 +1,12 @@
module std::crypto::dh;
import std::math::bigint;
fn BigInt generate_secret(BigInt p, BigInt x, BigInt y)
{
return y.mod_pow(x, p);
}
fn BigInt public_key(BigInt p, BigInt g, BigInt x)
{
return g.mod_pow(x, p);
}

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

@@ -0,0 +1,75 @@
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 [in] key "The key to use"
@require key.len > 0 "The key must be at least 1 byte long"
*>
fn void Rc4.init(&self, char[] key)
{
// Init the state matrix
foreach (char i, &c : self.state) *c = i;
for (int i = 0, int j = 0; i < 256; i++)
{
j = (j + self.state[i] + key[i % key.len]) & 0xFF;
@swap(self.state[i], self.state[j]);
}
self.i = 0;
self.j = 0;
}
<*
Run a single pass of en/decryption using a particular key.
@param [in] key
@param [inout] data
*>
fn void crypt(char[] key, char[] data)
{
Rc4 rc4;
rc4.init(key);
rc4.crypt(data, data);
}
<*
Encrypt or decrypt a sequence of bytes.
@param [in] in "The input"
@param [out] out "The output"
@require in.len <= out.len "Output would overflow"
*>
fn void Rc4.crypt(&self, char[] in, char[] out)
{
uint i = self.i;
uint j = self.j;
char* state = &self.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];
}
self.i = i;
self.j = j;
}
<*
Clear the rc4 state.
@param [&out] self "The RC4 State"
*>
fn void Rc4.destroy(&self)
{
*self = {};
}

406
lib/std/encoding/base32.c3 Normal file
View File

@@ -0,0 +1,406 @@
module std::encoding::base32;
// This module implements base32 encoding according to RFC 4648
// (https://www.rfc-editor.org/rfc/rfc4648)
struct Base32Alphabet
{
char[32] encoding;
char[256] reverse;
}
const char NO_PAD = 0;
const char DEFAULT_PAD = '=';
<*
Encode the content of src into a newly allocated string
@param [in] src "The input to be encoded."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@return "The encoded string."
*>
fn String! encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
return encode_buffer(src, dst, padding, alphabet);
}
<*
Decode the content of src into a newly allocated char array.
@param [in] src "The input to be encoded."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@return "The decoded data."
*>
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding));
return decode_buffer(src, dst, padding, alphabet);
}
fn String! encode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
fn String! encode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
<*
Calculate the length in bytes of the decoded data.
@param n "Length in bytes of input."
@param padding "The padding character or 0 if none"
@require padding < 0xFF "Invalid padding character"
@return "Length in bytes of the decoded data."
*>
fn usz decode_len(usz n, char padding)
{
if (padding) return (n / 8) * 5;
// no padding
usz trailing = n % 8;
return n / 8 * 5 + (trailing * 5 ) / 8;
}
<*
Calculate the length in bytes of the encoded data.
@param n "Length in bytes on input."
@param padding "The padding character or 0 if none"
@require padding < 0xFF "Invalid padding character"
@return "Length in bytes of the encoded data."
*>
fn usz encode_len(usz n, char padding)
{
// A character is encoded into 8 x 5-bit blocks.
if (padding) return (n + 4) / 5 * 8;
// no padding
usz trailing = n % 5;
return n / 5 * 8 + (trailing * 8 + 4) / 5;
}
<*
Decode the content of src into dst, which must be properly sized.
@param src "The input to be decoded."
@param dst "The decoded input."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@require dst.len >= decode_len(src.len, padding) "Destination buffer too small"
@return "The resulting dst buffer"
@return! DecodingFailure
*>
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
{
if (src.len == 0) return dst[:0];
char* dst_ptr = dst;
usz dn = decode_len(src.len, padding);
usz n;
char[8] buf;
while (src.len > 0 && dst.len > 0)
{
usz i @noinit;
// load 8 bytes into buffer
for (i = 0; i < 8; i++)
{
if (src.len == 0)
{
if (padding > 0) return DecodingFailure.INVALID_PADDING?;
break;
}
if (src[0] == padding) break;
buf[i] = alphabet.reverse[src[0]];
if (buf[i] == INVALID) return DecodingFailure.INVALID_CHARACTER?;
src = src[1..];
}
// extract 5-bytes from the buffer which contains 8 x 5 bit chunks
switch (i)
{
case 8:
// |66677777| dst[4]
// | 77777| buf[7]
// |666 | buf[6] << 5
dst[4] = buf[7] | buf[6] << 5;
n++;
nextcase 7;
case 7:
// |45555566| dst[3]
// | 66| buf[6] >> 3
// | 55555 | buf[5] << 2
// |4 | buf[4] << 7
dst[3] = buf[6] >> 3 | buf[5] << 2 | buf[4] << 7;
n++;
nextcase 5;
case 5:
// |33334444| dst[2]
// | 4444| buf[4] >> 1
// |3333 | buf[3] << 4
dst[2] = buf[4] >> 1 | buf[3] << 4;
n++;
nextcase 4;
case 4:
// |11222223| dst[1]
// | 3| buf[3] >> 4
// | 22222 | buf[2] << 1
// |11 | buf[1] << 6
dst[1] = buf[3] >> 4 | buf[2] << 1 | buf[1] << 6;
n++;
nextcase 2;
case 2:
// |00000111| dst[0]
// | 111| buf[1] >> 2
// |00000 | buf[0] << 3
dst[0] = buf[1] >> 2 | buf[0] << 3;
n++;
default:
return DecodingFailure.INVALID_CHARACTER?;
}
if (dst.len < 5) break;
dst = dst[5..];
}
return dst_ptr[:n];
}
<*
Encode the content of src into dst, which must be properly sized.
@param [in] src "The input to be encoded."
@param [inout] dst "The encoded input."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@require dst.len >= encode_len(src.len, padding) "Destination buffer too small"
@return "The encoded size."
*>
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
{
if (src.len == 0) return (String)dst[:0];
char* dst_ptr = dst;
usz n = (src.len / 5) * 5;
usz dn = encode_len(src.len, padding);
uint msb, lsb;
for (usz i = 0; i < n; i += 5)
{
// to fit 40 bits we need two 32-bit uints
msb = (uint)src[i] << 24 | (uint)src[i+1] << 16
| (uint)src[i+2] << 8 | (uint)src[i+3];
lsb = msb << 8 | (uint)src[i+4];
// now slice them into 5-bit chunks and translate to the
// alphabet.
dst[0] = alphabet.encoding[(msb >> 27) & MASK];
dst[1] = alphabet.encoding[(msb >> 22) & MASK];
dst[2] = alphabet.encoding[(msb >> 17) & MASK];
dst[3] = alphabet.encoding[(msb >> 12) & MASK];
dst[4] = alphabet.encoding[(msb >> 7) & MASK];
dst[5] = alphabet.encoding[(msb >> 2) & MASK];
dst[6] = alphabet.encoding[(lsb >> 5) & MASK];
dst[7] = alphabet.encoding[lsb & MASK];
dst = dst[8..];
}
usz trailing = src.len - n;
if (trailing == 0) return (String)dst_ptr[:dn];
msb = 0;
switch (trailing)
{
case 4:
msb |= (uint)src[n+3];
lsb = msb << 8;
dst[6] = alphabet.encoding[(lsb >> 5) & MASK];
dst[5] = alphabet.encoding[(msb >> 2) & MASK];
nextcase 3;
case 3:
msb |= (uint)src[n+2] << 8;
dst[4] = alphabet.encoding[(msb >> 7) & MASK];
nextcase 2;
case 2:
msb |= (uint)src[n+1] << 16;
dst[3] = alphabet.encoding[(msb >> 12) & MASK];
dst[2] = alphabet.encoding[(msb >> 17) & MASK];
nextcase 1;
case 1:
msb |= (uint)src[n] << 24;
dst[1] = alphabet.encoding[(msb >> 22) & MASK];
dst[0] = alphabet.encoding[(msb >> 27) & MASK];
}
// add the padding
if (padding > 0)
{
for (usz i = (trailing * 8 / 5) + 1; i < 8; i++)
{
dst[i] = padding;
}
}
return (String)dst_ptr[:dn];
}
const uint MASK @private = 0b11111;
const char INVALID @private = 0xff;
const int STD_PADDING = '=';
const int NO_PADDING = -1;
fault Base32Error
{
DUPLICATE_IN_ALPHABET,
PADDING_IN_ALPHABET,
INVALID_CHARACTER_IN_ALPHABET,
DESTINATION_TOO_SMALL,
INVALID_PADDING,
CORRUPT_INPUT
}
struct Base32Encoder @deprecated
{
Base32Alphabet alphabet;
char padding;
}
<*
@param encoder "The 32-character alphabet for encoding."
@param padding "Set to a negative value to disable padding."
@require padding < 256
*>
fn void! Base32Encoder.init(&self, Alphabet encoder = STD_ALPHABET, int padding = STD_PADDING)
{
encoder.validate(padding)!;
*self = { .alphabet = { .encoding = (char[32])encoder }, .padding = padding < 0 ? (char)0 : (char)padding};
}
<*
Calculate the length in bytes of the encoded data.
@param n "Length in bytes on input."
@return "Length in bytes of the encoded data."
*>
fn usz Base32Encoder.encode_len(&self, usz n)
{
return encode_len(n, self.padding);
}
<*
Encode the content of src into dst, which must be properly sized.
@param [in] src "The input to be encoded."
@param [inout] dst "The encoded input."
@return "The encoded size."
@return! Base32Error.DESTINATION_TOO_SMALL
*>
fn usz! Base32Encoder.encode(&self, char[] src, char[] dst)
{
usz dn = self.encode_len(src.len);
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
return encode_buffer(src, dst, self.padding, &self.alphabet).len;
}
struct Base32Decoder @deprecated
{
Base32Alphabet alphabet;
char padding;
}
<*
@param decoder "The alphabet used for decoding."
@param padding "Set to a negative value to disable padding."
@require padding < 256
*>
fn void! Base32Decoder.init(&self, Alphabet decoder = STD_ALPHABET, int padding = STD_PADDING)
{
decoder.validate(padding)!;
*self = { .alphabet = { .encoding = (char[32])decoder }, .padding = padding < 0 ? (char)0 : (char)padding };
self.alphabet.reverse[..] = INVALID;
foreach (char i, c : decoder)
{
self.alphabet.reverse[c] = i;
}
}
<*
Calculate the length in bytes of the decoded data.
@param n "Length in bytes of input."
@return "Length in bytes of the decoded data."
*>
fn usz Base32Decoder.decode_len(&self, usz n)
{
return decode_len(n, self.padding);
}
<*
Decode the content of src into dst, which must be properly sized.
@param src "The input to be decoded."
@param dst "The decoded input."
@return "The decoded size."
@return! Base32Error.DESTINATION_TOO_SMALL, Base32Error.CORRUPT_INPUT
*>
fn usz! Base32Decoder.decode(&self, char[] src, char[] dst)
{
if (src.len == 0) return 0;
usz dn = self.decode_len(src.len);
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
return decode_buffer(src, dst, self.padding, &self.alphabet).len;
}
// Validate the 32-character alphabet to make sure that no character occurs
// twice and that the padding is not present in the alphabet.
fn void! Alphabet.validate(&self, int padding)
{
bool[256] checked;
foreach (c : self)
{
if (checked[c])
{
return Base32Error.DUPLICATE_IN_ALPHABET?;
}
checked[c] = true;
if (c == '\r' || c == '\n')
{
return Base32Error.INVALID_CHARACTER_IN_ALPHABET?;
}
}
if (padding >= 0)
{
char pad = (char)padding;
if (pad == '\r' || pad == '\n')
{
return Base32Error.INVALID_PADDING?;
}
if (checked[pad])
{
return Base32Error.PADDING_IN_ALPHABET?;
}
}
}
distinct Alphabet = char[32];
// Standard base32 Alphabet
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
// Extended Hex Alphabet
const Alphabet HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
const Base32Alphabet STANDARD = {
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
.reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffff1a1b1c1d1e1fffffffffffffffff
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
};
const Base32Alphabet HEX = {
.encoding = "0123456789ABCDEFGHIJKLMNOPQRSTUV",
.reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff00010203040506070809ffffffffffff
ff0a0b0c0d0e0f101112131415161718191a1b1c1d1e1fffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
};

401
lib/std/encoding/base64.c3 Normal file
View File

@@ -0,0 +1,401 @@
module std::encoding::base64;
import std::core::bitorder;
// The implementation is based on https://www.rfc-editor.org/rfc/rfc4648
// Specifically this section:
// https://www.rfc-editor.org/rfc/rfc4648#section-4
const char NO_PAD = 0;
const char DEFAULT_PAD = '=';
struct Base64Alphabet
{
char[64] encoding;
char[256] reverse;
}
const Base64Alphabet STANDARD = {
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
.reverse =
x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffff3effffff3f3435363738393a3b3c3dffffffffffff
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff
ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
};
const Base64Alphabet URL = {
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
.reverse =
x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffff3effff3435363738393a3b3c3dffffffffffff
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffff3f
ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
};
const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
fn String encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
return encode_buffer(src, dst, padding, alphabet);
}
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding))!;
return decode_buffer(src, dst, padding, alphabet);
}
fn String encode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
fn String encode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
<*
Calculate the size of the encoded data.
@param n "Size of the input to be encoded."
@param padding "The padding character or 0 if none"
@require padding < 0xFF "Invalid padding character"
@return "The size of the input once encoded."
*>
fn usz encode_len(usz n, char padding)
{
if (padding) return (n + 2) / 3 * 4;
usz trailing = n % 3;
return n / 3 * 4 + (trailing * 4 + 2) / 3;
}
<*
Calculate the size of the decoded data.
@param n "Size of the input to be decoded."
@param padding "The padding character or 0 if none"
@require padding < 0xFF "Invalid padding character"
@return "The size of the input once decoded."
@return! DecodingFailure.INVALID_PADDING
*>
fn usz! decode_len(usz n, char padding)
{
usz dn = n / 4 * 3;
usz trailing = n % 4;
if (padding)
{
if (trailing != 0) return DecodingFailure.INVALID_PADDING?;
// source size is multiple of 4
return dn;
}
if (trailing == 1) return DecodingFailure.INVALID_PADDING?;
return dn + trailing * 3 / 4;
}
<*
Encode the content of src into dst, which must be properly sized.
@param src "The input to be encoded."
@param dst "The encoded input."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@return "The encoded size."
@return! Base64Error.DESTINATION_TOO_SMALL
*>
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
if (src.len == 0) return (String)dst[:0];
usz dn = encode_len(src.len, padding);
char* dst_ptr = dst;
assert(dst.len >= dn);
usz trailing = src.len % 3;
char[] src3 = src[:^trailing];
while (src3.len > 0)
{
uint group = (uint)src3[0] << 16 | (uint)src3[1] << 8 | (uint)src3[2];
dst[0] = alphabet.encoding[group >> 18 & MASK];
dst[1] = alphabet.encoding[group >> 12 & MASK];
dst[2] = alphabet.encoding[group >> 6 & MASK];
dst[3] = alphabet.encoding[group & MASK];
dst = dst[4..];
src3 = src3[3..];
}
// Encode the remaining bytes according to:
// https://www.rfc-editor.org/rfc/rfc4648#section-3.5
switch (trailing)
{
case 1:
uint group = (uint)src[^1] << 16;
dst[0] = alphabet.encoding[group >> 18 & MASK];
dst[1] = alphabet.encoding[group >> 12 & MASK];
if (padding > 0)
{
dst[2] = padding;
dst[3] = padding;
}
case 2:
uint group = (uint)src[^2] << 16 | (uint)src[^1] << 8;
dst[0] = alphabet.encoding[group >> 18 & MASK];
dst[1] = alphabet.encoding[group >> 12 & MASK];
dst[2] = alphabet.encoding[group >> 6 & MASK];
if (padding > 0)
{
dst[3] = padding;
}
case 0:
break;
default:
unreachable();
}
return (String)dst_ptr[:dn];
}
<*
Decode the content of src into dst, which must be properly sized.
@param src "The input to be decoded."
@param dst "The decoded input."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require (decode_len(src.len, padding) ?? 0) <= dst.len "Destination buffer too small"
@require padding < 0xFF "Invalid padding character"
@return "The decoded data."
@return! DecodingFailure
*>
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
if (src.len == 0) return dst[:0];
usz dn = decode_len(src.len, padding)!;
assert(dst.len >= dn);
usz trailing = src.len % 4;
char* dst_ptr = dst;
char[] src4 = src;
switch
{
case !padding:
src4 = src[:^trailing];
default:
// If there is padding, keep the last 4 bytes for later.
// NB. src.len >= 4 as decode_len passed
trailing = 4;
if (src[^1] == padding) src4 = src[:^4];
}
while (src4.len > 0)
{
char c0 = alphabet.reverse[src4[0]];
char c1 = alphabet.reverse[src4[1]];
char c2 = alphabet.reverse[src4[2]];
char c3 = alphabet.reverse[src4[3]];
switch (0xFF)
{
case c0:
case c1:
case c2:
case c3:
return DecodingFailure.INVALID_CHARACTER?;
}
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3;
dst[0] = (char)(group >> 16);
dst[1] = (char)(group >> 8);
dst[2] = (char)group;
dst = dst[3..];
src4 = src4[4..];
}
if (trailing == 0) return dst_ptr[:dn];
src = src[^trailing..];
char c0 = alphabet.reverse[src[0]];
char c1 = alphabet.reverse[src[1]];
if (c0 == 0xFF || c1 == 0xFF) return DecodingFailure.INVALID_PADDING?;
if (!padding)
{
switch (src.len)
{
case 2:
uint group = (uint)c0 << 18 | (uint)c1 << 12;
dst[0] = (char)(group >> 16);
case 3:
char c2 = alphabet.reverse[src[2]];
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
dst[0] = (char)(group >> 16);
dst[1] = (char)(group >> 8);
}
}
else
{
// Valid paddings are:
// 2: xx==
// 1: xxx=
switch (padding)
{
case src[2]:
if (src[3] != padding) return DecodingFailure.INVALID_PADDING?;
uint group = (uint)c0 << 18 | (uint)c1 << 12;
dst[0] = (char)(group >> 16);
dn -= 2;
case src[3]:
char c2 = alphabet.reverse[src[2]];
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
dst[0] = (char)(group >> 16);
dst[1] = (char)(group >> 8);
dn -= 1;
}
}
return dst_ptr[:dn];
}
const MASK @private = 0b111111;
struct Base64Encoder @deprecated
{
char padding;
String alphabet;
}
fault Base64Error
{
DUPLICATE_IN_ALPHABET,
PADDING_IN_ALPHABET,
DESTINATION_TOO_SMALL,
INVALID_PADDING,
INVALID_CHARACTER,
}
<*
@param alphabet "The alphabet used for encoding."
@param padding "Set to a negative value to disable padding."
@require alphabet.len == 64
@require padding < 256
@return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
*>
fn Base64Encoder*! Base64Encoder.init(&self, String alphabet, int padding = '=')
{
check_alphabet(alphabet, padding)!;
*self = { .padding = padding < 0 ? 0 : (char)padding, .alphabet = alphabet };
return self;
}
<*
Calculate the size of the encoded data.
@param n "Size of the input to be encoded."
@return "The size of the input once encoded."
*>
fn usz Base64Encoder.encode_len(&self, usz n)
{
return encode_len(n, self.padding);
}
<*
Encode the content of src into dst, which must be properly sized.
@param src "The input to be encoded."
@param dst "The encoded input."
@return "The encoded size."
@return! Base64Error.DESTINATION_TOO_SMALL
*>
fn usz! Base64Encoder.encode(&self, char[] src, char[] dst)
{
if (src.len == 0) return 0;
usz dn = self.encode_len(src.len);
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
Base64Alphabet a = { .encoding = self.alphabet[:64] };
return encode_buffer(src, dst, self.padding, &a).len;
}
struct Base64Decoder @deprecated
{
char padding;
Base64Alphabet encoding;
bool init_done;
}
import std;
<*
@param alphabet "The alphabet used for encoding."
@param padding "Set to a negative value to disable padding."
@require alphabet.len == 64
@require padding < 256
@return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
*>
fn void! Base64Decoder.init(&self, String alphabet, int padding = '=')
{
self.init_done = true;
check_alphabet(alphabet, padding)!;
*self = { .padding = padding < 0 ? 0 : (char)padding, .encoding.encoding = alphabet[:64] };
self.encoding.reverse[..] = 0xFF;
foreach (i, c : alphabet)
{
self.encoding.reverse[c] = (char)i;
}
}
<*
Calculate the size of the decoded data.
@param n "Size of the input to be decoded."
@return "The size of the input once decoded."
@return! Base64Error.INVALID_PADDING
*>
fn usz! Base64Decoder.decode_len(&self, usz n)
{
return decode_len(n, self.padding) ?? Base64Error.INVALID_PADDING?;
}
<*
Decode the content of src into dst, which must be properly sized.
@param src "The input to be decoded."
@param dst "The decoded input."
@return "The decoded size."
@return! Base64Error.DESTINATION_TOO_SMALL, Base64Error.INVALID_PADDING, Base64Error.INVALID_CHARACTER
*>
fn usz! Base64Decoder.decode(&self, char[] src, char[] dst)
{
if (src.len == 0) return 0;
usz dn = self.decode_len(src.len)!;
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
char[]! decoded = decode_buffer(src, dst, self.padding, &self.encoding);
if (catch err = decoded)
{
case DecodingFailure.INVALID_PADDING:
return Base64Error.INVALID_PADDING?;
case DecodingFailure.INVALID_CHARACTER:
return Base64Error.INVALID_CHARACTER?;
default:
return err?;
}
return decoded.len;
}
// Make sure that all bytes in the alphabet are unique and
// the padding is not present in the alphabet.
fn void! check_alphabet(String alphabet, int padding) @local
{
bool[256] checked;
if (padding < 0)
{
foreach (c : alphabet)
{
if (checked[c]) return Base64Error.DUPLICATE_IN_ALPHABET?;
checked[c] = true;
}
return;
}
char pad = (char)padding;
foreach (c : alphabet)
{
if (c == pad) return Base64Error.PADDING_IN_ALPHABET?;
if (checked[c]) return Base64Error.DUPLICATE_IN_ALPHABET?;
checked[c] = true;
}
}

95
lib/std/encoding/csv.c3 Normal file
View File

@@ -0,0 +1,95 @@
module std::encoding::csv;
import std::io;
struct CsvReader
{
InStream stream;
String separator;
}
struct CsvRow (Printable)
{
String[] list;
String row;
Allocator allocator;
}
fn usz! CsvRow.to_format(&self, Formatter* f) @dynamic
{
return f.printf("%s", self.list);
}
fn usz CsvRow.len(&self) @operator(len)
{
return self.list.len;
}
<*
@require col < self.list.len
*>
fn String CsvRow.get_col(&self, usz col) @operator([])
{
return self.list[col];
}
fn void CsvReader.init(&self, InStream stream, String separator = ",")
{
self.stream = stream;
self.separator = separator;
}
fn CsvRow! CsvReader.read_new_row(self)
{
return self.read_row(allocator::heap()) @inline;
}
<*
@param [&inout] allocator
*>
fn CsvRow! CsvReader.read_row(self, Allocator allocator)
{
String row = io::readline(self.stream, allocator: allocator)!;
defer catch allocator::free(allocator, row);
String[] list = row.split(self.separator, allocator: allocator);
return { list, row, allocator };
}
fn CsvRow! CsvReader.read_temp_row(self)
{
return self.read_row(allocator::temp()) @inline;
}
<*
@require self.allocator != null `Row already freed`
*>
fn void CsvRow.free(&self)
{
allocator::free(self.allocator, self.list);
allocator::free(self.allocator, self.row);
self.allocator = null;
}
fn void! CsvReader.skip_row(self) @maydiscard => @pool()
{
(void)io::treadline(self.stream);
}
macro void! CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @maydiscard
{
InStream stream = self.stream;
String sep = self.separator;
while (rows--)
{
@stack_mem(512; Allocator mem)
{
String! s = io::readline(stream, mem);
if (catch err = s)
{
if (err == IoError.EOF) return;
return err?;
}
@body(s.split(sep, allocator: mem));
};
}
}

View File

@@ -0,0 +1,7 @@
module std::encoding;
fault DecodingFailure
{
INVALID_CHARACTER,
INVALID_PADDING,
}

109
lib/std/encoding/hex.c3 Normal file
View File

@@ -0,0 +1,109 @@
module std::encoding::hex;
import std::encoding @norecurse;
// The implementation is based on https://www.rfc-editor.org/rfc/rfc4648
fn String encode_buffer(char[] code, char[] buffer)
{
return (String)buffer[:encode_bytes(code, buffer)];
}
fn char[]! decode_buffer(char[] code, char[] buffer)
{
return buffer[:decode_bytes(code, buffer)!];
}
fn String encode(char[] code, Allocator allocator)
{
char[] data = allocator::alloc_array(allocator, char, encode_len(code.len));
return (String)data[:encode_bytes(code, data)];
}
fn char[]! decode(char[] code, Allocator allocator)
{
char[] data = allocator::alloc_array(allocator, char, decode_len(code.len));
return data[:decode_bytes(code, data)!];
}
fn String encode_new(char[] code) @inline => encode(code, allocator::heap());
fn String encode_temp(char[] code) @inline => encode(code, allocator::temp());
fn char[]! decode_new(char[] code) @inline => decode(code, allocator::heap());
fn char[]! decode_temp(char[] code) @inline => decode(code, allocator::temp());
<*
Calculate the size of the encoded data.
@param n "Size of the input to be encoded."
@return "The size of the input once encoded."
*>
fn usz encode_len(usz n) => n * 2;
<*
Encode the content of src into dst, which must be properly sized.
@param src "The input to be encoded."
@param dst "The encoded input."
@return "The encoded size."
@require dst.len >= encode_len(src.len) "Destination array is not large enough"
*>
fn usz encode_bytes(char[] src, char[] dst)
{
usz j = 0;
foreach (v : src)
{
dst[j] = HEXALPHABET[v >> 4];
dst[j + 1] = HEXALPHABET[v & 0x0f];
j = j + 2;
}
return src.len * 2;
}
<*
Calculate the size of the decoded data.
@param n "Size of the input to be decoded."
@return "The size of the input once decoded."
*>
macro usz decode_len(usz n) => n / 2;
<*
Decodes src into bytes. Returns the actual number of bytes written to dst.
Expects that src only contains hexadecimal characters and that src has even
length.
@param src "The input to be decoded."
@param dst "The decoded input."
@require src.len % 2 == 0 "src is not of even length"
@require dst.len >= decode_len(src.len) "Destination array is not large enough"
@return! DecodingFailure.INVALID_CHARACTER
*>
fn usz! decode_bytes(char[] src, char[] dst)
{
usz i;
for (usz j = 1; j < src.len; j += 2)
{
char a = HEXREVERSE[src[j - 1]];
char b = HEXREVERSE[src[j]];
if (a > 0x0f || b > 0x0f) return DecodingFailure.INVALID_CHARACTER?;
dst[i] = (a << 4) | b;
i++;
}
return i;
}
const char[?] HEXALPHABET @private = "0123456789abcdef";
const char[?] HEXREVERSE @private =
x`ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
00010203040506070809ffffffffffff
ff0a0b0c0d0e0fffffffffffffffffff
ffffffffffffffffffffffffffffffff
ff0a0b0c0d0e0fffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff`;

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

@@ -0,0 +1,392 @@
// 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;
fault JsonParsingError
{
EOF,
UNEXPECTED_CHARACTER,
INVALID_ESCAPE_SEQUENCE,
DUPLICATE_MEMBERS,
INVALID_NUMBER,
}
fn Object*! parse_string(String s, Allocator allocator = allocator::heap())
{
return parse((ByteReader){}.init(s), allocator);
}
fn Object*! temp_parse_string(String s)
{
return parse((ByteReader){}.init(s), allocator::temp());
}
fn Object*! parse(InStream s, Allocator allocator = allocator::heap())
{
@stack_mem(512; Allocator mem)
{
JsonContext context = { .last_string = dstring::new_with_capacity(64, mem), .stream = s, .allocator = allocator };
@pool(allocator)
{
return parse_any(&context);
};
};
}
fn Object*! temp_parse(InStream s)
{
return parse(s, allocator::temp());
}
// -- Implementation follows --
enum JsonTokenType @local
{
NO_TOKEN,
LBRACE,
LBRACKET,
COMMA,
COLON,
RBRACE,
RBRACKET,
STRING,
NUMBER,
TRUE,
FALSE,
NULL,
EOF,
}
struct JsonContext @local
{
uint line;
InStream stream;
Allocator allocator;
JsonTokenType token;
DString last_string;
double last_number;
char current;
bitstruct : char {
bool skip_comments;
bool reached_end;
bool pushed_back;
}
}
fn Object*! parse_from_token(JsonContext* context, JsonTokenType token) @local
{
switch (token)
{
case NO_TOKEN: unreachable();
case LBRACE: return parse_map(context);
case LBRACKET: return parse_array(context);
case COMMA:
case RBRACE:
case RBRACKET:
case COLON: return JsonParsingError.UNEXPECTED_CHARACTER?;
case STRING: return object::new_string(context.last_string.str_view(), context.allocator);
case NUMBER: return object::new_float(context.last_number, context.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?;
}
}
fn Object*! parse_any(JsonContext* context) @local
{
return parse_from_token(context, advance(context));
}
fn JsonTokenType! lex_number(JsonContext *context, char c) @local
{
@stack_mem(256; Allocator mem)
{
DString t = dstring::new_with_capacity(32, allocator: mem);
bool negate = c == '-';
if (negate)
{
t.append(c);
c = read_next(context)!;
}
while (c.is_digit())
{
t.append(c);
c = read_next(context)!;
}
if (c == '.')
{
t.append(c);
while (c = read_next(context)!, c.is_digit())
{
t.append(c);
}
}
if ((c | 32) == 'e')
{
t.append(c);
c = read_next(context)!;
switch (c)
{
case '-':
case '+':
t.append(c);
c = read_next(context)!;
}
if (!c.is_digit()) return JsonParsingError.INVALID_NUMBER?;
while (c.is_digit())
{
t.append(c);
c = read_next(context)!;
}
}
pushback(context, c);
double! d = t.str_view().to_double() ?? JsonParsingError.INVALID_NUMBER?;
context.last_number = d!;
return NUMBER;
};
}
fn Object*! parse_map(JsonContext* context) @local
{
Object* map = object::new_obj(context.allocator);
defer catch map.free();
JsonTokenType token = advance(context)!;
@stack_mem(256; Allocator mem)
{
DString temp_key = dstring::new_with_capacity(32, mem);
while (token != JsonTokenType.RBRACE)
{
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
DString string = context.last_string;
if (map.has_key(string.str_view())) return JsonParsingError.DUPLICATE_MEMBERS?;
// Copy the key to our temp holder, since our
// last_string may be used in parse_any
temp_key.clear();
temp_key.append(string);
parse_expected(context, COLON)!;
Object* element = parse_any(context)!;
map.set(temp_key.str_view(), element);
token = advance(context)!;
if (token == JsonTokenType.COMMA)
{
token = advance(context)!;
continue;
}
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
return map;
};
}
fn Object*! parse_array(JsonContext* context) @local
{
Object* list = object::new_obj(context.allocator);
defer catch list.free();
JsonTokenType token = advance(context)!;
while (token != JsonTokenType.RBRACKET)
{
Object* element = parse_from_token(context, token)!;
list.push(element);
token = advance(context)!;
if (token == JsonTokenType.COMMA)
{
token = advance(context)!;
continue;
}
if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
return list;
}
fn void pushback(JsonContext* context, char c) @local
{
if (!context.reached_end)
{
assert(!context.pushed_back);
context.pushed_back = true;
context.current = c;
}
}
fn char! read_next(JsonContext* context) @local
{
if (context.reached_end) return '\0';
if (context.pushed_back)
{
context.pushed_back = false;
return context.current;
}
char! c = context.stream.read_byte();
if (catch err = c)
{
case IoError.EOF:
context.reached_end = true;
return '\0';
default:
return err?;
}
if (c == 0)
{
context.reached_end = true;
}
return c;
}
fn JsonTokenType! advance(JsonContext* context) @local
{
char c;
// Skip whitespace
while WS: (c = read_next(context)!)
{
switch (c)
{
case '\n':
context.line++;
nextcase;
case ' ':
case '\t':
case '\r':
case '\v':
continue;
case '/':
if (!context.skip_comments) break;
c = read_next(context)!;
if (c != '*')
{
pushback(context, c);
break WS;
}
while COMMENT: (true)
{
// Skip to */
while (c = read_next(context)!)
{
if (c == '\n') context.line++;
if (c != '*') continue;
// Skip through all the '*'
while (c = read_next(context)!)
{
if (c == '\n') context.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 lex_string(context);
case '-':
case '0'..'9':
return lex_number(context, c);
case 't':
match(context, "rue")!;
return TRUE;
case 'f':
match(context, "alse")!;
return FALSE;
case 'n':
match(context, "ull")!;
return NULL;
default:
return JsonParsingError.UNEXPECTED_CHARACTER?;
}
}
fn void! match(JsonContext* context, String str) @local
{
foreach (c : str)
{
char l = read_next(context)!;
if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
}
fn void! parse_expected(JsonContext* context, JsonTokenType token) @local
{
if (advance(context)! != token) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
fn JsonTokenType! lex_string(JsonContext* context)
{
context.last_string.clear();
while LOOP: (true)
{
char c = read_next(context)!;
switch (c)
{
case '\0':
return JsonParsingError.EOF?;
case 1..31:
return JsonParsingError.UNEXPECTED_CHARACTER?;
case '"':
break LOOP;
case '\\':
break;
default:
context.last_string.append(c);
continue;
}
c = read_next(context)!;
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 = read_next(context)!;
if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
val = val << 4 + (c > '9' ? (c | 32) - 'a' + 10 : c - '0');
}
context.last_string.append_char32(val);
continue;
default:
return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
}
context.last_string.append(c);
}
return STRING;
}

View File

@@ -1,80 +0,0 @@
// TODO: ensure the type is an enum first.
module enumset<Enum>;
$assert(Enum.min < Enum.max, "Only strictly increasing enums may be used with enum sets.");
$assert(Enum.max < 64, "Maximum value of an enum used as enum set is 63");
$assert(Enum.min >= 0, "Minimum value of an enum used as enum set is 0");
$switch ($$C_INT_SIZE):
$case 64:
private define EnumSetType = ulong;
$case 32:
$if (Enum.max < 32):
private define EnumSetType = uint;
$else:
private define EnumSetType = ulong;
$endif;
$default:
$if (Enum.max < 16):
private define EnumSetType = ushort;
$elif (Enum.max < 31):
private define EnumSetType = uint;
$else:
private define EnumSetType = ulong;
$endif;
$endswitch;
define EnumSet = distinct EnumSetType;
fn void EnumSet.add(EnumSet *this, Enum v)
{
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
}
fn void EnumSet.clear(EnumSet *this)
{
*this = 0;
}
fn bool EnumSet.remove(EnumSet *this, Enum v)
{
EnumSetType old = (EnumSetType)*this;
EnumSetType new = old & ~(1u << (EnumSetType)v);
*this = (EnumSet)new;
return old != new;
}
fn bool EnumSet.has(EnumSet *this, Enum v)
{
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
}
fn void EnumSet.add_all(EnumSet *this, EnumSet s)
{
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
}
fn void EnumSet.retain_all(EnumSet *this, EnumSet s)
{
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
}
fn EnumSet EnumSet.and_of(EnumSet *this, EnumSet s)
{
return (EnumSet)((EnumSetType)*this & (EnumSetType)s);
}
fn EnumSet EnumSet.or_of(EnumSet *this, EnumSet s)
{
return (EnumSet)((EnumSetType)*this | (EnumSetType)s);
}
fn EnumSet EnumSet.diff_of(EnumSet *this, EnumSet s)
{
return (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
}
fn EnumSet EnumSet.xor_of(EnumSet *this, EnumSet s)
{
return (EnumSet)((EnumSetType)*this ^ (EnumSetType)s);
}

View File

@@ -1,58 +0,0 @@
// 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::env;
enum CompilerOptLevel
{
O0,
O1,
O2,
O3
}
enum OsType
{
UNKNOWN,
NONE,
ANANAS,
CLOUD_ABI,
DRAGON_FLY,
FREEBSD,
FUCHSIA,
IOS,
KFREEBSD,
LINUX,
PS3,
MACOSX,
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,
}
const OsType OS_TYPE = (OsType)($$OS_TYPE);
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)($$COMPILER_OPT_LEVEL);
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
const bool I128_SUPPORT = $$PLATFORM_I128_SUPPORTED;
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;

View File

@@ -0,0 +1,94 @@
module std::experimental::scheduler(<Event>);
import std::collections, std::thread, std::time;
struct DelayedSchedulerEvent @local
{
inline Event event;
Clock execution_time;
}
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @local
{
switch
{
case self.execution_time < other.execution_time: return -1;
case self.execution_time > other.execution_time: return 1;
default: return 0;
}
}
struct FrameScheduler
{
PriorityQueue(<DelayedSchedulerEvent>) delayed_events;
List(<Event>) events;
List(<Event>) pending_events;
bool pending;
Mutex mtx;
}
fn void FrameScheduler.init(&self)
{
self.events.init(mem);
self.pending_events.init(mem);
self.delayed_events.init(mem);
(void)self.mtx.init();
bool pending;
}
macro void FrameScheduler.@destroy(&self; @destruct(Event e))
{
foreach (e : self.events) @destruct(e);
foreach (e : self.pending_events) @destruct(e);
foreach (e : self.delayed_events.heap) @destruct(e.event);
self.events.free();
self.pending_events.free();
self.delayed_events.free();
(void)self.mtx.destroy();
}
fn void FrameScheduler.queue_delayed_event(&self, Event event, Duration delay)
{
self.mtx.@in_lock()
{
self.delayed_events.push({ event, clock::now().add_duration(delay)});
@atomic_store(self.pending, true);
};
}
fn bool FrameScheduler.has_delayed(&self)
{
self.mtx.@in_lock()
{
return @ok(self.delayed_events.first());
};
}
fn void FrameScheduler.queue_event(&self, Event event)
{
self.mtx.@in_lock()
{
self.pending_events.push(event);
@atomic_store(self.pending, true);
};
}
fn Event! FrameScheduler.pop_event(&self)
{
while (true)
{
if (try event = self.events.pop()) return event;
if (!@atomic_load(self.pending)) return IteratorResult.NO_MORE_ELEMENT?;
self.mtx.@in_lock()
{
self.events.add_all(&self.pending_events);
self.pending_events.clear();
Clock c = clock::now();
while (try top = self.delayed_events.first())
{
if (top.execution_time > c) break;
self.events.push(self.delayed_events.pop()!!);
}
@atomic_store(self.pending, self.delayed_events.len() > 0);
if (!self.events.len()) return IteratorResult.NO_MORE_ELEMENT?;
};
}
}

View File

@@ -4,50 +4,50 @@
module std::hash::adler32;
private const uint ADLER_CONST = 65521;
const uint ADLER_CONST @private = 65521;
struct Adler32
{
uint a;
uint b;
uint a;
uint b;
}
fn void Adler32.init(Adler32 *this)
fn void Adler32.init(&self)
{
*this = { 1, 0 };
*self = { 1, 0 };
}
fn void Adler32.updatec(Adler32* this, char c)
fn void Adler32.updatec(&self, char c)
{
this.a = (this.a + c) % ADLER_CONST;
this.b = (this.b + this.a) % ADLER_CONST;
self.a = (self.a + c) % ADLER_CONST;
self.b = (self.b + self.a) % ADLER_CONST;
}
fn void Adler32.update(Adler32* this, char[] data)
fn void Adler32.update(&self, 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 };
uint a = self.a;
uint b = self.b;
foreach (char x : data)
{
a = (a + x) % ADLER_CONST;
b = (b + a) % ADLER_CONST;
}
*self = { a, b };
}
fn uint Adler32.final(Adler32* this)
fn uint Adler32.final(&self)
{
return (this.b << 16) | this.a;
return (self.b << 16) | self.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;
uint a = 1;
uint b = 0;
foreach (char x : data)
{
a = (a + x) % ADLER_CONST;
b = (b + a) % ADLER_CONST;
}
return (b << 16) | a;
}

View File

@@ -5,45 +5,45 @@ module std::hash::crc32;
struct Crc32
{
uint result;
uint result;
}
fn void Crc32.init(Crc32* this, uint seed = 0)
fn void Crc32.init(&self, uint seed = 0)
{
this.result = ~seed;
self.result = ~seed;
}
fn void Crc32.updatec(Crc32* this, char c)
fn void Crc32.updatec(&self, char c)
{
this.result = (this.result >> 8) ^ CRC32_TABLE[(this.result ^ c) & 0xFF];
self.result = (self.result >> 8) ^ CRC32_TABLE[(self.result ^ c) & 0xFF];
}
fn void Crc32.update(Crc32* this, char[] data)
fn void Crc32.update(&self, char[] data)
{
uint result = this.result;
foreach (char x : data)
{
result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF];
}
this.result = result;
uint result = self.result;
foreach (char x : data)
{
result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF];
}
self.result = result;
}
fn uint Crc32.final(Crc32* this)
fn uint Crc32.final(&self)
{
return ~this.result;
return ~self.result;
}
fn uint encode(char[] data)
{
uint result = ~(uint)(0);
foreach (char x : data)
{
result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF];
}
uint result = ~(uint)(0);
foreach (char x : data)
{
result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF];
}
return ~result;
}
private const uint[256] CRC32_TABLE = {
const uint[256] CRC32_TABLE @private = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,

View File

@@ -5,107 +5,107 @@ module std::hash::crc64;
struct Crc64
{
ulong result;
ulong result;
}
fn void Crc64.init(Crc64* this, uint seed = 0)
fn void Crc64.init(&self, uint seed = 0)
{
this.result = seed;
self.result = seed;
}
fn void Crc64.updatec(Crc64* this, char c)
fn void Crc64.updatec(&self, char c)
{
this.result = (this.result << 8) ^ CRC64_TABLE[(char)((this.result >> 56) ^ c)];
self.result = (self.result << 8) ^ CRC64_TABLE[(char)((self.result >> 56) ^ c)];
}
fn void Crc64.update(Crc64* this, char[] data)
fn void Crc64.update(&self, char[] data)
{
ulong result = this.result;
foreach (char x : data)
{
result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)];
}
this.result = result;
ulong result = self.result;
foreach (char x : data)
{
result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)];
}
self.result = result;
}
fn ulong Crc64.final(Crc64* this)
fn ulong Crc64.final(&self)
{
return this.result;
return self.result;
}
fn ulong encode(char[] data)
{
ulong result = (ulong)(0);
foreach (char x : data)
{
result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)];
}
ulong result = (ulong)(0);
foreach (char x : data)
{
result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)];
}
return result;
}
private const ulong[256] CRC64_TABLE = {
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,
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;
distinct Fnv32a = uint;
const FNV32A_START @private = 0x811c9dc5;
const FNV32A_MUL @private = 0x01000193;
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV32A_MUL;
fn void Fnv32a.init(&self)
{
*self = FNV32A_START;
}
fn void Fnv32a.update(&self, char[] data)
{
Fnv32a h = *self;
foreach (char x : data)
{
update(&h, x);
}
*self = h;
}
macro void Fnv32a.update_char(&self, char c)
{
update(self, c);
}
fn uint encode(char[] data)
{
uint h = FNV32A_START;
foreach (char x : data)
{
update(&h, x);
}
return h;
}

41
lib/std/hash/fnv64a.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::fnv64a;
distinct Fnv64a = ulong;
const FNV64A_START @private = 0xcbf29ce484222325;
const FNV64A_MUL @private = 0x00000100000001b3;
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV64A_MUL;
fn void Fnv64a.init(&self)
{
*self = FNV64A_START;
}
fn void Fnv64a.update(&self, char[] data)
{
Fnv64a h = *self;
foreach (char x : data)
{
update(&h, x);
}
*self = h;
}
macro void Fnv64a.update_char(&self, char c)
{
update(self, c);
}
fn ulong encode(char[] data)
{
ulong h = FNV64A_START;
foreach (char x : data)
{
update(&h, x);
}
return h;
}

107
lib/std/hash/hmac.c3 Normal file
View File

@@ -0,0 +1,107 @@
module std::hash::hmac(<HashAlg, HASH_BYTES, BLOCK_BYTES>);
import std::crypto;
struct Hmac
{
HashAlg a, b;
}
fn char[HASH_BYTES] hash(char[] key, char[] message)
{
Hmac hmac @noinit;
hmac.init(key);
hmac.update(message);
return hmac.final();
}
<*
@require output.len > 0 "Output must be greater than zero"
@require output.len < int.max / HASH_BYTES "Output is too large"
*>
fn void pbkdf2(char[] pw, char[] salt, uint iterations, char[] output)
{
usz l = output.len / HASH_BYTES;
usz r = output.len % HASH_BYTES;
Hmac hmac;
hmac.init(pw);
char[] dst_curr = output;
for (usz i = 1; i <= l; i++)
{
@derive(&hmac, salt, iterations, i, dst_curr[:HASH_BYTES]);
dst_curr = dst_curr[HASH_BYTES..];
}
if (r > 0)
{
char[HASH_BYTES] tmp;
@derive(&hmac, salt, iterations, l + 1, &tmp);
dst_curr[..] = tmp[:dst_curr.len];
mem::zero_volatile(&tmp);
}
}
fn void Hmac.init(&self, char[] key)
{
char[BLOCK_BYTES] buffer;
if (key.len > BLOCK_BYTES)
{
self.a.init();
self.a.update(key);
buffer[:HASH_BYTES] = self.a.final()[..];
}
else
{
buffer[:key.len] = key[..];
}
foreach (&b : buffer) *b ^= IPAD;
self.a.init();
self.a.update(&buffer);
foreach (&b : buffer) *b ^= IPAD ^ OPAD;
self.b.init();
self.b.update(&buffer);
mem::zero_volatile(&buffer);
}
fn void Hmac.update(&self, char[] data)
{
self.a.update(data);
}
fn char[HASH_BYTES] Hmac.final(&self)
{
self.b.update(&&self.a.final());
return self.b.final();
}
const IPAD @private = 0x36;
const OPAD @private = 0x5C;
macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[] out)
{
assert(out.len == HASH_BYTES);
char[HASH_BYTES] tmp @noinit;
defer mem::zero_volatile(&tmp);
Hmac hmac = *hmac_start;
hmac.update(salt);
UIntBE be = { (uint)index };
hmac.update(&&bitcast(be, char[4]));
tmp = hmac.final();
out[..] = tmp;
for (int it = 1; it < iterations; it++)
{
hmac = *hmac_start;
hmac.update(&tmp);
tmp = hmac.final();
foreach (i, v : tmp)
{
out[i] ^= v;
}
}
}

225
lib/std/hash/md5.c3 Normal file
View File

@@ -0,0 +1,225 @@
module std::hash::md5;
import std::hash::hmac;
import std::bits;
const BLOCK_BYTES = 64;
const HASH_BYTES = 16;
struct Md5
{
uint lo, hi;
uint a, b, c, d;
char[64] buffer;
uint[16] block;
}
def HmacMd5 = Hmac(<Md5, HASH_BYTES, BLOCK_BYTES>);
def hmac = hmac::hash(<Md5, HASH_BYTES, BLOCK_BYTES>);
def pbkdf2 = hmac::pbkdf2(<Md5, HASH_BYTES, BLOCK_BYTES>);
fn char[HASH_BYTES] hash(char[] data)
{
Md5 md5;
md5.init();
md5.update(data);
return md5.final();
}
fn void Md5.init(&self)
{
self.a = 0x67452301;
self.b = 0xefcdab89;
self.c = 0x98badcfe;
self.d = 0x10325476;
self.lo = 0;
self.hi = 0;
}
fn void Md5.update(&ctx, char[] data)
{
uint saved_lo = ctx.lo;
if ((ctx.lo = (saved_lo + data.len) & 0x1fffffff) < saved_lo) ctx.hi++;
ctx.hi += data.len >> 29;
usz used = (usz)saved_lo & 0x3f;
if (used)
{
usz available = 64 - used;
if (data.len < available)
{
ctx.buffer[used:data.len] = data[..];
return;
}
ctx.buffer[used:available] = data[:available];
data = data[available..];
body(ctx, &ctx.buffer, 64);
}
if (data.len >= 64)
{
data = body(ctx, data, data.len & ~(usz)0x3f)[:data.len & 0x3f];
}
ctx.buffer[:data.len] = data[..];
}
fn char[HASH_BYTES] Md5.final(&ctx)
{
usz used = (usz)ctx.lo & 0x3f;
ctx.buffer[used++] = 0x80;
usz available = 64 - used;
if (available < 8)
{
ctx.buffer[used:available] = 0;
body(ctx, &ctx.buffer, 64);
used = 0;
available = 64;
}
ctx.buffer[used:available - 8] = 0;
ctx.lo <<= 3;
ctx.buffer[56:4] = bitcast(ctx.lo, char[4])[..];
ctx.buffer[60:4] = bitcast(ctx.hi, char[4])[..];
body(ctx, &ctx.buffer, 64);
char[16] res @noinit;
res[0:4] = bitcast(ctx.a, char[4]);
res[4:4] = bitcast(ctx.b, char[4]);
res[8:4] = bitcast(ctx.c, char[4]);
res[12:4] = bitcast(ctx.d, char[4]);
*ctx = {};
return res;
}
module std::hash::md5 @private;
// Implementation
macro @f(x, y, z) => z ^ (x & (y ^ z));
macro @g(x, y, z) => y ^ (z & (x ^ y));
macro @h(x, y, z) => (x ^ y) ^ z;
macro @h2(x, y, z) => x ^ (y ^ z);
macro @i(x, y, z) => y ^ (x | ~z);
macro @step(#f, a, b, c, d, ptr, n, t, s)
{
*a += #f(b, c, d) + @unaligned_load(*(uint *)&ptr[n * 4], 2) + t;
*a = (*a << s) | ((*a & 0xffffffff) >> (32 - s));
*a += b;
}
fn char* body(Md5* ctx, void* data, usz size)
{
char* ptr;
uint a, b, c, d;
uint saved_a, saved_b, saved_c, saved_d;
ptr = data;
a = ctx.a;
b = ctx.b;
c = ctx.c;
d = ctx.d;
do
{
saved_a = a;
saved_b = b;
saved_c = c;
saved_d = d;
/* Round 1 */
@step(@f, &a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
@step(@f, &d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
@step(@f, &c, d, a, b, ptr, 2, 0x242070db, 17) ;
@step(@f, &b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
@step(@f, &a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
@step(@f, &d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
@step(@f, &c, d, a, b, ptr, 6, 0xa8304613, 17) ;
@step(@f, &b, c, d, a, ptr, 7, 0xfd469501, 22) ;
@step(@f, &a, b, c, d, ptr, 8, 0x698098d8, 7) ;
@step(@f, &d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
@step(@f, &c, d, a, b, ptr, 10, 0xffff5bb1, 17);
@step(@f, &b, c, d, a, ptr, 11, 0x895cd7be, 22);
@step(@f, &a, b, c, d, ptr, 12, 0x6b901122, 7) ;
@step(@f, &d, a, b, c, ptr, 13, 0xfd987193, 12);
@step(@f, &c, d, a, b, ptr, 14, 0xa679438e, 17);
@step(@f, &b, c, d, a, ptr, 15, 0x49b40821, 22);
/* Round 2 */
@step(@g, &a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
@step(@g, &d, a, b, c, ptr, 6, 0xc040b340, 9) ;
@step(@g, &c, d, a, b, ptr, 11, 0x265e5a51, 14);
@step(@g, &b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
@step(@g, &a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
@step(@g, &d, a, b, c, ptr, 10, 0x02441453, 9) ;
@step(@g, &c, d, a, b, ptr, 15, 0xd8a1e681, 14);
@step(@g, &b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
@step(@g, &a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
@step(@g, &d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
@step(@g, &c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
@step(@g, &b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
@step(@g, &a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
@step(@g, &d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
@step(@g, &c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
@step(@g, &b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
/* Round 3 */
@step(@h, &a, b, c, d, ptr, 5, 0xfffa3942, 4);
@step(@h2, &d, a, b, c, ptr, 8, 0x8771f681, 11);
@step(@h, &c, d, a, b, ptr, 11, 0x6d9d6122, 16);
@step(@h2, &b, c, d, a, ptr, 14, 0xfde5380c, 23);
@step(@h, &a, b, c, d, ptr, 1, 0xa4beea44, 4);
@step(@h2, &d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
@step(@h, &c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
@step(@h2, &b, c, d, a, ptr, 10, 0xbebfbc70, 23);
@step(@h, &a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
@step(@h2, &d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
@step(@h, &c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
@step(@h2, &b, c, d, a, ptr, 6, 0x04881d05, 23) ;
@step(@h, &a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
@step(@h2, &d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
@step(@h, &c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
@step(@h2, &b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
/* Round 4 */
@step(@i, &a, b, c, d, ptr, 0, 0xf4292244, 6) ;
@step(@i, &d, a, b, c, ptr, 7, 0x432aff97, 10) ;
@step(@i, &c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
@step(@i, &b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
@step(@i, &a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
@step(@i, &d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
@step(@i, &c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
@step(@i, &b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
@step(@i, &a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
@step(@i, &d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
@step(@i, &c, d, a, b, ptr, 6, 0xa3014314, 15) ;
@step(@i, &b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
@step(@i, &a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
@step(@i, &d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
@step(@i, &c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
@step(@i, &b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
ptr += 64;
} while (size -= 64);
ctx.a = a;
ctx.b = b;
ctx.c = c;
ctx.d = d;
return ptr;
}

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

@@ -0,0 +1,257 @@
// 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::hash::hmac;
import std::bits;
const BLOCK_BYTES = 64;
const HASH_BYTES = 20;
struct Sha1
{
uint[5] state;
uint[2] count;
char[BLOCK_BYTES] buffer;
}
def HmacSha1 = Hmac(<Sha1, HASH_BYTES, BLOCK_BYTES>);
def hmac = hmac::hash(<Sha1, HASH_BYTES, BLOCK_BYTES>);
def pbkdf2 = hmac::pbkdf2(<Sha1, HASH_BYTES, BLOCK_BYTES>);
fn char[HASH_BYTES] hash(char[] data)
{
Sha1 sha1 @noinit;
sha1.init();
sha1.update(data);
return sha1.final();
}
fn void Sha1.init(&self)
{
// SHA1 initialization constants
*self = {
.state = {
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0
}
};
}
<*
@param [in] data
@require data.len <= uint.max
*>
fn void Sha1.update(&self, char[] data)
{
uint j = self.count[0];
uint len = data.len;
if ((self.count[0] += len << 3) < j) self.count[1]++;
self.count[1] += len >> 29;
j = (j >> 3) & 63;
uint i;
if (j + len > 63)
{
i = 64 - j;
self.buffer[j..] = data[:i];
sha1_transform(&self.state, &self.buffer);
for (; i + 63 < len; i += 64)
{
sha1_transform(&self.state, &data[i]);
}
j = 0;
}
self.buffer[j:len - i] = data[i..];
}
fn char[HASH_BYTES] Sha1.final(&self)
{
char[8] finalcount;
for (uint i = 0; i < 8; i++)
{
finalcount[i] = (char)((self.count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 0xFF);
}
self.update((char[]){ 0o200 });
while ((self.count[0] & 504) != 448)
{
self.update((char[]){ 0 });
}
self.update(&finalcount);
char[HASH_BYTES] digest;
for (uint i = 0; i < HASH_BYTES; i++)
{
digest[i] = (char)((self.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF);
}
// Clear mem
*self = {};
finalcount = {};
return digest;
}
union Long16 @local
{
char[BLOCK_BYTES] c;
uint[16] l;
}
macro blk(Long16* 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(Long16* 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(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + v.rotl(5);
*wref = w.rotl(30);
}
macro r1(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + v.rotl(5);
*wref = w.rotl(30);
}
macro r2(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + v.rotl(5);
*wref = w.rotl(30);
}
macro r3(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + v.rotl(5);
*wref = w.rotl(30);
}
macro r4(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + v.rotl(5);
*wref = w.rotl(30);
}
<*
@param [&inout] state
@param [&in] buffer
*>
fn void sha1_transform(uint[5]* 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;
block = {};
}

176
lib/std/hash/sha256.c3 Normal file
View File

@@ -0,0 +1,176 @@
module std::hash::sha256;
import std::hash::hmac;
const BLOCK_SIZE = 64;
const HASH_SIZE = 32;
const uint[64] K @local = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
// Right rotate function
macro uint @rotr(uint x, uint n) @local => (((x) >> (n)) | ((x) << (32 - (n))));
// SHA-256 functions
macro uint @ch(uint x, uint y, uint z) @local => (x & y) ^ (~x & z);
macro uint @maj(uint x, uint y, uint z) @local => (x & y) ^ (x & z) ^ (y & z);
macro uint @_sigma0(uint x) @local => @rotr(x, 2) ^ @rotr(x, 13) ^ @rotr(x, 22);
macro uint @_sigma1(uint x) @local => @rotr(x, 6) ^ @rotr(x, 11) ^ @rotr(x, 25);
macro uint @sigma0(uint x) @local => @rotr(x, 7) ^ @rotr(x, 18) ^ (x >> 3);
macro uint @sigma1(uint x) @local => @rotr(x, 17) ^ @rotr(x, 19) ^ (x >> 10);
struct Sha256
{
uint[8] state;
ulong bitcount;
char[BLOCK_SIZE] buffer;
}
def HmacSha256 = Hmac(<Sha256, HASH_SIZE, BLOCK_SIZE>);
def hmac = hmac::hash(<Sha256, HASH_SIZE, BLOCK_SIZE>);
def pbkdf2 = hmac::pbkdf2(<Sha256, HASH_SIZE, BLOCK_SIZE>);
fn char[HASH_SIZE] hash(char[] data)
{
Sha256 sha256 @noinit;
sha256.init();
sha256.update(data);
return sha256.final();
}
fn void Sha256.init(&self)
{
// Sha256 initialization constants
*self = {
.state = {
0x6A09E667,
0xBB67AE85,
0x3C6EF372,
0xA54FF53A,
0x510E527F,
0x9B05688C,
0x1F83D9AB,
0x5BE0CD19
}
};
}
<*
@param [in] data
@require data.len <= uint.max
*>
fn void Sha256.update(&self, char[] data) {
uint i = 0;
uint len = data.len;
uint buffer_pos = (uint)(self.bitcount / 8) % BLOCK_SIZE;
self.bitcount += (ulong)(len * 8);
while (len--) {
self.buffer[buffer_pos++] = data[i++];
if (buffer_pos == BLOCK_SIZE) {
sha256_transform(&self.state, &self.buffer);
buffer_pos = 0; // Reset buffer position
}
}
}
fn char[HASH_SIZE] Sha256.final(&self) {
char[HASH_SIZE] hash;
ulong i = (self.bitcount / 8) % BLOCK_SIZE;
// Append 0x80 to the buffer
self.buffer[i++] = 0x80;
// Pad the buffer with zeros
if (i > BLOCK_SIZE - 8) {
while (i < BLOCK_SIZE) {
self.buffer[i++] = 0x00;
}
sha256_transform(&self.state, &self.buffer);
i = 0; // Reset buffer index after transformation
}
while (i < BLOCK_SIZE - 8) {
self.buffer[i++] = 0x00;
}
// Append the bitcount in big-endian format
for (int j = 0; j < 8; ++j) {
self.buffer[BLOCK_SIZE - 8 + j] = (char)((self.bitcount >> (56 - j * 8)) & 0xFF);
}
sha256_transform(&self.state, &self.buffer);
// Convert state to the final hash
for (i = 0; i < 8; ++i) {
hash[i * 4] = (char)((self.state[i] >> 24) & 0xFF);
hash[i * 4 + 1] = (char)((self.state[i] >> 16) & 0xFF);
hash[i * 4 + 2] = (char)((self.state[i] >> 8) & 0xFF);
hash[i * 4 + 3] = (char)(self.state[i] & 0xFF);
}
return hash;
}
<*
@param [&inout] state
@param [&in] buffer
*>
fn void sha256_transform(uint* state, char* buffer) @local {
uint a, b, c, d, e, f, g, h, t1, t2;
uint[64] m;
int i;
// Prepare the message schedule
for (i = 0; i < 16; ++i) {
m[i] = ((uint)buffer[i * 4] << 24) | ((uint)buffer[i * 4 + 1] << 16) |
((uint)buffer[i * 4 + 2] << 8) | ((uint)buffer[i * 4 + 3]); // Ensure values are cast to uint for correct shifts
}
for (i = 16; i < 64; ++i) {
m[i] = @sigma1(m[i - 2]) + m[i - 7] + @sigma0(m[i - 15]) + m[i - 16];
}
// Initialize working variables
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];
// Perform the main SHA-256 compression function
for (i = 0; i < 64; ++i) {
t1 = h + @_sigma1(e) + @ch(e, f, g) + K[i] + m[i];
t2 = @_sigma0(a) + @maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
// Update the state
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
a = b = c = d = e = f = g = h = t1 = t2 = i = 0;
m[:64] = buffer[:64] = 0;
}

View File

@@ -1,520 +0,0 @@
// 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 std::mem;
import libc;
import std::env;
struct File
{
CFile file;
}
extern fn int _puts(char* message) @extname("puts");
extern fn int printf(char* message, ...);
extern fn int _putchar(char c) @extname("putchar");
fn int putchar(char c) @inline
{
return _putchar(c);
}
/**
* @param [&in] message
* @return `number of bytes printed.`
*/
fn int print(char* message)
{
char* pointer = message;
while (*pointer != '\0')
{
if (!putchar(*pointer)) return 0;
pointer++;
}
return 1;
}
/**
* @param [&in] message
* @return `number of bytes printed.`
*/
fn int println(char *message = "") @inline
{
return _puts(message);
}
fn void! File.open(File* file, char[] filename, char[] mode)
{
char* filename_copy = mem::talloc(filename.len + 1)!!;
char* mode_copy = mem::talloc(mode.len + 1)!!;
mem::copy(filename_copy, (char*)(filename), filename.len);
mem::copy(mode_copy, (char*)(mode), mode.len);
filename_copy[filename.len] = 0;
mode_copy[filename.len] = 0;
file.file = libc::fopen(filename_copy, mode_copy);
if (!file.file) return IoError.FILE_NOT_FOUND!;
}
enum Seek
{
SET = 0,
CURSOR = 1,
END = 2
}
fault IoError
{
FILE_NOT_FOUND,
FILE_NOT_SEEKABLE,
FILE_NOT_VALID,
FILE_INVALID_POSITION,
FILE_OVERFLOW,
FILE_IS_PIPE,
FILE_EOF,
FILE_INCOMPLETE_WRITE,
INTERRUPTED,
UNKNOWN_ERROR,
}
/**
* @require file.file != null
**/
fn void! File.seek(File *file, long offset, Seek seekMode = Seek.SET)
{
if (libc::fseek(file.file, (SeekIndex)(offset), (int)(seekMode)))
{
switch (libc::errno())
{
case EBADF: return IoError.FILE_NOT_SEEKABLE!;
case EINVAL: return IoError.FILE_INVALID_POSITION!;
case EOVERFLOW: return IoError.FILE_OVERFLOW!;
case ESPIPE: return IoError.FILE_IS_PIPE!;
default: return IoError.UNKNOWN_ERROR!;
}
}
}
/**
* @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 ECONNRESET:
case EBADF: return IoError.FILE_NOT_VALID!;
case EINTR: return IoError.INTERRUPTED!;
case EDQUOT:
case EFAULT:
case EAGAIN:
case EFBIG:
case ENETDOWN:
case ENETUNREACH:
case ENOSPC:
case EIO: return IoError.FILE_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;
}
/**
* @require file && file.file
*/
fn usize File.read(File* file, void* buffer, usize items, usize element_size = 1)
{
return libc::fread(buffer, element_size, items, file.file);
}
/**
* @param [&in] file
* @param [&out] buffer
* @param items
* @param element_size
* @require file.file `File must be initialized`
* @require element_size > 1
*/
fn usize File.write(File* file, void* buffer, usize items, usize element_size = 1)
{
return libc::fwrite(buffer, element_size, items, file.file);
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn usize! File.println(File* file, char[] string)
{
usize len = string.len;
if (len != libc::fwrite(string.ptr, 1, len, file.file)) return IoError.UNKNOWN_ERROR!;
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR!;
return len + 1;
}
/*
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
}
*/
/*
#define __SLBF 0x0001 /* line buffered */
#define __SNBF 0x0002 /* unbuffered */
#define __SRD 0x0004 /* OK to read */
#define __SWR 0x0008 /* OK to write */
/* RD and WR are never simultaneously asserted */
#define __SRW 0x0010 /* open for reading & writing */
#define __SEOF 0x0020 /* found EOF */
#define __SERR 0x0040 /* found error */
#define __SMBF 0x0080 /* _buf is from malloc */
#define __SAPP 0x0100 /* fdopen()ed in append mode */
#define __SSTR 0x0200 /* this is an sprintf/snprintf string */
#define __SOPT 0x0400 /* do fseek() optimisation */
#define __SNPT 0x0800 /* do not do fseek() optimisation */
#define __SOFF 0x1000 /* set iff _offset is in fact correct */
#define __SMOD 0x2000 /* true => fgetln modified _p text */
#define __SALC 0x4000 /* allocate string space dynamically */
#define __SIGN 0x8000 /* ignore this file in _fwalk */
/*
* The following three definitions are for ANSI C, which took them
* from System V, which brilliantly took internal interface macros and
* made them official arguments to setvbuf(), without renaming them.
* Hence, these ugly _IOxxx names are *supposed* to appear in user code.
*
* Although numbered as their counterparts above, the implementation
* does not rely on this.
*/
#define _IOFBF 0 /* setvbuf should set fully buffered */
#define _IOLBF 1 /* setvbuf should set line buffered */
#define _IONBF 2 /* setvbuf should set unbuffered */
#define BUFSIZ 1024 /* size of buffer used by setbuf */
#define EOF (-1)
/* must be == _POSIX_STREAM_MAX <limits.h> */
#define FOPEN_MAX 20 /* must be <= OPEN_MAX <sys/syslimits.h> */
#define FILENAME_MAX 1024 /* must be <= PATH_MAX <sys/syslimits.h> */
/* System V/ANSI C; this is the wrong way to do this, do *not* use these. */
#ifndef _ANSI_SOURCE
#define P_tmpdir "/var/tmp/"
#endif
#define L_tmpnam 1024 /* XXX must be == PATH_MAX */
#define TMP_MAX 308915776
#define stdin __stdinp
#define stdout __stdoutp
#define stderr __stderrp
/* ANSI-C */
__BEGIN_DECLS
char *fgets(char * __restrict, int, FILE *);
#if defined(_DARWIN_UNLIMITED_STREAMS) || defined(_DARWIN_C_SOURCE)
#else /* !_DARWIN_UNLIMITED_STREAMS && !_DARWIN_C_SOURCE */
FILE *fopen(const char * __restrict __filename, const char * __restrict __mode) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_2_0, __DARWIN_ALIAS(fopen));
#endif /* (DARWIN_UNLIMITED_STREAMS || _DARWIN_C_SOURCE) */
int fprintf(FILE * __restrict, const char * __restrict, ...) __printflike(2, 3);
int fputs(const char * __restrict, FILE * __restrict) __DARWIN_ALIAS(fputs);
size_t fread(void * __restrict __ptr, size_t __size, size_t __nitems, FILE * __restrict __stream);
FILE *freopen(const char * __restrict, const char * __restrict,
FILE * __restrict) __DARWIN_ALIAS(freopen);
int fscanf(FILE * __restrict, const char * __restrict, ...) __scanflike(2, 3);
int fsetpos(FILE *, const fpos_t *);
long ftell(FILE *);
size_t fwrite(const void * __restrict __ptr, size_t __size, size_t __nitems, FILE * __restrict __stream) __DARWIN_ALIAS(fwrite);
int getc(FILE *);
int getchar(void);
char *gets(char *);
void perror(const char *) __cold;
int printf(const char * __restrict, ...) __printflike(1, 2);
int putc(int, FILE *);
int putchar(int);
int puts(const char *);
int remove(const char *);
int rename (const char *__old, const char *__new);
void rewind(FILE *);
int scanf(const char * __restrict, ...) __scanflike(1, 2);
void setbuf(FILE * __restrict, char * __restrict);
int setvbuf(FILE * __restrict, char * __restrict, int, size_t);
int sprintf(char * __restrict, const char * __restrict, ...) __printflike(2, 3) __swift_unavailable("Use snprintf instead.");
int sscanf(const char * __restrict, const char * __restrict, ...) __scanflike(2, 3);
FILE *tmpfile(void);
__swift_unavailable("Use mkstemp(3) instead.")
#if !defined(_POSIX_C_SOURCE)
__deprecated_msg("This function is provided for compatibility reasons only. Due to security concerns inherent in the design of tmpnam(3), it is highly recommended that you use mkstemp(3) instead.")
#endif
char *tmpnam(char *);
int ungetc(int, FILE *);
int vfprintf(FILE * __restrict, const char * __restrict, va_list) __printflike(2, 0);
int vprintf(const char * __restrict, va_list) __printflike(1, 0);
int vsprintf(char * __restrict, const char * __restrict, va_list) __printflike(2, 0) __swift_unavailable("Use vsnprintf instead.");
__END_DECLS
/* Additional functionality provided by:
* POSIX.1-1988
*/
#if __DARWIN_C_LEVEL >= 198808L
#define L_ctermid 1024 /* size for ctermid(); PATH_MAX */
__BEGIN_DECLS
#include <_ctermid.h>
#if defined(_DARWIN_UNLIMITED_STREAMS) || defined(_DARWIN_C_SOURCE)
FILE *fdopen(int, const char *) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_3_2, __DARWIN_EXTSN(fdopen));
#else /* !_DARWIN_UNLIMITED_STREAMS && !_DARWIN_C_SOURCE */
FILE *fdopen(int, const char *) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_2_0, __DARWIN_ALIAS(fdopen));
#endif /* (DARWIN_UNLIMITED_STREAMS || _DARWIN_C_SOURCE) */
int fileno(FILE *);
__END_DECLS
#endif /* __DARWIN_C_LEVEL >= 198808L */
/* Additional functionality provided by:
* POSIX.2-1992 C Language Binding Option
*/
#if TARGET_OS_EMBEDDED
#define __swift_unavailable_on(osx_msg, ios_msg) __swift_unavailable(ios_msg)
#else
#define __swift_unavailable_on(osx_msg, ios_msg) __swift_unavailable(osx_msg)
#endif
#if __DARWIN_C_LEVEL >= 199209L
__BEGIN_DECLS
int pclose(FILE *) __swift_unavailable_on("Use posix_spawn APIs or NSTask instead.", "Process spawning is unavailable.");
#if defined(_DARWIN_UNLIMITED_STREAMS) || defined(_DARWIN_C_SOURCE)
FILE *popen(const char *, const char *) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_3_2, __DARWIN_EXTSN(popen)) __swift_unavailable_on("Use posix_spawn APIs or NSTask instead.", "Process spawning is unavailable.");
#else /* !_DARWIN_UNLIMITED_STREAMS && !_DARWIN_C_SOURCE */
FILE *popen(const char *, const char *) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_2_0, __DARWIN_ALIAS(popen)) __swift_unavailable_on("Use posix_spawn APIs or NSTask instead.", "Process spawning is unavailable.");
#endif /* (DARWIN_UNLIMITED_STREAMS || _DARWIN_C_SOURCE) */
__END_DECLS
#endif /* __DARWIN_C_LEVEL >= 199209L */
#undef __swift_unavailable_on
/* Additional functionality provided by:
* POSIX.1c-1995,
* POSIX.1i-1995,
* and the omnibus ISO/IEC 9945-1: 1996
*/
#if __DARWIN_C_LEVEL >= 199506L
/* Functions internal to the implementation. */
__BEGIN_DECLS
int __srget(FILE *);
int __svfscanf(FILE *, const char *, va_list) __scanflike(2, 0);
int __swbuf(int, FILE *);
__END_DECLS
/*
* The __sfoo macros are here so that we can
* define function versions in the C library.
*/
#define __sgetc(p) (--(p)->_r < 0 ? __srget(p) : (int)(*(p)->_p++))
#if defined(__GNUC__) && defined(__STDC__)
__header_always_inline int __sputc(int _c, FILE *_p) {
if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n'))
return (*_p->_p++ = _c);
else
return (__swbuf(_c, _p));
}
#else
/*
* This has been tuned to generate reasonable code on the vax using pcc.
*/
#define __sputc(c, p) \
(--(p)->_w < 0 ? \
(p)->_w >= (p)->_lbfsize ? \
(*(p)->_p = (c)), *(p)->_p != '\n' ? \
(int)*(p)->_p++ : \
__swbuf('\n', p) : \
__swbuf((int)(c), p) : \
(*(p)->_p = (c), (int)*(p)->_p++))
#endif
#define __sfeof(p) (((p)->_flags & __SEOF) != 0)
#define __sferror(p) (((p)->_flags & __SERR) != 0)
#define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF)))
#define __sfileno(p) ((p)->_file)
__BEGIN_DECLS
void flockfile(FILE *);
int ftrylockfile(FILE *);
void funlockfile(FILE *);
int getc_unlocked(FILE *);
int getchar_unlocked(void);
int putc_unlocked(int, FILE *);
int putchar_unlocked(int);
/* Removed in Issue 6 */
#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L
int getw(FILE *);
int putw(int, FILE *);
#endif
__swift_unavailable("Use mkstemp(3) instead.")
#if !defined(_POSIX_C_SOURCE)
__deprecated_msg("This function is provided for compatibility reasons only. Due to security concerns inherent in the design of tempnam(3), it is highly recommended that you use mkstemp(3) instead.")
#endif
char *tempnam(const char *__dir, const char *__prefix) __DARWIN_ALIAS(tempnam);
__END_DECLS
#ifndef lint
#define getc_unlocked(fp) __sgetc(fp)
#define putc_unlocked(x, fp) __sputc(x, fp)
#endif /* lint */
#define getchar_unlocked() getc_unlocked(stdin)
#define putchar_unlocked(x) putc_unlocked(x, stdout)
#endif /* __DARWIN_C_LEVEL >= 199506L */
/* Additional functionality provided by:
* POSIX.1-2001
* ISO C99
*/
#if __DARWIN_C_LEVEL >= 200112L
#include <sys/_types/_off_t.h>
__BEGIN_DECLS
int fseeko(FILE * __stream, off_t __offset, int __whence);
off_t ftello(FILE * __stream);
__END_DECLS
#endif /* __DARWIN_C_LEVEL >= 200112L */
#if __DARWIN_C_LEVEL >= 200112L || defined(_C99_SOURCE) || defined(__cplusplus)
__BEGIN_DECLS
int snprintf(char * __restrict __str, size_t __size, const char * __restrict __format, ...) __printflike(3, 4);
int vfscanf(FILE * __restrict __stream, const char * __restrict __format, va_list) __scanflike(2, 0);
int vscanf(const char * __restrict __format, va_list) __scanflike(1, 0);
int vsnprintf(char * __restrict __str, size_t __size, const char * __restrict __format, va_list) __printflike(3, 0);
int vsscanf(const char * __restrict __str, const char * __restrict __format, va_list) __scanflike(2, 0);
__END_DECLS
#endif /* __DARWIN_C_LEVEL >= 200112L || defined(_C99_SOURCE) || defined(__cplusplus) */
/* Additional functionality provided by:
* POSIX.1-2008
*/
#if __DARWIN_C_LEVEL >= 200809L
#include <sys/_types/_ssize_t.h>
__BEGIN_DECLS
int dprintf(int, const char * __restrict, ...) __printflike(2, 3) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
int vdprintf(int, const char * __restrict, va_list) __printflike(2, 0) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
ssize_t getdelim(char ** __restrict __linep, size_t * __restrict __linecapp, int __delimiter, FILE * __restrict __stream) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
ssize_t getline(char ** __restrict __linep, size_t * __restrict __linecapp, FILE * __restrict __stream) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
FILE *fmemopen(void * __restrict __buf, size_t __size, const char * __restrict __mode) __API_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0), watchos(4.0));
FILE *open_memstream(char **__bufp, size_t *__sizep) __API_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0), watchos(4.0));
__END_DECLS
#endif /* __DARWIN_C_LEVEL >= 200809L */
/* Darwin extensions */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
__BEGIN_DECLS
extern __const int sys_nerr; /* perror(3) external variables */
extern __const char *__const sys_errlist[];
int asprintf(char ** __restrict, const char * __restrict, ...) __printflike(2, 3);
char *ctermid_r(char *);
char *fgetln(FILE *, size_t *);
__const char *fmtcheck(const char *, const char *);
int fpurge(FILE *);
void setbuffer(FILE *, char *, int);
int setlinebuf(FILE *);
int vasprintf(char ** __restrict, const char * __restrict, va_list) __printflike(2, 0);
FILE *zopen(const char *, const char *, int);
/*
* Stdio function-access interface.
*/
FILE *funopen(const void *,
int (* _Nullable)(void *, char *, int),
int (* _Nullable)(void *, const char *, int),
fpos_t (* _Nullable)(void *, fpos_t, int),
int (* _Nullable)(void *));
__END_DECLS
#define fropen(cookie, fn) funopen(cookie, fn, 0, 0, 0)
#define fwopen(cookie, fn) funopen(cookie, 0, fn, 0, 0)
#define feof_unlocked(p) __sfeof(p)
#define ferror_unlocked(p) __sferror(p)
#define clearerr_unlocked(p) __sclearerr(p)
#define fileno_unlocked(p) __sfileno(p)
#endif /* __DARWIN_C_LEVEL >= __DARWIN_C_FULL */
#ifdef _USE_EXTENDED_LOCALES_
#include <xlocale/_stdio.h>
#endif /* _USE_EXTENDED_LOCALES_ */
#if defined (__GNUC__) && _FORTIFY_SOURCE > 0 && !defined (__cplusplus)
/* Security checking functions. */
#include <secure/_stdio.h>
#endif
#endif /* _STDIO_H_ */
*/

94
lib/std/io/bits.c3 Normal file
View File

@@ -0,0 +1,94 @@
module std::io;
struct BitReader
{
InStream reader;
uint bits;
uint len;
}
fn void BitReader.init(&self, InStream byte_reader)
{
*self = { .reader = byte_reader };
}
fn void BitReader.clear(&self) @inline
{
self.len = 0;
}
<*
@require nbits <= 8
@require self.len + nbits <= uint.sizeof * 8
*>
fn char! BitReader.read_bits(&self, uint nbits)
{
uint bits = self.bits;
if (self.len < nbits)
{
// New bits are pushed right.
char c = self.reader.read_byte()!;
bits <<= 8;
bits |= c;
self.bits = bits;
self.len += 8;
}
self.len -= nbits;
uint mask = (1 << nbits) - 1;
return (char)((bits >> self.len) & mask);
}
struct BitWriter
{
OutStream writer;
uint bits;
uint len;
}
// c3 doesn't allow to shift more than bit width of a variable,
// so use closest byte boundary of 24 instead of 32
const int WRITER_BITS = 24;
fn void BitWriter.init(&self, OutStream byte_writer)
{
*self = { .writer = byte_writer };
}
fn void! BitWriter.flush(&self)
{
if (self.len == 0) return;
int padding = ($sizeof(self.bits) * 8 - self.len);
uint bits = self.bits << padding;
uint n = (self.len + 7) / 8;
char[4] buffer;
bitorder::write(bits, &buffer, UIntBE);
io::write_all(self.writer, buffer[:n])!;
self.len = 0;
}
<*
@require nbits <= 32
*>
fn void! BitWriter.write_bits(&self, uint bits, uint nbits)
{
if (nbits == 0) return;
while (self.len + nbits > WRITER_BITS)
{
uint to_push = WRITER_BITS - self.len;
uint bits_to_push = (bits >> (nbits - to_push)) & ((1 << to_push) - 1);
self.bits <<= to_push;
self.bits |= bits_to_push;
self.len += to_push;
nbits -= to_push;
self.flush()!;
}
if (nbits == 0) return;
self.bits <<= nbits;
self.bits |= bits & ((1 << nbits) - 1);
self.len += nbits;
}

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

@@ -0,0 +1,222 @@
module std::io;
import libc;
struct File (InStream, OutStream)
{
CFile file;
}
module std::io::file;
import libc, std::io::path, std::io::os;
fn File! open(String filename, String mode)
{
return from_handle(os::native_fopen(filename, mode));
}
fn File! open_path(Path path, String mode)
{
return from_handle(os::native_fopen(path.str_view(), mode));
}
fn bool exists(String file) => @pool()
{
return path::exists(path::temp_new(file)) ?? false;
}
fn File from_handle(CFile file)
{
return { .file = file };
}
fn bool is_file(String path)
{
return os::native_is_file(path);
}
fn usz! get_size(String path)
{
return os::native_file_size(path);
}
fn void! delete(String filename) => os::native_remove(filename) @inline;
<*
@require self.file != null
*>
fn void! File.reopen(&self, String filename, String mode)
{
self.file = os::native_freopen(self.file, filename, mode)!;
}
<*
@require self.file != null
*>
fn usz! File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic
{
os::native_fseek(self.file, offset, seek_mode)!;
return os::native_ftell(self.file);
}
/*
Implement later
<*
@require self.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 self.file != null
*>
fn void! File.write_byte(&self, char c) @dynamic
{
return os::native_fputc(c, self.file);
}
<*
@param [&inout] self
*>
fn void! File.close(&self) @inline @dynamic
{
if (self.file && libc::fclose(self.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?;
}
}
self.file = null;
}
<*
@require self.file != null
*>
fn bool File.eof(&self) @inline
{
return libc::feof(self.file) != 0;
}
<*
@param [in] buffer
*>
fn usz! File.read(&self, char[] buffer) @dynamic
{
return os::native_fread(self.file, buffer);
}
<*
@param [out] buffer
@require self.file != null `File must be initialized`
*>
fn usz! File.write(&self, char[] buffer) @dynamic
{
return os::native_fwrite(self.file, buffer);
}
fn Fd File.fd(self) @if(env::LIBC)
{
return libc::fileno(self.file);
}
fn bool File.isatty(self) @if(env::LIBC)
{
return libc::isatty(self.fd()) > 0;
}
fn char! File.read_byte(&self) @dynamic
{
int c = libc::fgetc(self.file);
if (c == -1) return IoError.EOF?;
return (char)c;
}
<*
Load up to buffer.len characters. Returns IoError.OVERFLOW if the file is longer
than the buffer.
@param filename "The path to the file to read"
@param [in] buffer "The buffer to read to"
*>
fn char[]! load_buffer(String filename, char[] buffer)
{
File file = open(filename, "rb")!;
defer (void)file.close();
usz len = file.seek(0, END)!;
if (len > buffer.len) return IoError.OVERFLOW?;
file.seek(0, SET)!;
usz read = 0;
while (read < len)
{
read += file.read(buffer[read:len - read])!;
}
return buffer[:len];
}
fn char[]! load(Allocator allocator, String filename) => load_new(filename, allocator);
fn char[]! load_new(String filename, Allocator allocator = allocator::heap())
{
File file = open(filename, "rb")!;
defer (void)file.close();
usz len = file.seek(0, END)!;
file.seek(0, SET)!;
char* data = allocator::malloc_try(allocator, len)!;
defer catch allocator::free(allocator, data);
usz read = 0;
while (read < len)
{
read += file.read(data[read:len - read])!;
}
return data[:len];
}
fn char[]! load_path_new(Path path, Allocator allocator = allocator::heap()) => load_new(path.str_view(), allocator);
fn char[]! load_temp(String filename)
{
return load_new(filename, allocator::temp());
}
fn char[]! load_path_temp(Path path) => load_temp(path.str_view());
fn void! save(String filename, char[] data)
{
File file = open(filename, "wb")!;
defer (void)file.close();
while (data.len)
{
usz written = file.write(data)!;
data = data[written..];
}
}
<*
@require self.file != null `File must be initialized`
*>
fn void! File.flush(&self) @dynamic
{
libc::fflush(self.file);
}

571
lib/std/io/formatter.c3 Normal file
View File

@@ -0,0 +1,571 @@
module std::io;
import std::collections::map;
import libc;
const int PRINTF_NTOA_BUFFER_SIZE = 256;
interface Printable
{
fn String to_string(Allocator allocator) @optional;
fn String to_new_string(Allocator allocator) @optional @deprecated("Use to_string");
fn usz! to_format(Formatter* formatter) @optional;
}
fault PrintFault
{
BUFFER_EXCEEDED,
INTERNAL_BUFFER_EXCEEDED,
INVALID_FORMAT,
NOT_ENOUGH_ARGUMENTS,
INVALID_ARGUMENT,
}
def OutputFn = fn void!(void* buffer, char c);
def FloatType = double;
macro bool is_struct_with_default_print($Type)
{
return $Type.kindof == STRUCT
&&& !$defined($Type.to_format)
&&& !$defined($Type.to_new_string)
&&& !$defined($Type.to_string);
}
<*
Introspect a struct and print it to a formatter
@require @typekind(value) == STRUCT `This macro is only valid on macros`
*>
macro usz! struct_to_format(value, Formatter* f, bool $force_dump)
{
var $Type = $typeof(value);
usz total = f.print("{ ")!;
$foreach ($i, $member : $Type.membersof)
$if $i > 0:
total += f.print(", ")!;
$endif
$if $member.nameof != "":
total += f.printf("%s: ", $member.nameof)!;
$endif
$if ($force_dump &&& $member.typeid.kindof == STRUCT) |||
is_struct_with_default_print($typefrom($member.typeid)):
total += struct_to_format($member.get(value), f, $force_dump)!;
$else
total += f.printf("%s", $member.get(value))!;
$endif
$endforeach
return total + f.print(" }");
}
fn usz! ReflectedParam.to_format(&self, Formatter* f) @dynamic
{
return f.printf("[Parameter '%s']", self.name);
}
fn usz! Formatter.printf(&self, String format, args...)
{
return self.vprintf(format, args) @inline;
}
struct Formatter
{
void *data;
OutputFn out_fn;
struct
{
PrintFlags flags;
uint width;
uint prec;
usz idx;
anyfault first_fault;
}
}
bitstruct PrintFlags : uint
{
bool zeropad;
bool left;
bool plus;
bool space;
bool hash;
bool uppercase;
bool precision;
}
fn void Formatter.init(&self, OutputFn out_fn, void* data = null)
{
*self = { .data = data, .out_fn = out_fn};
}
fn usz! Formatter.out(&self, char c) @private
{
if (catch err = self.out_fn(self.data, c))
{
if (self.first_fault) return self.first_fault?;
self.first_fault = err;
return err?;
}
return 1;
}
fn usz! Formatter.print_with_function(&self, Printable arg)
{
if (&arg.to_format)
{
PrintFlags old = self.flags;
uint old_width = self.width;
uint old_prec = self.prec;
defer
{
self.flags = old;
self.width = old_width;
self.prec = old_prec;
}
if (!arg) return self.out_substr("(null)");
return arg.to_format(self);
}
if (&arg.to_string)
{
PrintFlags old = self.flags;
uint old_width = self.width;
uint old_prec = self.prec;
defer
{
self.flags = old;
self.width = old_width;
self.prec = old_prec;
}
if (!arg) return self.out_substr("(null)");
@stack_mem(1024; Allocator mem)
{
return self.out_substr(arg.to_string(mem));
};
}
return SearchResult.MISSING?;
}
fn usz! Formatter.out_unknown(&self, String category, any arg) @private
{
return self.out_substr("[") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr("]");
}
fn usz! Formatter.out_str(&self, any arg) @private
{
switch (arg.type.kindof)
{
case TYPEID:
return self.out_substr("typeid");
case VOID:
return self.out_substr("void");
case ANYFAULT:
case FAULT:
return self.out_substr((*(anyfault*)arg.ptr).nameof);
case INTERFACE:
case ANY:
return self.out_str(*(any*)arg);
case OPTIONAL:
unreachable();
case SIGNED_INT:
case UNSIGNED_INT:
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
return self.ntoa_any(arg, 10) ?? self.out_substr("<INVALID>");
case FLOAT:
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR");
case BOOL:
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
default:
}
usz! n = self.print_with_function((Printable)arg);
if (try n) return n;
if (@catch(n) != SearchResult.MISSING) n!;
switch (arg.type.kindof)
{
case ENUM:
usz i = types::any_to_enum_ordinal(arg, usz)!!;
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
return self.out_substr(arg.type.names[i]);
case STRUCT:
return self.out_unknown("struct", arg);
case UNION:
return self.out_unknown("union", arg);
case BITSTRUCT:
return self.out_unknown("bitstruct", arg);
case FUNC:
return self.out_unknown("function", arg);
case DISTINCT:
if (arg.type == String.typeid)
{
return self.out_substr(*(String*)arg);
}
if (arg.type == ZString.typeid)
{
return self.out_substr(*(ZString*)arg ? ((ZString*)arg).str_view() : "(null)");
}
if (arg.type == DString.typeid)
{
return self.out_substr(*(DString*)arg ? ((DString*)arg).str_view() : "(null)");
}
return self.out_str(arg.as_inner());
case POINTER:
typeid inner = arg.type.inner;
void** pointer = arg.ptr;
if (arg.type.inner != void.typeid)
{
any deref = any_make(*pointer, inner);
n = self.print_with_function((Printable)deref);
if (try n) return n;
if (@catch(n) != SearchResult.MISSING) n!;
}
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.width = 0;
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
case ARRAY:
// this is SomeType[?] so grab the "SomeType"
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz alen = arg.type.len;
// Pretend this is a String
void* ptr = (void*)arg.ptr;
usz len = self.out('[')!;
for (usz i = 0; i < alen; i++)
{
if (i != 0) len += self.out_substr(", ")!;
len += self.out_str(any_make(ptr, inner))!;
ptr += size;
}
len += self.out(']')!;
return len;
case VECTOR:
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
// this is SomeType[?] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz vlen = arg.type.len;
// Pretend this is a String
void* ptr = (void*)arg.ptr;
usz len = self.out_substr("[<")!;
for (usz i = 0; i < vlen; i++)
{
if (i != 0) len += self.out_substr(", ")!;
len += self.out_str(any_make(ptr, inner))!;
ptr += size;
}
len += self.out_substr(">]")!;
return len;
case SLICE:
// this is SomeType[] so grab the "SomeType"
typeid inner = arg.type.inner;
if (inner == void.typeid) inner = char.typeid;
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
usz size = inner.sizeof;
// Pretend this is a String
String* temp = (void*)arg.ptr;
void* ptr = (void*)temp.ptr;
usz slen = temp.len;
usz len = self.out('[')!;
for (usz i = 0; i < slen; i++)
{
if (i != 0) len += self.out_substr(", ")!;
len += self.out_str(any_make(ptr, inner))!;
ptr += size;
}
len += self.out(']')!;
return len;
case ANY:
case INTERFACE:
unreachable("Already handled");
default:
}
return self.out_substr("Invalid type");
}
fn void! out_null_fn(void* data @unused, char c @unused) @private
{
}
macro usz! @report_fault(Formatter* f, $fault)
{
(void)f.out_substr($fault);
return PrintFault.INVALID_FORMAT?;
}
macro usz! @wrap_bad(Formatter* f, #action)
{
usz! len = #action;
if (catch err = len)
{
case PrintFault.BUFFER_EXCEEDED:
case PrintFault.INTERNAL_BUFFER_EXCEEDED:
return f.first_err(err)?;
default:
err = f.first_err(PrintFault.INVALID_ARGUMENT);
f.out_substr("<INVALID>")!;
return err?;
}
return len;
}
fn usz! Formatter.vprintf(&self, String format, any[] anys)
{
self.first_fault = {};
if (!self.out_fn)
{
// use null output function
self.out_fn = &out_null_fn;
}
usz total_len;
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
total_len += self.out(c)!;
continue;
}
i++;
if (i >= format_len) return @report_fault(self, "%ERR");
c = format[i];
if (c == '%')
{
total_len += self.out(c)!;
continue;
}
// evaluate flags
self.flags = {};
while FLAG_EVAL: (true)
{
switch (c)
{
case '0': self.flags.zeropad = true;
case '-': self.flags.left = true;
case '+': self.flags.plus = true;
case ' ': self.flags.space = true;
case '#': self.flags.hash = true;
default: break FLAG_EVAL;
}
if (++i >= format_len) return @report_fault(self, "%ERR");
c = format[i];
}
// evaluate width field
int! w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
if (catch w) return @report_fault(self, "%ERR");
c = format[i];
if (w < 0)
{
self.flags.left = true;
w = -w;
}
self.width = w;
// evaluate precision field
self.prec = 0;
if (c == '.')
{
self.flags.precision = true;
if (++i >= format_len) return @report_fault(self, "<BAD FORMAT>");
int! prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
if (catch prec) return @report_fault(self, "<BAD FORMAT>");
self.prec = prec < 0 ? 0 : prec;
c = format[i];
}
// evaluate specifier
uint base = 0;
if (variant_index >= anys.len)
{
self.first_err(PrintFault.NOT_ENOUGH_ARGUMENTS);
total_len += self.out_substr("<MISSING>")!;
continue;
}
any current = anys[variant_index++];
switch (c)
{
case 'd':
base = 10;
self.flags.hash = false;
case 'X' :
self.flags.uppercase = true;
nextcase;
case 'x' :
base = 16;
case 'O':
self.flags.uppercase = true;
nextcase;
case 'o' :
base = 8;
case 'B':
self.flags.uppercase = true;
nextcase;
case 'b' :
base = 2;
case 'A':
self.flags.uppercase = true;
nextcase;
case 'a':
total_len += @wrap_bad(self, self.atoa(float_from_any(current)))!;
continue;
case 'F' :
self.flags.uppercase = true;
nextcase;
case 'f':
total_len += @wrap_bad(self, self.ftoa(float_from_any(current)))!;
continue;
case 'E':
self.flags.uppercase = true;
nextcase;
case 'e':
total_len += @wrap_bad(self, self.etoa(float_from_any(current)))!;
continue;
case 'G':
self.flags.uppercase = true;
nextcase;
case 'g':
total_len += @wrap_bad(self, self.gtoa(float_from_any(current)))!;
continue;
case 'c':
total_len += self.out_char(current)!;
continue;
case 'H':
self.flags.uppercase = true;
nextcase;
case 'h':
char[] out @noinit;
switch (current)
{
case char[]:
out = *current;
case ichar[]:
out = *(char[]*)current;
default:
if (current.type.kindof == ARRAY && (current.type.inner == char.typeid || current.type.inner == ichar.typeid))
{
out = ((char*)current.ptr)[:current.type.sizeof];
break;
}
total_len += self.out_substr("<INVALID>")!;
continue;
}
if (self.flags.left)
{
usz len = print_hex_chars(self, out, self.flags.uppercase)!;
total_len += len;
total_len += self.pad(' ', self.width, len)!;
continue;
}
if (self.width)
{
total_len += self.pad(' ', self.width, out.len * 2)!;
}
total_len += print_hex_chars(self, out, self.flags.uppercase)!;
continue;
case 's':
if (self.flags.left)
{
usz len = self.out_str(current)!;
total_len += len;
total_len += self.pad(' ', self.width, len)!;
continue;
}
if (self.width)
{
OutputFn out_fn = self.out_fn;
self.out_fn = (OutputFn)&out_null_fn;
usz len = self.out_str(current)!;
self.out_fn = out_fn;
total_len += self.pad(' ', self.width, len)!;
}
total_len += self.out_str(current)!;
continue;
case 'p':
self.flags.zeropad = true;
self.flags.hash = true;
base = 16;
default:
self.first_err(PrintFault.INVALID_FORMAT);
total_len += self.out_substr("<BAD FORMAT>")!;
continue;
}
if (base != 10)
{
self.flags.plus = false;
self.flags.space = false;
}
// ignore '0' flag when precision is given
if (self.flags.precision) self.flags.zeropad = false;
bool is_neg;
total_len += @wrap_bad(self, self.ntoa(int_from_any(current, &is_neg), is_neg, base))!;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
if (self.first_fault) return self.first_fault?;
return total_len;
}
fn usz! Formatter.print(&self, String str)
{
if (!self.out_fn)
{
// use null output function
self.out_fn = &out_null_fn;
}
foreach (c : str) self.out(c)!;
return self.idx;
}

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