Compare commits

..

1672 Commits

Author SHA1 Message Date
Christian Buttner
5ab5fdf954 Address/memory/thread sanitizer. 2024-08-21 16:01:45 +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
Christoffer Lerno
8743223dd6 Allocators. Rename of "optenum" to fault. Memcpy and memset added. Cleanup of declaration use. 2022-04-09 20:07:59 +02:00
Christoffer Lerno
2e2a1ca21a Fix defer with static variables. 2022-03-31 10:11:37 +02:00
Christoffer Lerno
34bd5fa6da Fix of macro defer with bodies. 2022-03-28 10:19:24 +02:00
Christoffer Lerno
131bf5ed34 Cleanup and change of varcast. 2022-03-27 13:22:04 +02:00
Christoffer Lerno
78134316b7 Addin @varcast 2022-03-27 13:09:06 +02:00
Christoffer Lerno
b31629c5e8 Removal of "or_else jump". Fixes to defer & macros/blocks, optimized failable return. @noscope macros removed. Disallow meaningless defer.
Correctly show error on return or rethrow inside of a defer. Fix copying an access expression. Removing scoped expr.
2022-03-26 20:16:36 +01:00
data-man
bbfc2fc8ab Update checkout action to v3 2022-03-23 21:15:52 +01:00
gdm85
1940f6967c Add support for podman
Use a tmpfs when running
Prefix podman images with 'localhost/'
2022-03-23 18:44:46 +01:00
data-man
adf2b2e818 Update build-with-docker.sh [skip ci] 2022-03-23 16:48:46 +01:00
Christoffer Lerno
6a9b14d107 Added regression test for errors. 2022-03-21 18:18:14 +01:00
Christoffer Lerno
8b8a8d81db Fix of issue with "a ?? false" 2022-03-21 13:22:59 +01:00
data-man
d1fadf6428 Don't replace digits to underscore in created files 2022-03-20 19:41:30 +01:00
Christoffer Lerno
05d8e6c7b8 Fix debug panic for vector shift and division. 2022-03-20 19:39:31 +01:00
Christoffer Lerno
6789fab93c Panic function that may be redefined. Trap and stacktrace builtins. Bug using builtin fixed. Fixes to using $$LINE and friends. Produces a stacktrace on error. 2022-03-18 12:43:17 +01:00
Christoffer Lerno
3490814d73 Simplify scope flags. 2022-03-13 22:49:10 +01:00
Christoffer Lerno
3799dbb082 Use declid instead of decl* in the decltable. 2022-03-12 14:45:54 +01:00
Christoffer Lerno
60d7c8aa14 Add timings. 2022-03-12 02:35:07 +01:00
Christoffer Lerno
55598b2de8 Remove multi-line strings. 2022-03-12 00:40:06 +01:00
Christoffer Lerno
4fe2a70ee1 Only add dll visibility on external functions for win32. 2022-03-11 13:27:01 +01:00
Christoffer Lerno
ab56b2d047 Only add dll visibility on non-local functions for win32. 2022-03-11 13:05:46 +01:00
Christoffer Lerno
1061b4e1dd Make doc an AST type. 2022-03-11 12:24:07 +01:00
Christoffer Lerno
e02362de0c Resolve contracts as they are encountered. 2022-03-11 11:17:39 +01:00
Christoffer Lerno
c414459075 Fix bug with missing debug location. 2022-03-10 15:55:44 +01:00
Christoffer Lerno
8f6fb8b7d7 Move body param as decl. Fix lexer bug with docs. 2022-03-10 14:54:52 +01:00
Christoffer Lerno
fc296ea579 Extracted macro declaration body to separately allocated field. 2022-03-10 14:54:52 +01:00
Christoffer Lerno
4258fe4d01 Simple check for [in] and [out] parameters. 2022-03-10 12:46:45 +01:00
Christoffer Lerno
0565e87e5e Support '@pure' and parse the optreturn tag. 2022-03-10 09:46:30 +01:00
Christoffer Lerno
6a48f81485 Smaller Decl and Expr. Compressed TypeInfo. 2022-03-09 19:10:11 +01:00
Christoffer Lerno
322d714305 Dev (#404)
Remove 'errtype' name and reduce Expr / TypeInfo memory footprint.
2022-03-08 23:38:27 +01:00
Christoffer Lerno
069a2d40cb Changing how defer works. Remove of undef. Simplify ensure. 2022-03-08 14:15:31 +01:00
Christoffer Lerno
9b0dfe8ba3 Cleanup: remove CT_ELIF ast node. 2022-03-04 17:08:47 +01:00
Christoffer Lerno
2802b2b96d Ensure, $eval and $evaltype, @unreachable() replaces $unreachable. <Type>.sizeof 2022-03-04 15:49:17 +01:00
Christoffer Lerno
4f4bc80953 Symtab has a new hash map type. New, small, sourcespan, external symbols simplified. Updated docs. 2022-03-01 10:32:52 +01:00
data-man
e45853c0cb Update README [skip ci] 2022-03-01 09:55:34 +01:00
Christoffer Lerno
f54a93890a Remove the EXPR_CONST_IDENTIFIER 2022-02-18 13:56:13 +01:00
Christoffer Lerno
6b4e4f6114 Update formatting to consistently use tabs. 2022-02-18 12:56:17 +01:00
Christoffer Lerno
bf5683b41c General cleanup, more native platform detection. 2022-02-18 09:54:12 +01:00
Christoffer Lerno
73351e0aa9 Updated version for implicit imports. 2022-02-16 20:42:39 +01:00
Christoffer Lerno
df3e51c17e Implicit imports. 2022-02-16 20:41:54 +01:00
data-man
d11d80e896 Update --list-precedence table 2022-02-15 18:55:02 +01:00
Christoffer Lerno
7226bff6ea Use JSON, unifying settings. 2022-02-15 18:53:54 +01:00
Christoffer Lerno
fd82f9685f Make target from string case dependent. 2022-02-15 18:53:54 +01:00
Christoffer Lerno
c70d6716da Added second missing unreachable. 2022-02-08 11:54:19 +01:00
Christoffer Lerno
e57c2710d9 Added missing unreachable. 2022-02-08 11:48:43 +01:00
Christoffer Lerno
d42193dbd6 Fixes the alloca alignment on x64 for alignment > 16. Improved indexing into ConstInitializer adding const indexing into a const array during compile time. Add a temporary enum values result. 2022-02-08 11:41:34 +01:00
Dmitry Atamanov
9f9f24c3cc CI config refactoring (#389) 2022-02-08 11:05:38 +01:00
data-man
f3e326fcd9 CI config refactoring 2022-02-05 22:26:53 +01:00
Christoffer Lerno
ee32a5fd47 Added $stringify. Remove of editor_plugin folder. 2022-02-02 15:22:28 +01:00
Christoffer Lerno
06917f2e65 Added "guess a number". Fix CT enum / int comparison. Fix some array pointer decay scenarios. Infer type of expression blocks. Correctly merge anyerr. 2022-02-01 22:46:32 +01:00
Christoffer Lerno
ba66aaaf12 Foreach overload is now done using attributes (@operator(elementat), @operator(elementref) and @operator(len)) rather than reserved functions. 2022-01-29 16:45:03 +01:00
data-man
9639ad6a73 Move fail-fast above 2022-01-29 10:38:19 +01:00
Christoffer Lerno
cf56825d26 Fixed bug when using indexing on a generic type. Made array::list::List work with [] and foreach. 2022-01-26 21:27:13 +01:00
Christoffer Lerno
e5bcb74822 Added levenshtein.c3 to working files. Fixes to reverse indexing. Added min/max functions. Tentatively removed "opaque" 2022-01-26 17:40:01 +01:00
Christoffer Lerno
8eb295bf5b Cleanup. 2022-01-25 17:01:49 +01:00
Christoffer Lerno
bd6c3db413 Disable MSYS tests and add MacOS tests. 2022-01-25 16:58:08 +01:00
Christoffer Lerno
21fd2c4485 Update to LLVM fix. 2022-01-25 15:14:22 +01:00
Christoffer Lerno
bc8fbdb54a Fix LLVM 14 compatibility 2022-01-25 15:11:02 +01:00
Christoffer Lerno
8922399c36 Add $for 2022-01-24 22:52:01 +01:00
Christoffer Lerno
1e7ad2e241 Add $foreach 2022-01-24 18:53:44 +01:00
Christoffer Lerno
4f212f7634 Remove a gazillion imports. 2022-01-24 14:53:38 +01:00
Christoffer Lerno
2c5ae858b8 Re-add () to ct constructs. 2022-01-24 00:52:43 +01:00
Christoffer Lerno
ef95c1a630 Fix $switch. And make top level $switch work. 2022-01-24 00:01:54 +01:00
Christoffer Lerno
b2be8349ed Add typeless varargs. 2022-01-23 19:43:57 +01:00
Christoffer Lerno
01e9086666 Add precedence listing. 2022-01-22 17:31:33 +01:00
Christoffer Lerno
66b763193d Use O1 on CXX release to avoid issues. 2022-01-21 02:22:58 +01:00
Christoffer Lerno
36e40e59cb Added enumset to stdlib. 2022-01-20 20:00:22 +01:00
Christoffer Lerno
dce33ba5b5 Fixing #380 where const aggregates were copied by value. 2022-01-20 16:52:28 +01:00
Christoffer Lerno
ec291d4a9d This allows testing optnum, optenum, errnum and resnum as alternatives to errtype. 2022-01-19 23:21:17 +01:00
Christoffer Lerno
e6ad9c324d This fixes the bug with "case 3 .. 1" #357 2022-01-19 23:11:03 +01:00
Christoffer Lerno
3450016978 This adds the ability to list keywords, operators etc. implementing #350. 2022-01-19 13:11:12 +01:00
Christoffer Lerno
827499ca80 Update version to reflect recent changes. 2022-01-18 23:32:59 +01:00
Christoffer Lerno
556be2ff7f "@autoimport" attribute #366 2022-01-18 23:16:48 +01:00
Christoffer Lerno
8adb0faa06 Multi module import support. #354 2022-01-18 22:53:31 +01:00
Christoffer Lerno
b6450861d2 Cast no longer needs (type)(expr) format. Ordinary C (type)expr now works. 2022-01-18 22:32:12 +01:00
Christoffer Lerno
1e56948a22 Prepare removing "func" 2022-01-18 14:15:44 +01:00
Christoffer Lerno
e4e8abbc6c Dev (#376)
Errno updates with errno for linux/win/macos. Updated $$ syntax to also match compiler constants.
2022-01-18 01:31:14 +01:00
Christoffer Lerno
3f60443d66 Update errno. 2022-01-17 22:28:06 +01:00
Christoffer Lerno
f53dd95aa7 Update errno. 2022-01-17 22:24:52 +01:00
Christoffer Lerno
1743036104 Added some File functionality. 2022-01-17 22:16:29 +01:00
Christoffer Lerno
a2fa61f58b Updated licensing information. 2022-01-17 17:18:22 +01:00
Christoffer Lerno
4059d22315 Remove unnecessary load in LLVM statement lowering. Added more tests from Clang. 2022-01-17 17:08:28 +01:00
Christoffer Lerno
05f0059b1b Fix float conversion. Strings as array initializers work much better. 2022-01-17 16:06:17 +01:00
Christoffer Lerno
bc3b58b3e3 Optimized ternary where both branches are empty. 2022-01-17 12:01:27 +01:00
Christoffer Lerno
99ea0afcbf Optimize multiple int casts on ptrtoint, into a single cast. Fixed incorrect widening cast from signed -> unsigned. 2022-01-17 00:45:26 +01:00
Christoffer Lerno
23461b179f More splitting of the AbiArgInfo to make it easier to find duplicate behaviour. 2022-01-16 16:21:55 +01:00
Christoffer Lerno
d916f111b3 Fixes a | b when a | b are boolean. Fixes to RISCV and x64 ABI implementations. 2022-01-16 01:13:43 +01:00
Christoffer Lerno
0f4a43717e Fixes to the aarch64 ABI. 2022-01-15 01:37:43 +01:00
Christoffer Lerno
c5a862f4d1 Fix to the x64 ABI. More tests. 2022-01-14 21:30:29 +01:00
Christoffer Lerno
fb22a36aa9 Fix of #374. Even more "or" LLVM 14 fixes. 2022-01-13 21:51:19 +01:00
Christoffer Lerno
889bc27800 Skip "or with zero" 2022-01-13 21:38:24 +01:00
Christoffer Lerno
eed5b7db54 Correctly call x64 varargs. 2022-01-13 21:22:50 +01:00
Christoffer Lerno
5683fe3f8c Add parsing for escaping and remove "fault" token name. Allow excluding stdlib. Fixes to the x64 abi: no narrowing done for i32 results, assuming too many registers, more tests. 2022-01-12 23:56:59 +01:00
data-man
684ad9e663 Disable mimalloc by the option 2022-01-11 17:32:06 +01:00
Christoffer Lerno
a6b29bccb7 Somewhat smaller SymEntry. Different mixing function in stables. 2022-01-10 22:05:56 +01:00
Christoffer Lerno
71623a1874 Minor bugfix. 2022-01-10 18:59:29 +01:00
Christoffer Lerno
16179d2513 Make it easier to match LLVM 14 output. 2022-01-10 14:50:57 +01:00
Dmitry Atamanov
fd8cd6a8e2 Use several LLVM versions in CI (#369)
* Use several LLVM versions in CI
2022-01-10 14:40:55 +01:00
Christoffer Lerno
180b17b213 Updated with latest TB. 2022-01-10 14:38:31 +01:00
Christoffer Lerno
4764981708 Update cols for warning and prev as well. 2022-01-10 14:08:23 +01:00
Disheng Su
3cf057ff95 Print file's full path and col number in compiler's error message 2022-01-10 14:06:29 +01:00
Christoffer Lerno
ea3b50d039 Codegen with better annotations on globals. 2022-01-10 13:58:15 +01:00
Christoffer Lerno
041c096801 Refactored some of the llvm lowering. Work on TB. Disabled mimalloc due to issues with LLVM. 2022-01-09 21:23:37 +01:00
Dmitry Atamanov
d942dfbc99 Support build with mimalloc (#348)
* Support build with mimalloc

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

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
$ 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,70 +2,207 @@ 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: 18
LLVM_RELEASE_VERSION_LINUX: 17
LLVM_RELEASE_VERSION_UBUNTU20: 17
LLVM_DEV_VERSION: 20
jobs:
build-msys2-mingw:
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 --debug-log --emit-llvm run hello_world_win32
dir build\llvm_ir
..\..\build\${{ matrix.build_type }}\c3c.exe clean
dir build\llvm_ir
- 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 --debug-log build hello_world_win32_lib
- name: Vendor-fetch
run: |
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
- name: Try raylib
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_arkanoid.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_tetris.c3
- name: run compiler tests
run: |
cd test
python3.exe src/tester.py ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
- name: Compile run unit tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1
- name: Test python script
run: |
py msvc_build_libraries.py --accept-license
dir msvc_sdk
- name: upload artifacts
uses: actions/upload-artifact@v3
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
matrix:
build_type: [Release, Debug]
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: git binutils mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-llvm mingw-w64-x86_64-polly mingw-w64-x86_64-python mingw-w64-x86_64-lld
install: git binutils mingw-w64-x86_64-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-18.1.8-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-18.1.8-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/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
../../build/c3c run --debug-log
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --cc cc --debug-log
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c.exe test_suite/
build-msys2-clang:
runs-on: windows-latest
#if: ${{ false }}
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: CLANG64
update: true
update: false
install: git binutils mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-toolchain mingw-w64-clang-x86_64-python
- name: CMake
run: |
mkdir build && cd build
cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
../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
../../build/c3c run --debug-log
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
@@ -75,27 +212,581 @@ jobs:
build-linux:
runs-on: ubuntu-latest
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20]
steps:
- uses: actions/checkout@v1
- name: (Ubuntu) Download LLVM
- uses: actions/checkout@v4
- name: Install common deps
run: |
sudo apt-get install zlib1g zlib1g-dev clang-11 libllvm11 llvm-11 llvm-11-dev llvm-11-runtime liblld-11-dev liblld-11 python3
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
- name: CMake
- name: Install Clang ${{matrix.llvm_version}}
run: |
mkdir build && cd build
cmake .. -DLLVM_DIR=/usr/lib/llvm-11/cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
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 run unit tests
run: |
cd test
../build/c3c compile-test unit
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build
../../build/c3c run --debug-log
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --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 --debug-log --linker=builtin
- 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
python3 src/tester.py ../build/c3c test_suite/
- 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@v3
with:
name: c3-linux-${{matrix.build_type}}
path: c3-linux-${{matrix.build_type}}.tar.gz
build-linux-ubuntu20:
runs-on: ubuntu-20.04
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [17, 18, 19]
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
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: |
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
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --linker=builtin
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_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@v3
with:
name: c3-ubuntu-20-${{matrix.build_type}}
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
build-with-docker:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ubuntu_version: [20.04, 22.04]
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20]
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 --debug-log
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --linker=builtin
- 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
python3 src/tester.py ../build/c3c test_suite/
build-mac:
runs-on: macos-latest
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [17, 18]
steps:
- uses: actions/checkout@v4
- name: Download LLVM
run: |
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: |
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 raylib
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/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 run unit tests
run: |
cd test
../build/c3c compile-test unit
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --linker=builtin
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_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@v3
with:
name: c3-macos-${{matrix.build_type}}
path: c3-macos-${{matrix.build_type}}.zip
release:
runs-on: ubuntu-latest
needs: [build-msvc, build-linux, build-mac]
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@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@v3
- run: cp -r lib c3-windows-Release
- run: cp -r lib c3-windows-Debug
- run: cp msvc_build_libraries.py c3-windows-Release
- run: cp msvc_build_libraries.py c3-windows-Debug
- run: zip -r c3-windows-Release.zip c3-windows-Release
- run: zip -r c3-windows-Debug.zip c3-windows-Debug
- id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: latest
release_name: latest
draft: false
prerelease: true
- name: upload windows
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-windows-Release.zip
asset_name: c3-windows.zip
asset_content_type: application/zip
- name: upload windows debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-windows-Debug.zip
asset_name: c3-windows-debug.zip
asset_content_type: application/zip
- name: upload linux
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-linux-Release/c3-linux-Release.tar.gz
asset_name: c3-linux.tar.gz
asset_content_type: application/gzip
- name: upload linux debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-linux-Debug/c3-linux-Debug.tar.gz
asset_name: c3-linux-debug.tar.gz
asset_content_type: application/gzip
- name: upload ubuntu 20
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz
asset_name: c3-ubuntu-20.tar.gz
asset_content_type: application/gzip
- name: upload ubuntu 20 debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz
asset_name: c3-ubuntu-20-debug.tar.gz
asset_content_type: application/gzip
- name: upload macos
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-macos-Release/c3-macos-Release.zip
asset_name: c3-macos.zip
asset_content_type: application/zip
- name: upload macos debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-macos-Debug/c3-macos-Debug.zip
asset_name: c3-macos-debug.zip
asset_content_type: application/zip

12
.gitignore vendored
View File

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

3
.gitmodules vendored Normal file
View File

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

View File

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

15
CMakeSettings.json Normal file
View File

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

View File

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

20
LICENSE_STDLIB Normal file
View File

@@ -0,0 +1,20 @@
Copyright (c) 2022 Christoffer Lernö and contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

401
README.md
View File

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

View File

@@ -1,42 +1,44 @@
#!/bin/bash
## build-with-docker.sh
## @author gdm85
##
## Script to build c3c for either Ubuntu 18 or Ubuntu 20.
##
#
if [ $# -ne 1 -a $# -ne 2 ]; then
echo "Usage: build-with-docker.sh (18|20) [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
if [ -z "$2" ]; then
CMAKE_BUILD_TYPE=Debug
else
CMAKE_BUILD_TYPE="$2"
fi
TAG="$1"
if [ "$1" = 18 ]; then
UBUNTU_VERSION="18.04"
DEPS="llvm-10-dev liblld-10-dev libclang-10-dev"
elif [ "$1" = 20 ]; then
UBUNTU_VERSION="20.04"
DEPS="llvm-11-dev liblld-11-dev clang-11 libllvm11 llvm-11-runtime"
else
echo "ERROR: expected 18 or 20 as Ubuntu version argument" 1>&2
exit 2
fi
cd docker && docker build -t c3c-builder:$TAG --build-arg UID=$(id -u) --build-arg GID=$(id -g) \
--build-arg DEPS="$DEPS" --build-arg UBUNTU_VERSION="$UBUNTU_VERSION" .
cd ..
rm -rf build bin lib
rm -rf build bin
mkdir -p build bin
exec docker run -ti --rm -v "$PWD":/home/c3c/source -w /home/c3c/source c3c-builder:$TAG bash -c \
"cd build && cmake -DLLVM_DIR=/usr/lib/llvm-11/cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE .. && cmake --build . && mv c3c ../bin/ && mv lib ../lib"
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"

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

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);

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

@@ -0,0 +1,520 @@
// 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::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

@@ -0,0 +1,458 @@
// 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;
}
/**
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap())
{
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)
{
return self.new_init(initial_capacity, allocator::temp()) @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 = allocator::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}
fn String AnyList.to_tstring(&self)
{
return 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.new_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 temp allocator
* @return! IteratorResult.NO_MORE_ELEMENT
**/
fn any! AnyList.temp_pop(&self) => self.new_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.
**/
fn any! AnyList.new_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.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,160 @@
/**
* @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())
{
self.data.new_init(initial_capacity, allocator);
return self;
}
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1)
{
return self.new_init(initial_capacity, allocator::temp()) @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();
if (q >= current_len)
{
usz n = q + 1;
self.data.reserve(n);
if (n - 1 >= current_len)
{
self.data.entries[current_len .. (n - 1)] = 0;
}
self.data.size = n;
}
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,434 @@
// 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::new_format("%s", *self, .allocator = allocator);
}
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, 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[] ElasticArray.to_new_array(&self, Allocator allocator = allocator::heap())
{
return list_common::list_to_new_array(Type, self, allocator);
}
fn Type[] ElasticArray.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 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,63 @@
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)i, *value)!;
}
n += formatter.print(" }")!;
return n;
}
fn String EnumMap.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}
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,172 @@
// 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 maybe 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 / 8] |= (char)(1u << ((usz)v % 8));
$else
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v);
$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 / 8] &= (char)~(1u << ((usz)v % 8));
return true;
$else
EnumSetType old = (EnumSetType)*self;
EnumSetType new = old & ~(1u << (EnumSetType)v);
*self = (EnumSet)new;
return old != new;
$endif
}
fn bool EnumSet.has(&self, Enum v)
{
$if IS_CHAR_ARRAY:
return (bool)(((*self)[(usz)v / 8] << ((usz)v % 8)) & 0x01);
$else
return ((EnumSetType)*self & (1u << (EnumSetType)v)) != 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::new_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,400 @@
// 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;
struct HashMap
{
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 = allocator::heap())
{
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)
{
return self.new_init(capacity, load_factor, allocator::temp()) @inline;
}
/**
* 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 [&inout] allocator "The allocator to use"
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map, Allocator allocator = allocator::heap())
{
self.new_init(other_map.table.len, other_map.load_factor, allocator);
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)
{
return map.new_init_from_map(other_map, allocator::temp()) @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.new_init();
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return true;
}
}
map.add_entry(hash, key, value, index);
return false;
}
fn void! HashMap.remove(&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.key_tlist(&map)
{
return map.key_new_list(allocator::temp()) @inline;
}
fn Key[] HashMap.key_new_list(&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)
{
list[index++] = entry.key;
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;
}
}
}
}
fn Value[] HashMap.value_tlist(&map)
{
return map.value_new_list(allocator::temp()) @inline;
}
fn Value[] HashMap.value_new_list(&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;
}
// --- 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 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)
{
if (!e) continue;
map.put_for_create(e.key, e.value);
}
}
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
{
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);
}

View File

@@ -0,0 +1,330 @@
// 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.new_init(&self, Allocator allocator = allocator::heap())
{
*self = { .allocator = allocator };
return self;
}
fn LinkedList* LinkedList.temp_init(&self)
{
return self.new_init(allocator::temp()) @inline;
}
/**
* @require self.allocator
**/
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
**/
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 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
**/
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--;
}

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

@@ -0,0 +1,566 @@
// 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.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap())
{
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)
{
return self.new_init(initial_capacity, allocator::temp()) @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())
{
self.new_init(values.len, allocator) @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)
{
self.temp_init(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::new_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);
}
/**
* @require self.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
**/
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
**/
fn void List.insert_at(&self, usz index, Type type)
{
self.reserve(1);
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.set_size(self.size + 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;
}
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;
}
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
}
macro Type List.@item_at(&self, usz index) @operator([])
{
return self.entries[index];
}
fn Type* List.get_ref(&self, usz index) @operator(&[]) @inline
{
return &self.entries[index];
}
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
**/
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;
}

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

@@ -0,0 +1,434 @@
// 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())
{
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 [&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)
{
if (!e) continue;
map._put_for_create(e.key, e.value);
}
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,19 @@
module std::collections::maybe(<Type>);
struct Maybe
{
Type value;
bool has_value;
}
fn Maybe value(Type val)
{
return { .value = val, .has_value = true };
}
const Maybe EMPTY = { };
macro Type! Maybe.get(self)
{
return self.has_value ? self.value : SearchResult.MISSING?;
}

View File

@@ -0,0 +1,469 @@
// 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.key_new_list(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", self.i)!;
case UNSIGNED_INT:
return formatter.printf("%d", (uint128)self.i)!;
case FLOAT:
return formatter.printf("%d", 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) {
allocator::free(self.allocator, entry.key);
entry.value.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.new_init(.allocator = 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.new_init(.allocator = 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)allocator::free(self.allocator, entry.key);
(void)entry.value.free();
}
self.map.set(key.copy(self.map.allocator), 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,148 @@
// 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.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;
}
}
/**
* @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,85 @@
/**
* @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
{
return string::new_format("[%s..%s]", self.start, self.end, .allocator = allocator);
}
fn String Range.to_tstring(&self)
{
return self.to_new_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 = allocator::heap()) @dynamic
{
return string::new_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,103 @@
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;
}

View File

@@ -0,0 +1,106 @@
// 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 void ArenaAllocator.init(&self, char[] data)
{
self.data = data;
self.used = 0;
}
fn void ArenaAllocator.clear(&self)
{
self.used = 0;
}
struct ArenaAllocatorHeader @local
{
usz size;
char[*] data;
}
/*
* @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,203 @@
// 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;
}
import std::io;
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
* @require self.page `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 `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 = {|
if (!page && self.unused_page)
{
self.page = page = self.unused_page;
self.unused_page = page.prev_arena;
page.prev_arena = null;
}
if (!page) return self._alloc_new(size, alignment);
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;
}
}
return self._alloc_new(size, alignment);
}
page.used = new_used;
assert(start + size == page.memory + page.used);
void* mem = start;
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem - 1;
chunk.size = size;
return mem;
|}!;
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 "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;
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,156 @@
// 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 libc;
const LibcAllocator LIBC_ALLOCATOR = {};
distinct LibcAllocator (Allocator) = uptr;
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);
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,149 @@
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;
}
/**
* @param [&inout] allocator
* Initialize a memory arena for use using the provided bytes.
**/
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
**/
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.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,227 @@
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)
{
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:
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)!;
}
page = (TempAllocatorPage*)mem - 1;
page.start = mem;
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,217 @@
// 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.new_init(.allocator = 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)
{
usz allocated = 0;
@pool()
{
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;
self.allocs_total++;
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 void TrackingAllocator.print_report(&self) => self.fprint_report(io::stdout())!!;
fn void! TrackingAllocator.fprint_report(&self, OutStream out)
{
usz total = 0;
usz entries = 0;
bool leaks = false;
@pool()
{
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);
}
}
}
};
}

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

@@ -0,0 +1,147 @@
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) == VECTOR || @typekind(array) == ARRAY
* @require @typekind(array[0]) == VECTOR || @typekind(array[0]) == ARRAY
**/
macro slice2d(array, x = 0, xlen = 0, y = 0, ylen = 0)
{
if (xlen < 1) xlen = $typeof(array[0]).len + xlen;
if (ylen < 1) ylen = $typeof(array).len + ylen;
var $ElementType = $typeof(array[0][0]);
return Slice2d(<$ElementType>) { ($ElementType*)&array, $typeof(array[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_new(arr1, arr2, Allocator allocator = allocator::heap())
{
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,
* 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 result.len == arr1.len + arr2.len
**/
macro tconcat(arr1, arr2) => 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(self, usz idy) @operator([])
{
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
}
/**
* @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
}

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

@@ -0,0 +1,684 @@
// 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`
**/
macro void @scope(&variable; @body) @builtin
{
var temp = *variable;
defer *variable = temp;
@body();
}
/**
* Swap two variables
* @require $assignable(*b, $typeof(*a)) && $assignable(*a, $typeof(*b))
**/
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);
return;
}
$endif
$$trap();
}
fn void default_panic(String message, String file, String function, uint line) @if(!env::NATIVE_STACKTRACE)
{
$if $defined(io::stderr):
io::eprint("\nERROR: '");
io::eprint(message);
io::eprintfn("', in %s (%s:%d)", function, file, line);
$endif
$$trap();
}
def PanicFn = fn void(String message, String file, String function, uint line);
PanicFn panic = &default_panic;
fn void panicf(String fmt, String file, String function, uint line, args...)
{
@stack_mem(512; Allocator allocator)
{
DString s;
s.new_init(.allocator = allocator);
s.appendf(fmt, ...args);
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)i;
}
return SearchResult.MISSING?;
}
/**
* Mark an expression as likely to be true
*
* @param #value "expression to be marked likely"
* @param $probability "in the range 0 - 1"
* @require $probability >= 0 && $probability <= 1.0
**/
macro bool @likely(bool #value, $probability = 1.0) @builtin
{
$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);
}
macro anyfault @catch(#expr) @builtin
{
if (catch f = #expr) return f;
return anyfault {};
}
macro bool @ok(#expr) @builtin
{
if (catch #expr) return false;
return true;
}
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 uint int.hash(int i) => i;
macro uint uint.hash(uint i) => i;
macro uint short.hash(short s) => s;
macro uint ushort.hash(ushort s) => s;
macro uint char.hash(char c) => c;
macro uint ichar.hash(ichar c) => c;
macro uint long.hash(long i) => (uint)((i >> 32) ^ i);
macro uint ulong.hash(ulong i) => (uint)((i >> 32) ^ i);
macro uint int128.hash(int128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
macro uint uint128.hash(uint128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
macro uint bool.hash(bool b) => (uint)b;
macro uint typeid.hash(typeid t) => ((ulong)(uptr)t).hash();
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
macro uint void*.hash(void* ptr) => ((ulong)(uptr)ptr).hash();
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
}

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

@@ -0,0 +1,55 @@
// 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);
// 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;
}
}

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

@@ -0,0 +1,529 @@
module std::core::dstring;
import std::io;
distinct DString (OutStream) = 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())
{
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)
{
self.new_init(capacity, allocator::temp()) @inline;
return *self;
}
fn DString new_with_capacity(usz capacity, Allocator allocator = allocator::heap())
{
return DString{}.new_init(capacity, allocator);
}
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())
{
DString string;
string.new_init(self.len() + b.len(), allocator);
string.append(self);
string.append(b);
return string;
}
fn DString DString.temp_concat(self, DString b) => self.new_concat(b, allocator::temp());
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
{
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];
}
fn void DString.append_utf32(&self, Char32[] chars)
{
self.reserve(chars.len);
foreach (Char32 c : chars)
{
self.append_char32(c);
}
}
/**
* @require index < self.len()
**/
fn void DString.set(self, usz index, char c)
{
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 void 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;
}
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_new_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
}
fn void DString.insert_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);
}
}
fn usz! DString.appendf(&self, String format, args...) @maydiscard
{
if (!self.data()) self.new_init(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.new_init(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 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;
}

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

@@ -0,0 +1,194 @@
// 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 OsType OS_TYPE = (OsType)$$OS_TYPE;
const ArchType ARCH_TYPE = (ArchType)$$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)$$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)$$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 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);

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

@@ -0,0 +1,753 @@
// 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 [in] x "The variable or dereferenced pointer to load."
* @param $alignment "The alignment to assume for the load"
* @return "The value of x"
*
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
**/
macro @unaligned_load(&x, usz $alignment) @builtin
{
return $$unaligned_load(x, $alignment);
}
/**
* @param [out] 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 of x"
*
* @require $assignable(value, $typeof(*x)) : "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);
}
macro @volatile_load(&x) @builtin
{
return $$volatile_load(x);
}
/**
* @require $assignable(y, $typeof(*x)) : "The value doesn't match the variable"
**/
macro @volatile_store(&x, y) @builtin
{
return $$volatile_store(x, ($typeof(*x))y);
}
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 [in] x "the variable or dereferenced pointer to load."
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
* @param $volatile "whether the load should be volatile, defaults to 'false'"
* @return "returns the value of x"
*
* @require $ordering != AtomicOrdering.RELEASE "Release ordering is not valid for load."
* @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for load."
* @require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
* @require @typekind(x) == POINTER "You can only load from a pointer"
**/
macro @atomic_load(&x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
return $$atomic_load(x, $volatile, (int)$ordering);
}
/**
* @param [out] x "the variable or dereferenced pointer to store to."
* @param value "the value to store."
* @param $ordering "the atomic ordering of the store, defaults to SEQ_CONSISTENT"
* @param $volatile "whether the store should be volatile, defaults to 'false'"
*
* @require $ordering != AtomicOrdering.ACQUIRE "Acquire ordering is not valid for store."
* @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for store."
* @require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
**/
macro void @atomic_store(&x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
$$atomic_store(x, value, $volatile, (int)$ordering);
}
/**
* @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 clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = 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, bool $inlined = 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);
}
/**
* @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;
}
macro 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();
}
macro void @report_heap_allocs_in_scope(;@body())
{
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();
}
@body();
}
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;
}
macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
{
TempAllocator* current = allocator::temp();
var $has_arg = !$is_const(#other_temp);
$if $has_arg:
TempAllocator* original = current;
if (current == (void*)#other_temp) current = allocator::temp_allocator_next();
$endif
usz mark = current.used;
defer
{
current.reset(mark);
$if $has_arg:
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(1) @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::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
}
/**
* 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);
}
/**
* 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
}
macro temp_alloc($Type) @nodiscard
{
return tmalloc($Type.sizeof);
}
/**
* @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)!!;
}

View File

@@ -0,0 +1,424 @@
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($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
tlocal Allocator thread_allocator @private = &allocator::LIBC_ALLOCATOR;
tlocal TempAllocator* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private;
Allocator temp_base_allocator @private = &allocator::LIBC_ALLOCATOR;
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];
}

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,258 @@
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_BF16,
AMX_COMPLEX,
AMX_FP16,
AMX_INT8,
AMX_TILE,
AVX,
AVX10_1_256,
AVX10_1_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,
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 {};
CpuId leaf7s1 = leaf7.eax >= 1 ? x86_cpuid(7, 1) : CpuId {};
CpuId ext1 = x86_cpuid(0x80000000).eax >= 0x80000001 ? x86_cpuid(0x80000001) : CpuId {};
CpuId ext8 = x86_cpuid(0x80000000).eax >= 0x80000008 ? x86_cpuid(0x80000008) : CpuId {};
CpuId leaf_d = max_level >= 0xd ? x86_cpuid(0xd, 0x1) : CpuId {};
CpuId leaf_14 = max_level >= 0x14 ? x86_cpuid(0x14) : CpuId {};
CpuId leaf_19 = max_level >= 0x19 ? x86_cpuid(0x19) : CpuId {};
CpuId leaf_24 = max_level >= 0x24 ? x86_cpuid(0x24) : CpuId {};
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(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,166 @@
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_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_to_void_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
#m(args);
return 0;
}

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

@@ -0,0 +1,250 @@
// 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 AnyRaw
{
void* ptr;
typeid type;
}
struct SliceRaw
{
void* ptr;
usz len;
}
def BenchmarkFn = fn void!();
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)
{
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 default_benchmark_runner()
{
@pool()
{
return run_benchmarks(benchmark_collection_create(allocator::temp()));
};
}
def TestFn = fn void!();
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;
}
struct TestContext
{
JmpBuf buf;
}
// 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);
}
TestContext* test_context @private;
fn void test_panic(String message, String file, String function, uint line)
{
io::printn("[error]");
io::print("\n Error: ");
io::print(message);
io::printn();
io::printfn(" - in %s %s:%s.\n", function, file, line);
libc::longjmp(&test_context.buf, 1);
}
fn bool run_tests(TestUnit[] tests)
{
usz max_name;
foreach (&unit : tests)
{
if (max_name < unit.name.len) max_name = unit.name.len;
}
quicksort(tests, &cmp_test_unit);
TestContext context;
test_context = &context;
PanicFn old_panic = builtin::panic;
defer builtin::panic = old_panic;
builtin::panic = &test_panic;
int tests_passed = 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);
io::printn(name);
name.clear();
foreach(unit : tests)
{
defer name.clear();
name.appendf("Testing %s ", unit.name);
name.append_repeat('.', max_name - unit.name.len + 2);
io::printf("%s ", name.str_view());
if (libc::setjmp(&context.buf) == 0)
{
if (catch err = unit.func())
{
io::printfn("[failed] Failed due to: %s", err);
continue;
}
io::printn("[ok]");
tests_passed++;
}
}
io::printfn("\n%d test%s run.\n", test_count, test_count > 1 ? "s" : "");
io::printfn("Test Result: %s. %d passed, %d failed.",
tests_passed < test_count ? "FAILED" : "ok", tests_passed, test_count - tests_passed);
return test_count == tests_passed;
}
fn bool default_test_runner()
{
@pool()
{
return run_tests(test_collection_create(allocator::temp()));
};
}
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,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(addr);
$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);

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

@@ -0,0 +1,740 @@
module std::core::string;
import std::ascii;
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 String created using the formatting function.
*
* @param [in] fmt `The formatting string`
**/
macro String tformat(String fmt, ...)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat);
return str.str_view();
}
/**
* Return a temporary ZString created using the formatting function.
*
* @param [in] fmt `The formatting string`
**/
macro ZString tformat_zstr(String fmt, ...)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat);
return str.zstr_view();
}
/**
* Return a new String created using the formatting function.
*
* @param [in] fmt `The formatting string`
* @param [inout] allocator `The allocator to use`
**/
macro String new_format(String fmt, ..., Allocator allocator = allocator::heap())
{
@pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat);
return str.copy_str(allocator);
};
}
/**
* Return a new ZString created using the formatting function.
*
* @param [in] fmt `The formatting string`
* @param [inout] allocator `The allocator to use`
**/
macro ZString new_format_zstr(String fmt, ..., Allocator allocator = allocator::heap())
{
@pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat);
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 ")
{
usz start = 0;
usz len = string.len;
while (start < len && char_in_set(string[start], to_trim)) start++;
if (start == len) return string[:0];
usz end = len - 1;
while (end > start && char_in_set(string[end], to_trim)) end--;
return string[start..end];
}
/**
* 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 [&inout] allocator "The allocator, defaults to the heap allocator"
* @param max "Max number of elements, 0 means no limit, defaults to 0"
* @require needle.len > 0 "The needle must be at least 1 character long"
* @ensure return.len > 0
**/
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap())
{
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 (i == capacity)
{
capacity *= 2;
holder = allocator::realloc(allocator, holder, String.sizeof * capacity);
}
holder[i++] = res;
}
return holder[:i];
}
/**
* This function is identical to String.split, but implicitly uses the
* temporary allocator.
*
* @param [in] s
* @param [in] needle
* @param max "Max number of elements, 0 means no limit, defaults to 0"
**/
fn String[] String.tsplit(s, String needle, usz max = 0)
{
return s.split(needle, max, allocator::temp()) @inline;
}
/**
* 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 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;
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.len) 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::temp())
{
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_new_wstring(s, Allocator allocator = allocator::heap())
{
return (WString)s.to_new_utf16(allocator).ptr;
}
fn WString! String.to_temp_wstring(s)
{
return (WString)s.to_temp_utf16().ptr;
}
fn Char32[]! String.to_new_utf32(s, Allocator allocator = allocator::heap())
{
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_temp_utf32(s)
{
return s.to_new_utf32(allocator::temp());
}
/**
* 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, Allocator allocator = allocator::heap())
{
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 = {|
char ch = string[index++];
if (base_used != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A' + 10);
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?;
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?;
return (char)(ch - 'a' + 10);
|}!;
if (c >= base_used) return NumberConversion.MALFORMED_INTEGER?;
value = {|
if (is_negative)
{
$Type new_value = value * base_used - c;
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
}
$Type new_value = value * base_used + c;
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
return 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 Splitter { self, split, 0 };
}
struct Splitter
{
String string;
String split;
usz current;
}
fn void Splitter.reset(&self)
{
self.current = 0;
}
fn String! Splitter.next(&self)
{
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)
{
defer self.current = current + next + self.split.len;
return remaining[:next];
}
self.current = len;
return remaining;
}

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,487 @@
module std::core::string;
import std::math;
// Float parsing based on code in Musl floatscan.c by Rich Felker.
// Musl uses the MIT license, copied below:
// ----------------------------------------------------------------------
// Copyright © 2005-2014 Rich Felker, et al.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ----------------------------------------------------------------------
const KMAX = 128;
const MASK = KMAX - 1;
const B1B_DIG = 2;
const uint[2] B1B_MAX = { 9007199, 254740991 };
/**
* @require chars.len > 0
**/
macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
{
uint[KMAX] x;
const uint[2] TH = B1B_MAX;
int emax = - $emin - $bits + 3;
const int[*] P10S = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
usz index;
bool got_digit = chars[0] == '0';
bool got_rad;
long lrp, dc;
int k, j, lnz;
usz len = chars.len;
usz last_char = len - 1;
assert(len);
char c @noinit;
// Skip past first characters
while ((c = chars[index]) == '0')
{
if (index == last_char) return sign * 0.0;
index++;
}
if (c == '.')
{
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
{
lrp--;
got_digit = true;
while (last_char != index && (c = chars[++index]) == '0')
{
lrp--;
}
}
}
while (c - '0' < 10u || c == '.')
{
switch
{
case c == '.':
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
got_rad = true;
lrp = dc;
case k < KMAX - 3:
dc++;
if (c != '0') lnz = (int)dc;
if (j)
{
x[k] = x[k] * 10 + c - '0';
}
else
{
x[k] = c - '0';
}
if (++j == 9)
{
k++;
j = 0;
}
got_digit = true;
default:
dc++;
if (c != '0') x[KMAX - 4] |= 1;
}
if (index == last_char) break;
assert(index < last_char);
c = chars[++index];
}
if (!got_rad) lrp = dc;
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if ((c | 32) == 'e')
{
if (last_char == index) return NumberConversion.MALFORMED_FLOAT?;
long e10 = String.to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT?!;
lrp += e10;
}
else if (index != last_char)
{
return NumberConversion.MALFORMED_FLOAT?;
}
// Handle zero specially to avoid nasty special cases later
if (!x[0]) return sign * 0.0;
// Optimize small integers (w/no exponent) and over/under-flow
if (lrp == dc && dc < 10 && ($bits > 30 || (ulong)x[0] >> $bits == 0)) return sign * (double)x[0];
if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
// Align incomplete final B1B digit
if (j)
{
for (; j < 9; j++) x[k] *= 10;
k++;
j = 0;
}
int a;
int z = k;
int e2;
long rp = lrp;
// Optimize small to mid-size integers (even in exp. notation)
if (lnz < 9 && lnz <= rp && rp < 18)
{
if (rp == 9) return sign * (double)x[0];
if (rp < 9) return sign * (double)x[0] / P10S[8 - rp];
int bitlim = $bits - 3 * (int)(rp - 9);
if (bitlim > 30 || x[0] >> bitlim == 0) return sign * (double)x[0] * P10S[rp - 10];
}
// Align radix point to B1B digit boundary
if (rp % 9)
{
long rpm9 = rp >= 0 ? rp % 9 : rp % 9 + 9;
int p10 = P10S[8 - rpm9];
uint carry = 0;
for (k = a; k != z; k++)
{
uint tmp = x[k] % p10;
x[k] = x[k] / p10 + carry;
carry = 1000000000 / p10 * tmp;
if (k == a && !x[k])
{
a = (a + 1) & MASK;
rp -= 9;
}
}
if (carry) x[z++] = carry;
rp += 9 - rpm9;
}
// Upscale until desired number of bits are left of radix point
while (rp < 9 * B1B_DIG || (rp == 9 * B1B_DIG && x[a] < TH[0]))
{
uint carry = 0;
e2 -= 29;
for (k = (z - 1) & MASK; ; k = (k - 1) & MASK)
{
ulong tmp = (ulong)x[k] << 29 + carry;
if (tmp > 1000000000)
{
carry = (uint)(tmp / 1000000000);
x[k] = (uint)(tmp % 1000000000);
}
else
{
carry = 0;
x[k] = (uint)tmp;
}
if (k == (z - 1) & MASK && k != a && !x[k]) z = k;
if (k == a) break;
}
if (carry)
{
rp += 9;
a = (a - 1) & MASK;
if (a == z)
{
z = (z - 1) & MASK;
x[(z - 1) & MASK] |= x[z];
}
x[a] = carry;
}
}
// Downscale until exactly number of bits are left of radix point
while (true)
{
uint carry = 0;
int sh = 1;
int i;
for (i = 0; i < B1B_DIG; i++)
{
k = (a + i) & MASK;
if (k == z || x[k] < TH[i])
{
i = B1B_DIG;
break;
}
if (x[(a + i) & MASK] > TH[i]) break;
}
if (i == B1B_DIG && rp == 9 * B1B_DIG) break;
if (rp > 9 + 9 * B1B_DIG) sh = 9;
e2 += sh;
for (k = a; k != z; k = (k+1) & MASK)
{
uint tmp = x[k] & (1 << sh - 1);
x[k] = x[k] >> sh + carry;
carry = (1000000000 >> sh) * tmp;
if (k == a && !x[k])
{
a = (a + 1) & MASK;
i--;
rp -= 9;
}
}
if (carry)
{
if ((z + 1) & MASK != a)
{
x[z] = carry;
z = (z + 1) & MASK;
}
else
{
x[(z - 1) & MASK] |= 1;
}
}
}
// Assemble desired bits into floating point variable
double y;
int i;
for (i = 0; i < B1B_DIG; i++)
{
if ((a + i) & MASK == z) x[(z = (z + 1) & MASK) - 1] = 0;
y = 1000000000.0 * y + x[(a + i) & MASK];
}
y *= sign;
bool denormal;
/* Limit precision for denormal results */
uint bits = $bits;
if (bits > math::DOUBLE_MANT_DIG + e2 - $emin)
{
bits = math::DOUBLE_MANT_DIG + e2 - $emin;
if (bits < 0) bits = 0;
denormal = true;
}
// Calculate bias term to force rounding, move out lower bits
double bias;
double frac;
if (bits < math::DOUBLE_MANT_DIG)
{
bias = math::copysign(math::scalbn(1, 2 * math::DOUBLE_MANT_DIG - bits - 1), y);
frac = y % math::scalbn(1, math::DOUBLE_MANT_DIG - bits);
y -= frac;
y += bias;
}
// Process tail of decimal input so it can affect rounding
if ((a + i) & MASK != z)
{
uint t = x[(a + i) & MASK];
switch
{
case t < 500000000 && (t || (a + i + 1) & MASK != z):
frac += 0.25 * sign;
case t > 500000000:
frac += 0.75 * sign;
case t == 500000000:
if ((a + i + 1) & MASK == z)
{
frac += 0.5 * sign;
}
else
{
frac += 0.75 * sign;
}
}
if (math::DOUBLE_MANT_DIG - bits >= 2 && !(frac % 1)) frac++;
}
y += frac;
y -= bias;
if (((e2 + math::DOUBLE_MANT_DIG) & int.max) > emax - 5)
{
if (math::abs(y) >= 0x1p53)
{
if (denormal && bits == math::DOUBLE_MANT_DIG + e2 - $emin) denormal = false;
y *= 0.5;
e2++;
}
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT?;
}
return math::scalbn(y, e2);
}
macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
{
double scale = 1;
uint x;
long rp;
long dc;
char c @noinit;
bool got_rad;
bool got_digit;
bool got_tail;
usz len = chars.len;
usz last_char = len - 1;
usz index;
double y;
// Skip past first characters
while ((c = chars[index]) == '0')
{
if (index == last_char) return 0.0;
index++;
}
if (c == '.')
{
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
{
rp--;
got_digit = true;
while (last_char != index && (c = chars[++index]) == '0')
{
rp--;
}
}
}
while ((c - '0') < 10u || ((c | 32) - 'a') < 6u || c == '.')
{
if (c == '.')
{
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
got_rad = true;
rp = dc;
}
else
{
got_digit = true;
int d = {|
if (c > '9') return (c | 32) + 10 - 'a';
return c - '0';
|};
switch
{
case dc < 8:
x = x * 16 + d;
case dc < math::DOUBLE_MANT_DIG / 4 + 1:
y += d * (scale /= 16);
got_tail = true;
case d && !got_tail:
y += 0.5 * scale;
got_tail = true;
}
dc++;
}
if (index == last_char) break;
c = chars[++index];
}
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if (!got_rad) rp = dc;
for (; dc < 8; dc++) x *= 16;
long e2;
if ((c | 32) == 'p')
{
long e2val = String.to_long((String)chars[index + 1..]) ?? (NumberConversion.MALFORMED_FLOAT?)!;
e2 = e2val;
}
e2 += 4 * rp - 32;
if (!x) return sign * 0.0;
if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
while (x < 0x80000000)
{
if (y >= 0.5)
{
x += x + 1;
y += y - 1;
}
else
{
x += x;
y += y;
}
e2--;
}
int bits = $bits;
if ($bits > 32 + e2 - $emin)
{
bits = (int)(32 + e2 - $emin);
if (bits < 0) bits = 0;
}
double bias;
if (bits < math::DOUBLE_MANT_DIG)
{
bias = math::copysign(math::scalbn(1, 32 + math::DOUBLE_MANT_DIG - bits - 1), (double)sign);
}
if (bits < 32 && y && !(x & 1))
{
x++;
y = 0;
}
y = bias + sign * (double)x + sign * y;
y -= bias;
if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE?;
return math::scalbn(y, (int)e2);
}
macro String.to_real(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);
}

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

@@ -0,0 +1,337 @@
module std::core::types;
import libc;
fault ConversionResult
{
VALUE_OUT_OF_RANGE,
VALUE_OUT_OF_UNSIGNED_RANGE,
}
/**
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
**/
macro any_to_int(any v, $Type)
{
typeid any_type = v.type;
TypeKind kind = any_type.kindof;
if (kind == TypeKind.ENUM)
{
any_type = any_type.inner;
kind = any_type.kindof;
}
bool is_mixed_signed = $Type.kindof != any_type.kindof;
$Type max = $Type.max;
$Type min = $Type.min;
switch (any_type)
{
case ichar:
ichar c = *(char*)v.ptr;
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
return ($Type)c;
case short:
short s = *(short*)v.ptr;
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)s;
case int:
int i = *(int*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case long:
long l = *(long*)v.ptr;
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)l;
case int128:
int128 i = *(int128*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case char:
char c = *(char*)v.ptr;
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)c;
case ushort:
ushort s = *(ushort*)v.ptr;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)s;
case uint:
uint i = *(uint*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case ulong:
ulong l = *(ulong*)v.ptr;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)l;
case uint128:
uint128 i = *(uint128*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
default:
unreachable();
}
}
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;
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 TypeKind inner_kind($Type) @const
{
$if $Type.kindof == TypeKind.DISTINCT:
return inner_kind($typefrom($Type.inner));
$else
return $Type.kindof;
$endif
}
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 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;
}

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

@@ -0,0 +1,46 @@
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 promote_int(x)
{
$if @is_int(x):
return (double)x;
$else
return x;
$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));

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

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

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 = {};
}

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

@@ -0,0 +1,288 @@
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 STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
const MASK @private = 0b111111;
struct Base64Encoder
{
int 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 void! Base64Encoder.init(&self, String alphabet, int padding = '=')
{
check_alphabet(alphabet, padding)!;
*self = { .padding = padding, .alphabet = alphabet };
}
/**
* 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)
{
if (self.padding >= 0) return (n + 2) / 3 * 4;
usz trailing = n % 3;
return n / 3 * 4 + (trailing * 4 + 2) / 3;
}
/**
* 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?;
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] = self.alphabet[group >> 18 & MASK];
dst[1] = self.alphabet[group >> 12 & MASK];
dst[2] = self.alphabet[group >> 6 & MASK];
dst[3] = self.alphabet[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] = self.alphabet[group >> 18 & MASK];
dst[1] = self.alphabet[group >> 12 & MASK];
if (self.padding >= 0)
{
char pad = (char)self.padding;
dst[2] = pad;
dst[3] = pad;
}
case 2:
uint group = (uint)src[^2] << 16 | (uint)src[^1] << 8;
dst[0] = self.alphabet[group >> 18 & MASK];
dst[1] = self.alphabet[group >> 12 & MASK];
dst[2] = self.alphabet[group >> 6 & MASK];
if (self.padding >= 0)
{
char pad = (char)self.padding;
dst[3] = pad;
}
}
return dn;
}
struct Base64Decoder
{
int padding;
String alphabet;
char[256] reverse;
char invalid;
}
/**
* @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 = '=')
{
check_alphabet(alphabet, padding)!;
*self = { .padding = padding, .alphabet = alphabet };
bool[256] checked;
foreach (i, c : alphabet)
{
checked[c] = true;
self.reverse[c] = (char)i;
}
if (padding < 0)
{
self.invalid = 255;
return;
}
// Find a character for invalid neither in the alphabet nor equal to the padding.
char pad = (char)padding;
foreach (i, ok : checked)
{
if (!ok && (char)i != pad)
{
self.invalid = (char)i;
break;
}
}
}
/**
* 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)
{
usz dn = n / 4 * 3;
usz trailing = n % 4;
if (self.padding >= 0)
{
if (trailing != 0) return Base64Error.INVALID_PADDING?;
// source size is multiple of 4
}
else
{
if (trailing == 1) return Base64Error.INVALID_PADDING?;
dn += trailing * 3 / 4;
}
return 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."
* @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?;
usz trailing = src.len % 4;
char[] src4 = src;
switch
{
case self.padding < 0:
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;
char pad = (char)self.padding;
if (src[^1] == pad) src4 = src[:^4];
}
while (src4.len > 0)
{
char c0 = self.reverse[src4[0]];
char c1 = self.reverse[src4[1]];
char c2 = self.reverse[src4[2]];
char c3 = self.reverse[src4[3]];
switch (self.invalid)
{
case c0:
case c1:
case c2:
case c3:
return Base64Error.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 dn;
src = src[^trailing..];
char c0 = self.reverse[src[0]];
char c1 = self.reverse[src[1]];
if (c0 == self.invalid || c1 == self.invalid) return Base64Error.INVALID_PADDING?;
if (self.padding < 0)
{
switch (src.len)
{
case 2:
uint group = (uint)c0 << 18 | (uint)c1 << 12;
dst[0] = (char)(group >> 16);
case 3:
char c2 = self.reverse[src[2]];
if (c2 == self.invalid) return Base64Error.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=
char pad = (char)self.padding;
switch (pad)
{
case src[2]:
if (src[3] != pad) return Base64Error.INVALID_PADDING?;
uint group = (uint)c0 << 18 | (uint)c1 << 12;
dst[0] = (char)(group >> 16);
dn -= 2;
case src[3]:
char c2 = self.reverse[src[2]];
if (c2 == self.invalid) return Base64Error.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 dn;
}
// 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;
}
}

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

@@ -0,0 +1,64 @@
module std::encoding::csv;
import std::io;
struct CsvReader
{
InStream stream;
String separator;
}
fn void CsvReader.init(&self, InStream stream, String separator = ",")
{
self.stream = stream;
self.separator = separator;
}
fn String[]! CsvReader.read_new_row(self, Allocator allocator = allocator::heap())
{
return self.read_new_row_with_allocator(allocator::temp()) @inline;
}
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator allocator = allocator::heap())
{
@pool(allocator)
{
return io::treadline(self.stream).split(self.separator, .allocator = allocator);
};
}
fn String[]! CsvReader.read_temp_row(self)
{
return self.read_new_row_with_allocator(allocator::temp()) @inline;
}
fn void! CsvReader.skip_row(self) @maydiscard
{
@pool()
{
(void)io::treadline(self.stream);
};
}
macro CsvReader.@each_row(self, int rows = int.max; @body(String[] row))
{
InputStream* stream = self.stream;
String sep = self.separator;
while (rows--)
{
@stack_mem(512; Allocator mem)
{
String[] parts;
@pool()
{
String! s = stream.treadline();
if (catch err = s)
{
if (err == IoError.EOF) return;
return err?;
}
parts = s.split(sep, .allocator = mem);
};
@body(parts);
};
}
}

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

@@ -0,0 +1,387 @@
// 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())
{
JsonContext context = { .last_string = dstring::new_with_capacity(64, allocator), .stream = s, .allocator = allocator };
defer context.last_string.free();
@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
{
@pool()
{
DString t = dstring::temp_with_capacity(32);
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);
JsonTokenType token = advance(context)!;
defer catch map.free();
DString temp_key = dstring::new_with_capacity(32, context.allocator);
defer temp_key.free();
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. We do this to work around the issue
// if the temp allocator should be used as the default allocator.
temp_key.clear();
temp_key.append(string);
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?;
}
}
return STRING;
}

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

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

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

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

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

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

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

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

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

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

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

@@ -0,0 +1,85 @@
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;
}
fn void BitWriter.init(&self, OutStream byte_writer)
{
*self = { .writer = byte_writer };
}
fn void! BitWriter.flush(&self)
{
if (self.len == 0) return;
uint bits = self.bits << (32 - self.len);
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 <= 8
**/
fn void! BitWriter.write_bits(&self, uint bits, uint nbits)
{
if (nbits == 0) return;
uint n = self.len + nbits;
uint to_write = n / 8;
uint left = n % 8;
if (to_write > 0)
{
ulong lbits;
if (self.len > 0) lbits = (ulong)self.bits << (64 - self.len);
lbits |= (ulong)(bits >> left) << (64 - (n - left));
char[8] buffer;
bitorder::write(lbits, &buffer, ULongBE);
io::write_all(self.writer, buffer[:to_write])!;
}
self.bits <<= left;
self.bits |= bits & ((1 << left) - 1);
self.len = left;
}

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

@@ -0,0 +1,191 @@
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 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
{
if (!libc::fputc(c, self.file)) return IoError.EOF?;
}
/**
* @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
*/
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 `File must be initialized`
*/
fn usz! File.write(&self, char[] buffer) @dynamic
{
return os::native_fwrite(self.file, buffer);
}
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_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_temp(String filename)
{
return load_new(filename, allocator::temp());
}
/**
* @require self.file `File must be initialized`
*/
fn void! File.flush(&self) @dynamic
{
libc::fflush(self.file);
}

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

@@ -0,0 +1,463 @@
module std::io;
import std::collections::map;
import libc;
const int PRINTF_NTOA_BUFFER_SIZE = 256;
interface Printable
{
fn String to_new_string(Allocator allocator) @optional;
fn usz! to_format(Formatter* formatter) @optional;
}
fault PrintFault
{
BUFFER_EXCEEDED,
INTERNAL_BUFFER_EXCEEDED,
INVALID_FORMAT_STRING,
MISSING_ARG,
INVALID_ARGUMENT_TYPE,
}
fault FormattingFault
{
UNTERMINATED_FORMAT,
MISSING_ARG,
INVALID_WIDTH_ARG,
INVALID_FORMAT_TYPE,
}
def OutputFn = fn void!(void* buffer, char c);
def FloatType = double;
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;
}
}
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
{
self.out_fn(self.data, c)!;
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_new_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_new_string(mem));
};
}
return SearchResult.MISSING?;
}
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 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);
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)!!);
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_int(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_substr("<struct>");
case UNION:
return self.out_substr("<union>");
case BITSTRUCT:
return self.out_substr("<bitstruct>");
case FUNC:
return self.out_substr("<function>");
case DISTINCT:
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;
self.out_substr("0x")!;
return 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 == char.typeid)
{
return self.out_substr(*(String*)arg);
}
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;
default:
}
return self.out_substr("Invalid type");
}
fn void! out_null_fn(void* data @unused, char c @unused) @private
{
}
fn usz! Formatter.vprintf(&self, String format, any[] anys)
{
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 PrintFault.INVALID_FORMAT_STRING?;
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 PrintFault.INVALID_FORMAT_STRING?;
c = format[i];
}
// evaluate width field
int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
c = format[i];
if (w < 0)
{
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 PrintFault.INVALID_FORMAT_STRING?;
int prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
self.prec = prec < 0 ? 0 : prec;
c = format[i];
}
// evaluate specifier
uint base = 0;
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
any current = anys[variant_index++];
switch (c)
{
case 'd':
base = 10;
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 += self.atoa(float_from_any(current)!!)!;
continue;
case 'F' :
self.flags.uppercase = true;
nextcase;
case 'f':
total_len += self.ftoa(float_from_any(current)!!)!;
continue;
case 'E':
self.flags.uppercase = true;
nextcase;
case 'e':
total_len += self.etoa(float_from_any(current)!!)!;
continue;
case 'G':
self.flags.uppercase = true;
nextcase;
case 'g':
total_len += self.gtoa(float_from_any(current)!!)!;
continue;
case 'c':
total_len += self.out_char(current)!;
continue;
case 's':
if (self.flags.left)
{
usz len = self.out_str(current)!;
total_len += len;
total_len += self.pad(' ', self.width, len)!;
continue;
}
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:
return PrintFault.INVALID_FORMAT_STRING?;
}
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;
uint128 v = int_from_any(current, &is_neg)!!;
total_len += self.ntoa(v, is_neg, base)!;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
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;
}

View File

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

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

@@ -0,0 +1,403 @@
// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::io;
import libc;
enum Seek
{
SET,
CURSOR,
END
}
fault IoError
{
ALREADY_EXISTS,
BUSY,
CANNOT_READ_DIR,
DIR_NOT_EMPTY,
EOF,
FILE_CANNOT_DELETE,
FILE_IS_DIR,
FILE_IS_PIPE,
FILE_NOT_DIR,
FILE_NOT_FOUND,
FILE_NOT_VALID,
GENERAL_ERROR,
ILLEGAL_ARGUMENT,
INCOMPLETE_WRITE,
INTERRUPTED,
INVALID_POSITION,
INVALID_PUSHBACK,
NAME_TOO_LONG,
NOT_SEEKABLE,
NO_PERMISSION,
OUT_OF_SPACE,
OVERFLOW,
READ_ONLY,
SYMLINK_FAILED,
TOO_MANY_DESCRIPTORS,
UNEXPECTED_EOF,
UNKNOWN_ERROR,
UNSUPPORTED_OPERATION,
WOULD_BLOCK,
}
/**
* Read from a stream (default is stdin) to the next "\n"
* or to the end of the stream, whatever comes first.
* "\r" will be filtered from the String.
*
* @param stream `The stream to read from.`
* @require @is_instream(stream) `The stream must implement InStream.`
* @param [inout] allocator `the allocator to use.`
* @return `The string containing the data read.`
**/
macro String! readline(stream = io::stdin(), Allocator allocator = allocator::heap())
{
bool $is_stream = @typeis(stream, InStream);
$if $is_stream:
$typeof(&stream.read_byte) func = &stream.read_byte;
char val = func((void*)stream)!;
$else
char val = stream.read_byte()!;
$endif
if (val == '\n') return "";
@pool(allocator)
{
DString str = dstring::temp_with_capacity(256);
if (val != '\r') str.append(val);
while (1)
{
$if $is_stream:
char! c = func((void*)stream);
$else
char! c = stream.read_byte();
$endif
if (catch err = c)
{
if (err == IoError.EOF) break;
return err?;
}
if (c == '\r') continue;
if (c == '\n') break;
str.append_char(c);
}
return str.copy_str(allocator);
};
}
/**
* Reads a string, see `readline`, except the it is allocated
* on the temporary allocator and does not need to be freed.
*
* @param stream `The stream to read from.`
* @require @is_instream(stream) `The stream must implement InStream.`
* @return `The temporary string containing the data read.`
**/
macro String! treadline(stream = io::stdin())
{
return readline(stream, allocator::temp()) @inline;
}
/**
* Print a value to a stream.
*
* @param out `the stream to print to`
* @param x `the value to print`
* @require @is_outstream(out) `The output must implement OutStream.`
* @return `the number of bytes printed.`
*/
macro usz! fprint(out, x)
{
var $Type = $typeof(x);
$switch ($Type)
$case String: return out.write(x);
$case ZString: return out.write(x.str_view());
$case DString: return out.write(x.str_view());
$default:
$if $assignable(x, String):
return out.write((String)x);
$else
return fprintf(out, "%s", x);
$endif
$endswitch
}
/**
* Prints using a 'printf'-style formatting string.
* See `printf` for details on formatting.
*
* @param [inout] out `The OutStream to print to`
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! fprintf(OutStream out, String format, args...)
{
Formatter formatter;
formatter.init(&out_putstream_fn, &out);
return formatter.vprintf(format, args);
}
/**
* Prints using a 'printf'-style formatting string,
* appending '\n' at the end. See `printf`.
*
* @param [inout] out `The OutStream to print to`
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! fprintfn(OutStream out, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putstream_fn, &out);
usz len = formatter.vprintf(format, args)!;
out.write_byte('\n')!;
if (&out.flush) out.flush()!;
return len + 1;
}
/**
* @require @is_outstream(out) "The output must implement OutStream"
*/
macro usz! fprintn(out, x = "")
{
usz len = fprint(out, x)!;
out.write_byte('\n')!;
$switch
$case @typeid(out) == OutStream.typeid:
if (&out.flush) out.flush()!;
$case $defined(out.flush):
out.flush()!;
$endswitch
return len + 1;
}
/**
* Print any value to stdout.
**/
macro void print(x)
{
(void)fprint(io::stdout(), x);
}
/**
* Print any value to stdout, appending an '\n after.
*
* @param x "The value to print"
**/
macro void printn(x = "")
{
(void)fprintn(io::stdout(), x);
}
/**
* Print any value to stderr.
**/
macro void eprint(x)
{
(void)fprint(io::stderr(), x);
}
/**
* Print any value to stderr, appending an '\n after.
*
* @param x "The value to print"
**/
macro void eprintn(x)
{
(void)fprintn(io::stderr(), x);
}
fn void! out_putstream_fn(void* data, char c) @private
{
OutStream* stream = data;
return (*stream).write_byte(c);
}
fn void! out_putchar_fn(void* data @unused, char c) @private
{
libc::putchar(c);
}
/**
* Prints using a 'printf'-style formatting string.
* To print integer numbers, use "%d" or "%x"/"%X,
* the latter gives the hexadecimal representation.
*
* All types can be printed using "%s" which gives
* the default representation of the value.
*
* To create a custom output for a type, implement
* the Printable interface.
*
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! printf(String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
return formatter.vprintf(format, args);
}
/**
* Prints using a 'printf'-style formatting string,
* appending '\n' at the end. See `printf`.
*
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! printfn(String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
usz len = formatter.vprintf(format, args)!;
putchar('\n');
io::stdout().flush()!;
return len + 1;
}
/**
* Prints using a 'printf'-style formatting string
* to stderr.
*
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! eprintf(String format, args...) @maydiscard
{
Formatter formatter;
OutStream stream = stderr();
formatter.init(&out_putstream_fn, &stream);
return formatter.vprintf(format, args);
}
/**
* Prints using a 'printf'-style formatting string,
* to stderr appending '\n' at the end. See `printf`.
*
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! eprintfn(String format, args...) @maydiscard
{
Formatter formatter;
OutStream stream = stderr();
formatter.init(&out_putstream_fn, &stream);
usz len = formatter.vprintf(format, args)! + 1;
stderr().write_byte('\n')!;
stderr().flush()!;
return len;
}
/**
* Prints using a 'printf'-style formatting string,
* to a string buffer. See `printf`.
*
* @param [inout] buffer `The buffer to print to`
* @param [in] format `The printf-style format string`
* @return `a slice formed from the "buffer" with the resulting length.`
**/
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
{
Formatter formatter;
BufferData data = { .buffer = buffer };
formatter.init(&out_buffer_fn, &data);
usz size = formatter.vprintf(format, args)!;
return buffer[:data.written];
}
// Used to print to a buffer.
fn void! out_buffer_fn(void *data, char c) @private
{
BufferData *buffer_data = data;
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?;
buffer_data.buffer[buffer_data.written++] = c;
}
// Used for buffer printing
struct BufferData @private
{
char[] buffer;
usz written;
}
// Only available with LIBC
module std::io @if (env::LIBC);
import libc;
/**
* Libc `putchar`, prints a single character to stdout.
**/
fn void putchar(char c) @inline
{
libc::putchar(c);
}
/**
* Get standard out.
*
* @return `stdout as a File`
**/
fn File* stdout()
{
static File file;
if (!file.file) file = file::from_handle(libc::stdout());
return &file;
}
/**
* Get standard err.
*
* @return `stderr as a File`
**/
fn File* stderr()
{
static File file;
if (!file.file) file = file::from_handle(libc::stderr());
return &file;
}
/**
* Get standard in.
*
* @return `stdin as a File`
**/
fn File* stdin()
{
static File file;
if (!file.file) file = file::from_handle(libc::stdin());
return &file;
}
module std::io @if(!env::LIBC);
File stdin_file;
File stdout_file;
File stderr_file;
fn void putchar(char c) @inline
{
(void)stdout_file.write_byte(c);
}
fn File* stdout()
{
return &stdout_file;
}
fn File* stderr()
{
return &stderr_file;
}
fn File* stdin()
{
return &stdin_file;
}

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

@@ -0,0 +1,30 @@
module std::io::os;
import std::io::path, libc, std::os;
macro void! native_chdir(Path path)
{
$switch
$case env::POSIX:
if (posix::chdir(path.as_zstr()))
{
switch (libc::errno())
{
case errno::EACCES: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::ENOTDIR: return IoError.FILE_NOT_DIR?;
case errno::ENOENT: return IoError.FILE_NOT_FOUND?;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
default: return IoError.GENERAL_ERROR?;
}
}
$case env::WIN32:
@pool()
{
// TODO improve with better error handling.
if (win32::setCurrentDirectoryW(path.str_view().to_temp_utf16()!!)) return;
};
return IoError.GENERAL_ERROR?;
$default:
return IoError.UNSUPPORTED_OPERATION?;
$endswitch
}

129
lib/std/io/os/file_libc.c3 Normal file
View File

@@ -0,0 +1,129 @@
module std::io::os @if(env::LIBC);
import libc;
/**
* @require mode.len > 0
* @require filename.len > 0
**/
fn void*! native_fopen(String filename, String mode) @inline
{
@pool()
{
$if env::WIN32:
void* file = libc::_wfopen(filename.to_temp_wstring(), mode.to_temp_wstring())!;
$else
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
$endif
return file ?: file_open_errno()?;
};
}
fn void! native_remove(String filename)
{
@pool()
{
$if env::WIN32:
CInt result = libc::_wremove(filename.to_temp_wstring())!;
$else
CInt result = libc::remove(filename.zstr_tcopy());
$endif
if (result)
{
switch (libc::errno())
{
case errno::ENOENT:
return IoError.FILE_NOT_FOUND?;
case errno::EACCES:
default:
return IoError.FILE_CANNOT_DELETE?;
}
}
};
}
/**
* @require mode.len > 0
* @require filename.len > 0
**/
fn void*! native_freopen(void* file, String filename, String mode) @inline
{
@pool()
{
$if env::WIN32:
file = libc::_wfreopen(filename.to_temp_wstring(), mode.to_temp_wstring(), file)!;
$else
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
$endif
return file ?: file_open_errno()?;
};
}
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
{
if (libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode)) return file_seek_errno()?;
}
fn usz! native_ftell(CFile file) @inline
{
long index = libc::ftell(file);
return index >= 0 ? (usz)index : file_seek_errno()?;
}
fn usz! native_fwrite(CFile file, char[] buffer) @inline
{
return libc::fwrite(buffer.ptr, 1, buffer.len, file);
}
fn usz! native_fread(CFile file, char[] buffer) @inline
{
return libc::fread(buffer.ptr, 1, buffer.len, file);
}
macro anyfault file_open_errno() @local
{
switch (libc::errno())
{
case errno::EACCES: return IoError.NO_PERMISSION;
case errno::EDQUOT: return IoError.OUT_OF_SPACE;
case errno::EBADF: return IoError.FILE_NOT_VALID;
case errno::EEXIST: return IoError.ALREADY_EXISTS;
case errno::EINTR: return IoError.INTERRUPTED;
case errno::EFAULT: return IoError.GENERAL_ERROR;
case errno::EISDIR: return IoError.FILE_IS_DIR;
case errno::ELOOP: return IoError.SYMLINK_FAILED;
case errno::EMFILE: return IoError.TOO_MANY_DESCRIPTORS;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG;
case errno::ENFILE: return IoError.OUT_OF_SPACE;
case errno::ENOTDIR: return IoError.FILE_NOT_DIR;
case errno::ENOENT: return IoError.FILE_NOT_FOUND;
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
case errno::EOVERFLOW: return IoError.OVERFLOW;
case errno::EROFS: return IoError.READ_ONLY;
case errno::EOPNOTSUPP: return IoError.UNSUPPORTED_OPERATION;
case errno::EIO: return IoError.INCOMPLETE_WRITE;
case errno::EWOULDBLOCK: return IoError.WOULD_BLOCK;
default: return IoError.UNKNOWN_ERROR;
}
}
macro anyfault file_seek_errno() @local
{
switch (libc::errno())
{
case errno::ESPIPE: return IoError.FILE_IS_PIPE;
case errno::EPIPE: return IoError.FILE_IS_PIPE;
case errno::EOVERFLOW: return IoError.OVERFLOW;
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
case errno::EIO: return IoError.INCOMPLETE_WRITE;
case errno::EINVAL: return IoError.INVALID_POSITION;
case errno::EINTR: return IoError.INTERRUPTED;
case errno::EFBIG: return IoError.OUT_OF_SPACE;
case errno::EBADF: return IoError.FILE_NOT_VALID;
case errno::EAGAIN: return IoError.WOULD_BLOCK;
default: return IoError.UNKNOWN_ERROR;
}
}

View File

@@ -0,0 +1,75 @@
module std::io::os @if(env::NO_LIBC);
import libc;
def FopenFn = fn void*!(String, String);
def FreopenFn = fn void*!(void*, String, String);
def FcloseFn = fn void!(void*);
def FseekFn = fn void!(void*, isz, Seek);
def FtellFn = fn usz!(void*);
def FwriteFn = fn usz!(void*, char[] buffer);
def FreadFn = fn usz!(void*, char[] buffer);
def RemoveFn = fn void!(String);
FopenFn native_fopen_fn @weak @if(!$defined(native_fopen_fn));
FcloseFn native_fclose_fn @weak @if(!$defined(native_fclose_fn));
FreopenFn native_freopen_fn @weak @if(!$defined(native_freopen_fn));
FseekFn native_fseek_fn @weak @if(!$defined(native_fseek_fn));
FtellFn native_ftell_fn @weak @if(!$defined(native_ftell_fn));
FwriteFn native_fwrite_fn @weak @if(!$defined(native_fwrite_fn));
FreadFn native_fread_fn @weak @if(!$defined(native_fread_fn));
RemoveFn native_remove_fn @weak @if(!$defined(native_remove_fn));
/**
* @require mode.len > 0
* @require filename.len > 0
**/
fn void*! native_fopen(String filename, String mode) @inline
{
if (native_fopen_fn) return native_fopen_fn(filename, mode);
return IoError.UNSUPPORTED_OPERATION?;
}
/**
* Delete a file.
*
* @require filename.len > 0
**/
fn void! native_remove(String filename) @inline
{
if (native_remove_fn) return native_remove_fn(filename);
return IoError.UNSUPPORTED_OPERATION?;
}
/**
* @require mode.len > 0
* @require filename.len > 0
**/
fn void*! native_freopen(void* file, String filename, String mode) @inline
{
if (native_freopen_fn) return native_freopen_fn(file, filename, mode);
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
{
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! native_ftell(CFile file) @inline
{
if (native_ftell_fn) return native_ftell_fn(file);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! native_fwrite(CFile file, char[] buffer) @inline
{
if (native_fwrite_fn) return native_fwrite_fn(file, buffer);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! native_fread(CFile file, char[] buffer) @inline
{
if (native_fread_fn) return native_fread_fn(file, buffer);
return IoError.UNSUPPORTED_OPERATION?;
}

116
lib/std/io/os/fileinfo.c3 Normal file
View File

@@ -0,0 +1,116 @@
module std::io::os;
import libc, std::os, std::io;
fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX)
{
@pool()
{
$if env::DARWIN || env::LINUX:
int res = libc::stat(path.zstr_tcopy(), stat);
$else
unreachable("Stat unimplemented");
int res = 0;
$endif
if (res != 0)
{
switch (libc::errno())
{
case errno::EBADF:
return IoError.FILE_NOT_VALID?;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return IoError.GENERAL_ERROR?;
case errno::EACCES:
return IoError.NO_PERMISSION?;
case errno::ELOOP:
return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG:
return IoError.NAME_TOO_LONG?;
case errno::ENOENT:
return IoError.FILE_NOT_FOUND?;
case errno::ENOTDIR:
return IoError.FILE_NOT_DIR?;
case errno::EOVERFLOW:
return IoError.GENERAL_ERROR?;
default:
return IoError.UNKNOWN_ERROR?;
}
}
};
}
fn usz! native_file_size(String path) @if(env::WIN32)
{
@pool()
{
Win32_FILE_ATTRIBUTE_DATA data;
win32::getFileAttributesExW(path.to_temp_wstring()!, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
Win32_LARGE_INTEGER size;
size.lowPart = data.nFileSizeLow;
size.highPart = data.nFileSizeHigh;
return (usz)size.quadPart;
};
}
fn usz! native_file_size(String path) @if(!env::WIN32 && !env::DARWIN)
{
File f = file::open(path, "r")!;
defer (void)f.close();
return f.seek(0, Seek.END)!;
}
fn usz! native_file_size(String path) @if(env::DARWIN)
{
Stat stat;
native_stat(&stat, path)!;
return stat.st_size;
}
fn bool native_file_or_dir_exists(String path)
{
$switch
$case env::DARWIN:
$case env::LINUX:
Stat stat;
return @ok(native_stat(&stat, path));
$case env::WIN32:
@pool()
{
return (bool)win32::pathFileExistsW(path.to_temp_utf16()) ?? false;
};
$case env::POSIX:
@pool()
{
return posix::access(path.zstr_tcopy(), 0 /* F_OK */) != -1;
};
$default:
unreachable("Not supported");
$endswitch
}
fn bool native_is_file(String path)
{
$switch
$case env::DARWIN:
$case env::LINUX:
Stat stat;
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG);
$default:
File! f = file::open(path, "r");
defer (void)f.close();
return @ok(f);
$endswitch
}
fn bool native_is_dir(String path)
{
$if env::DARWIN || env::LINUX:
Stat stat;
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFDIR);
$else
return native_file_or_dir_exists(path) && !native_is_file(path);
$endif
}

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

@@ -0,0 +1,41 @@
module std::io::os;
import libc, std::os;
macro String! getcwd(Allocator allocator = allocator::heap())
{
$switch
$case env::WIN32:
const DEFAULT_BUFFER = 256;
Char16[DEFAULT_BUFFER] buffer;
WString res = win32::_wgetcwd(&buffer, DEFAULT_BUFFER);
bool free = false;
defer if (free) libc::free(res);
if (!res)
{
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
res = win32::_wgetcwd(null, 0);
free = true;
}
Char16[] str16 = res[:win32::wcslen(res)];
return string::new_from_utf16(str16, allocator);
$case env::POSIX:
const usz DEFAULT_BUFFER = 256;
char[DEFAULT_BUFFER] buffer;
ZString res = posix::getcwd(&buffer, DEFAULT_BUFFER);
bool free = false;
if (!res)
{
// Improve error
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
res = posix::getcwd(null, 0);
free = true;
}
defer if (free) libc::free((void*)res);
return res.copy(allocator);
$default:
return IoError.UNSUPPORTED_OPERATION?;
$endswitch
}

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

@@ -0,0 +1,51 @@
module std::io::os @if(env::POSIX);
import std::io, std::os;
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
{
PathList list;
list.new_init(.allocator = allocator);
DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)".");
defer if (directory) posix::closedir(directory);
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
Posix_dirent* entry;
while ((entry = posix::readdir(directory)))
{
String name = ((ZString)&entry.name).str_view();
if (!name || name == "." || name == "..") continue;
if (entry.d_type == posix::DT_LNK && no_symlinks) continue;
if (entry.d_type == posix::DT_DIR && no_dirs) continue;
Path path = path::new(name, allocator)!!;
list.push(path);
}
return list;
}
module std::io::os @if(env::WIN32);
import std::time, std::os, std::io;
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
{
PathList list;
list.new_init(.allocator = allocator);
@pool(allocator)
{
WString result = dir.str_view().tconcat(`\*`).to_temp_wstring()!!;
Win32_WIN32_FIND_DATAW find_data;
Win32_HANDLE find = win32::findFirstFileW(result, &find_data);
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
defer win32::findClose(find);
do
{
if (no_dirs && (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)) continue;
@pool(allocator)
{
String filename = string::temp_from_wstring((WString)&find_data.cFileName)!;
if (filename == ".." || filename == ".") continue;
list.push(path::new(filename, allocator)!);
};
} while (win32::findNextFileW(find, &find_data));
return list;
};
}

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

@@ -0,0 +1,50 @@
module std::io::os;
import libc;
import std::io::path;
import std::os::win32;
import std::os::posix;
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
{
$switch
$case env::POSIX:
if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true;
switch (libc::errno())
{
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::EDQUOT:
case errno::ENOSPC: return IoError.OUT_OF_SPACE?;
case errno::EISDIR:
case errno::EEXIST: return false;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
case errno::ENOTDIR: return IoError.FILE_NOT_FOUND?;
default: return IoError.GENERAL_ERROR?;
}
$case env::WIN32:
@pool()
{
// TODO security attributes
if (win32::createDirectoryW(path.str_view().to_temp_utf16()!!, null)) return true;
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;
case win32::ERROR_DISK_FULL:
return IoError.OUT_OF_SPACE?;
case win32::ERROR_ALREADY_EXISTS:
return false;
case win32::ERROR_PATH_NOT_FOUND:
return IoError.FILE_NOT_FOUND?;
default:
return IoError.GENERAL_ERROR?;
}
};
$default:
return IoError.UNSUPPORTED_OPERATION?;
$endswitch
}

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

@@ -0,0 +1,48 @@
module std::io::os;
import libc;
import std::io::path;
import std::os::win32;
import std::os::posix;
macro bool! native_rmdir(Path path)
{
$switch
$case env::POSIX:
if (!posix::rmdir(path.as_zstr())) return true;
switch (libc::errno())
{
case errno::EBUSY: return IoError.BUSY?;
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::ENOTDIR:
case errno::ENOENT: return false;
case errno::ENOTEMPTY: return IoError.DIR_NOT_EMPTY?;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
default: return IoError.GENERAL_ERROR?;
}
$case env::WIN32:
@pool()
{
if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true;
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;
case win32::ERROR_CURRENT_DIRECTORY:
return IoError.BUSY?;
case win32::ERROR_DIR_NOT_EMPTY:
return IoError.DIR_NOT_EMPTY?;
case win32::ERROR_DIRECTORY:
case win32::ERROR_PATH_NOT_FOUND:
return false;
default:
return IoError.GENERAL_ERROR?;
}
};
$default:
return IoError.UNSUPPORTED_OPERATION?;
$endswitch
}

64
lib/std/io/os/rmtree.c3 Normal file
View File

@@ -0,0 +1,64 @@
module std::io::os @if(env::POSIX);
import std::io, std::os, libc;
/**
* @require dir.str_view()
**/
fn void! native_rmtree(Path dir)
{
DIRPtr directory = posix::opendir(dir.as_zstr());
defer if (directory) posix::closedir(directory);
if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?;
Posix_dirent* entry;
while ((entry = posix::readdir(directory)))
{
@pool()
{
String name = ((ZString)&entry.name).str_view();
if (!name || name == "." || name == "..") continue;
Path new_path = dir.temp_append(name)!;
if (entry.d_type == posix::DT_DIR)
{
native_rmtree(new_path)!;
continue;
}
if (libc::remove(new_path.as_zstr()))
{
// TODO improve
return IoError.GENERAL_ERROR?;
}
};
}
os::native_rmdir(dir)!;
}
module std::io::os @if(env::WIN32);
import std::io, std::time, std::os;
fn void! native_rmtree(Path path)
{
Win32_WIN32_FIND_DATAW find_data;
String s = path.str_view().tconcat("\\*");
Win32_HANDLE find = win32::findFirstFileW(s.to_temp_utf16(), &find_data)!;
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
defer win32::findClose(find);
do
{
@pool()
{
String filename = string::new_from_wstring((WString)&find_data.cFileName, allocator::temp())!;
if (filename == "." || filename == "..") continue;
Path file_path = path.temp_append(filename)!;
if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)
{
native_rmtree(file_path)!;
}
else
{
win32::deleteFileW(file_path.str_view().to_temp_wstring()!!);
}
};
} while (win32::findNextFileW(find, &find_data) != 0);
os::native_rmdir(path)!;
}

View File

@@ -0,0 +1,32 @@
module std::io::os @if(env::LIBC);
import std::io::path, std::os;
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(!env::WIN32)
{
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
{
String tmpdir = env::get_var(env) ?? "";
if (tmpdir) return path::new(tmpdir, allocator);
}
return path::new("/tmp", allocator);
}
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(env::WIN32)
{
@pool(allocator)
{
Win32_DWORD len = win32::getTempPathW(0, null);
if (!len) return IoError.GENERAL_ERROR?;
Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1);
if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
return path::new(string::temp_from_utf16(buff[:len]), allocator);
};
}
module std::io::os @if(env::NO_LIBC);
import std::io::path;
macro Path! native_temp_directory(Allocator allocator = allocator::heap())
{
return IoError.UNSUPPORTED_OPERATION?;
}

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

@@ -0,0 +1,595 @@
module std::io::path;
import std::collections::list, std::io::os;
import std::os::win32;
const PathEnv DEFAULT_PATH_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX;
const char PREFERRED_SEPARATOR_WIN32 = '\\';
const char PREFERRED_SEPARATOR_POSIX = '/';
const char PREFERRED_SEPARATOR = env::WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
def PathList = List(<Path>);
fault PathResult
{
INVALID_PATH,
NO_PARENT,
}
def Path = PathImp;
struct PathImp (Printable)
{
String path_string;
PathEnv env;
}
enum PathEnv
{
WIN32,
POSIX
}
fn Path! new_cwd(Allocator allocator = allocator::heap())
{
@pool(allocator)
{
return new(os::getcwd(allocator::temp()), allocator);
};
}
fn Path! getcwd(Allocator allocator = allocator::heap()) @deprecated("Use new_cwd()")
{
@pool(allocator)
{
return new(os::getcwd(allocator::temp()), allocator);
};
}
fn bool is_dir(Path path) => os::native_is_dir(path.str_view());
fn bool is_file(Path path) => os::native_is_file(path.str_view());
fn usz! file_size(Path path) => os::native_file_size(path.str_view());
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
fn Path! temp_cwd() => new_cwd(allocator::temp()) @inline;
fn Path! tgetcwd() @deprecated("Use temp_cwd()") => new_cwd(allocator::temp()) @inline;
fn void! chdir(Path path) => os::native_chdir(path) @inline;
fn Path! temp_directory(Allocator allocator = allocator::heap()) => os::native_temp_directory(allocator);
fn void! delete(Path path) => os::native_remove(path.str_view()) @inline;
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
{
return c == '/' || (c == '\\' && path_env == PathEnv.WIN32);
}
macro bool is_posix_separator(char c)
{
return c == '/' || c == '\\';
}
macro bool is_win32_separator(char c)
{
return c == '/' || c == '\\';
}
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap()) @deprecated("use new_ls")
{
return new_ls(dir, no_dirs, no_symlinks, mask, allocator);
}
fn PathList! temp_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "")
{
return new_ls(dir, no_dirs, no_symlinks, mask, allocator::temp()) @inline;
}
fn PathList! new_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap())
{
$if $defined(os::native_ls):
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator);
$else
return IoError.UNSUPPORTED_OPERATION?;
$endif
}
enum MkdirPermissions
{
NORMAL,
USER_ONLY,
USER_AND_ADMIN
}
fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
if (is_dir(path)) return false;
if (exists(path)) return IoError.FILE_NOT_DIR?;
if (recursive)
{
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
}
if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?;
return os::native_mkdir(path, permissions);
}
fn bool! rmdir(Path path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
return os::native_rmdir(path);
}
fn void! rmtree(Path path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
$if $defined(os::native_rmtree):
return os::native_rmtree(path);
$else
return IoError.UNSUPPORTED_OPERATION?;
$endif
}
fn Path! new(String path, Allocator allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
{
return { normalize(path.copy(allocator), path_env), path_env };
}
fn Path! temp_new(String path, PathEnv path_env = DEFAULT_PATH_ENV)
{
return new(path, allocator::temp(), path_env);
}
fn Path! new_win32_wstring(WString path, Allocator allocator = allocator::heap())
{
@pool(allocator)
{
return path::new(string::temp_from_wstring(path)!, .allocator = allocator);
};
}
fn Path! new_windows(String path, Allocator allocator = allocator::heap())
{
return new(path, allocator, WIN32);
}
fn Path! new_posix(String path, Allocator allocator = allocator::heap())
{
return new(path, allocator, POSIX);
}
fn bool Path.equals(self, Path p2)
{
return self.env == p2.env && self.path_string == p2.path_string;
}
fn Path! Path.append(self, String filename, Allocator allocator = allocator::heap()) @deprecated("Use path.new_append(...)")
{
return self.new_append(filename, allocator) @inline;
}
/**
* Append the string to the current path.
*
* @param [in] filename
**/
fn Path! Path.new_append(self, String filename, Allocator allocator = allocator::heap())
{
if (!self.path_string.len) return new(filename, allocator, self.env)!;
assert(!is_separator(self.path_string[^1], self.env));
@pool(allocator)
{
DString dstr = dstring::temp_with_capacity(self.path_string.len + 1 + filename.len);
dstr.append(self.path_string);
dstr.append(PREFERRED_SEPARATOR);
dstr.append(filename);
return { normalize(dstr.copy_str(allocator), self.env), self.env };
};
}
fn Path! Path.temp_append(self, String filename) => self.new_append(filename, allocator::temp());
fn Path! Path.tappend(self, String filename) @deprecated("Use path.temp_append(...)") => self.new_append(filename, allocator::temp());
fn usz Path.start_of_base_name(self) @local
{
String path_str = self.path_string;
if (!path_str.len) return 0;
if (self.env == PathEnv.WIN32)
{
if (try index = path_str.rindex_of_char('\\'))
{
// c:\ style path, we're done!
if (path_str[0] != '\\') return index + 1;
// Handle \\server\foo
// Find the \ before "foo"
usz last_index = 2 + path_str[2..].index_of_char('\\')!!;
// If they don't match, we're done
assert(last_index <= index, "Invalid normalized, path %d vs %s in %s", last_index, index, path_str);
if (last_index != index) return index + 1;
// Otherwise just default to the volume length.
}
return volume_name_len(path_str, self.env)!!;
}
return path_str.rindex_of_char('/') + 1 ?? 0;
}
fn bool! Path.is_absolute(self)
{
String path_str = self.str_view();
if (!path_str.len) return false;
usz path_start = volume_name_len(path_str, self.env)!;
if (path_start > 0 && path_str[0] == '\\') return true;
return path_start < path_str.len && is_separator(path_str[path_start], self.env);
}
fn Path! Path.absolute(self, Allocator allocator = allocator::heap()) @deprecated("Use path.new_absolute()")
{
return self.new_absolute(allocator) @inline;
}
/**
* @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
**/
fn Path! Path.new_absolute(self, Allocator allocator = allocator::heap())
{
String path_str = self.str_view();
if (!path_str.len) return PathResult.INVALID_PATH?;
if (self.is_absolute()!) return new(path_str, allocator, self.env);
if (path_str == ".")
{
@pool(allocator)
{
String cwd = os::getcwd(allocator::temp())!;
return new(cwd, allocator, self.env);
};
}
$if DEFAULT_PATH_ENV == WIN32:
@pool(allocator)
{
const usz BUFFER_LEN = 4096;
WString buffer = (WString)mem::temp_alloc_array(Char16, BUFFER_LEN);
buffer = win32::_wfullpath(buffer, path_str.to_temp_wstring()!, BUFFER_LEN);
if (!buffer) return PathResult.INVALID_PATH?;
return { string::new_from_wstring(buffer, allocator), WIN32 };
};
$else
String cwd = os::getcwd(allocator::temp())!;
return Path { cwd, self.env }.new_append(path_str, allocator)!;
$endif
}
fn String Path.basename(self)
{
usz basename_start = self.start_of_base_name();
String path_str = self.path_string;
if (basename_start == path_str.len) return "";
return path_str[basename_start..];
}
fn String Path.dirname(self)
{
usz basename_start = self.start_of_base_name();
String path_str = self.path_string;
if (basename_start == 0) return ".";
usz start = volume_name_len(path_str, self.env)!!;
if (basename_start <= start + 1)
{
if (self.env == WIN32 && basename_start > start && path_str[0..1] == `\\`)
{
return path_str[:basename_start - 1];
}
return path_str[:basename_start];
}
return path_str[:basename_start - 1];
}
/**
* Test if the path has the given extension, so given the path /foo/bar.c3
* this would be true matching the extension "c3"
*
* @param [in] extension `The extension name (not including the leading '.')`
* @require extension.len > 0 : `The extension cannot be empty`
* @return `true if the extension matches`
**/
fn bool Path.has_extension(self, String extension)
{
String basename = self.basename();
if (basename.len <= extension.len) return false;
if (basename[^extension.len + 1] != '.') return false;
return basename[^extension.len..] == extension;
}
fn String! Path.extension(self)
{
String basename = self.basename();
usz index = basename.rindex_of(".")!;
// Plain ".foo" does not have an
if (index == 0) return SearchResult.MISSING?;
if (index == basename.len) return "";
return basename[index + 1..];
}
fn String Path.volume_name(self)
{
usz len = volume_name_len(self.str_view(), self.env)!!;
if (!len) return "";
return self.path_string[:len];
}
fn usz! volume_name_len(String path, PathEnv path_env) @local
{
usz len = path.len;
if (len < 2 || path_env != PathEnv.WIN32) return 0;
switch (path[0])
{
case '\\':
// "\\" paths.. must be longer than 2
if (len == 2) return 0;
int count = 1;
while (count < len && path[count] == '\\') count++;
// Not 2 => folded paths
if (count != 2) return 0;
// Check that we have a name followed by '\'
isz base_found = 0;
for (usz i = 2; i < len; i++)
{
char c = path[i];
if (is_win32_separator(c))
{
if (base_found) return i;
base_found = i;
continue;
}
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
}
if (base_found > 0 && base_found + 1 < len) return len;
return PathResult.INVALID_PATH?;
case 'A'..'Z':
case 'a'..'z':
return path[1] == ':' ? 2 : 0;
default:
return 0;
}
}
fn Path! Path.parent(self)
{
if (self.path_string.len == 1 && is_separator(self.path_string[0], self.env)) return PathResult.NO_PARENT?;
foreach_r(i, c : self.path_string)
{
if (is_separator(c, self.env))
{
return { self.path_string[:i], self.env };
}
}
return PathResult.NO_PARENT?;
}
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
{
if (!path_str.len) return "";
usz path_start = volume_name_len(path_str, path_env)!;
if (path_start > 0 && path_env == PathEnv.WIN32)
{
for (usz i = 0; i < path_start; i++) if (path_str[i] == '/') path_str[i] = '\\';
}
usz path_len = path_str.len;
if (path_start == path_len) return path_str;
char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
usz len = path_start;
bool has_root = is_separator(path_str[path_start], path_env);
if (has_root)
{
path_str[len++] = path_separator;
path_start++;
}
// It is safe to write it as true, since we already dealt with /foo.
// This allows us to avoid checking whether it is the start of the path.
bool previous_was_separator = true;
for (usz i = path_start; i < path_len; i++)
{
char c = path_str[i];
// Fold foo///bar into foo/bar
if (is_separator(c, path_env))
{
// Fold //
if (previous_was_separator) continue;
// New /, so mark and rewrite
path_str.ptr[len++] = path_separator;
previous_was_separator = true;
continue;
}
// The rest are names of the path elements, so check that the
// characters are valid.
if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH?;
// If we have '.' after a separator
if (c == '.' && previous_was_separator)
{
// Get the number of dots until next separator, expecting 1 or 2
bool is_last = i == path_len - 1;
int dots = 1;
if (!is_last)
{
char next = path_str[i + 1];
switch
{
case next == '.':
dots = 2;
is_last = i == path_len - 2;
if (!is_last && !is_separator(path_str[i + 2], path_env))
{
dots = 0;
}
case !is_separator(next, path_env):
dots = 0;
}
}
switch (dots)
{
case 1:
// /./abc -> skip to /./abc
// ^ ^
i++;
continue;
case 2:
// This is an error: /a/../..
if (len == path_start && has_root) return PathResult.INVALID_PATH?;
// If this .. at the start, or after ../? If so, we just copy ..
if (len == path_start ||
(len - path_start >= 3 && path_str[len - 1] == path_separator
&& path_str[len - 3] == '.' && path_str[len - 3] == '.' &&
(len - 3 == 0 || path_str[len - 4] == path_separator)))
{
if (i != len)
{
path_str[len] = '.';
path_str[len + 1] = '.';
}
len += 2;
if (len < path_len) path_str[len++] = path_separator;
i += 2;
continue;
}
// Step back, now looking at '/' abc/def/. -> abc/def/
len--;
// Step back until finding a separator or the start.
while (len > path_start && !is_separator(path_str[len - 1], path_env))
{
len--;
}
// Reading, we go from /../abc to /../abc
// ^ ^
i += 2;
continue;
default:
break;
}
}
if (i != len) path_str[len] = c;
previous_was_separator = false;
len++;
}
if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--;
if (path_str.len > len) path_str.ptr[len] = 0;
// Empty path after normalization -> "."
if (!len) return ".";
return path_str[:len];
}
fn ZString Path.as_zstr(self) => (ZString)self.path_string.ptr;
fn String Path.root_directory(self)
{
String path_str = self.str_view();
usz len = path_str.len;
if (!len) return "";
if (path_str == ".") return ".";
if (self.env == PathEnv.WIN32)
{
usz root_len = volume_name_len(path_str, self.env)!!;
if (root_len == len || !is_win32_separator(path_str[root_len])) return "";
return path_str[root_len..root_len];
}
if (!is_posix_separator(path_str[0])) return "";
for (usz i = 1; i < len; i++)
{
if (is_posix_separator(path_str[i]))
{
return path_str[:i];
}
}
return path_str;
}
def PathWalker = fn bool! (Path, bool is_dir, void*);
/*
* Walk the path recursively. PathWalker is run on every file and
* directory found. Return true to abort the walk.
* @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
*/
fn bool! Path.walk(self, PathWalker w, void* data)
{
const PATH_MAX = 512;
@stack_mem(PATH_MAX; Allocator allocator)
{
Path abs = self.new_absolute(allocator)!;
PathList files = new_ls(abs, .allocator = allocator)!;
foreach (f : files)
{
if (f.str_view() == "." || f.str_view() == "..") continue;
f = abs.new_append(f.str_view(), allocator)!;
bool is_directory = is_dir(f);
if (w(f, is_directory, data)!) return true;
if (is_directory && f.walk(w, data)!) return true;
}
};
return false;
}
fn String Path.str_view(self) @inline
{
return self.path_string;
}
fn bool Path.has_suffix(self, String str)
{
return self.str_view().ends_with(str);
}
fn void Path.free_with_allocator(self, Allocator allocator)
{
allocator::free(allocator, self.path_string.ptr);
}
fn void Path.free(self)
{
free(self.path_string.ptr);
}
fn usz! Path.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.print(self.str_view());
}
fn String Path.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return self.str_view().copy(allocator);
}
const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true,
['/'] = true,
};
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
[0..31] = true,
['>'] = true,
['<'] = true,
[':'] = true,
['\"'] = true,
['/'] = true,
['\\'] = true,
['|'] = true,
['?'] = true,
['*'] = true,
};
macro bool is_reserved_win32_path_char(char c)
{
return RESERVED_PATH_CHAR_WIN32[c];
}
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_PATH_ENV)
{
return path_env == PathEnv.WIN32
? RESERVED_PATH_CHAR_WIN32[c]
: RESERVED_PATH_CHAR_POSIX[c];
}

228
lib/std/io/stream.c3 Normal file
View File

@@ -0,0 +1,228 @@
module std::io;
import std::math;
interface InStream
{
fn void! close() @optional;
fn usz! seek(isz offset, Seek seek) @optional;
fn usz len() @optional;
fn usz! available() @optional;
fn usz! read(char[] buffer);
fn char! read_byte();
fn usz! write_to(OutStream out) @optional;
fn void! pushback_byte() @optional;
}
interface OutStream
{
fn void! destroy() @optional;
fn void! close() @optional;
fn void! flush() @optional;
fn usz! write(char[] bytes);
fn void! write_byte(char c);
fn usz! read_to(InStream in) @optional;
}
fn usz! available(InStream s)
{
if (&s.available) return s.available();
if (&s.seek)
{
usz curr = s.seek(0, Seek.CURSOR)!;
usz len = s.seek(0, Seek.END)!;
s.seek(curr, Seek.SET)!;
return len - curr;
}
return 0;
}
macro bool @is_instream(#expr)
{
return $assignable(#expr, InStream);
}
macro bool @is_outstream(#expr)
{
return $assignable(#expr, OutStream);
}
/**
* @param [&out] ref
* @require @is_instream(stream)
**/
macro usz! read_any(stream, any ref)
{
return read_all(stream, ((char*)ref)[:ref.type.sizeof]);
}
/**
* @param [&in] ref "the object to write."
* @require @is_outstream(stream)
* @ensure return == ref.type.sizeof
*/
macro usz! write_any(stream, any ref)
{
return write_all(stream, ((char*)ref)[:ref.type.sizeof]);
}
/**
* @require @is_instream(stream)
*/
macro usz! read_all(stream, char[] buffer)
{
if (buffer.len == 0) return 0;
usz n = stream.read(buffer)!;
if (n != buffer.len) return IoError.UNEXPECTED_EOF?;
return n;
}
/**
* @require @is_outstream(stream)
*/
macro usz! write_all(stream, char[] buffer)
{
if (buffer.len == 0) return 0;
usz n = stream.write(buffer)!;
if (n != buffer.len) return IoError.INCOMPLETE_WRITE?;
return n;
}
macro usz! @read_using_read_byte(&s, char[] buffer)
{
usz len = 0;
foreach (&cptr : buffer)
{
char! c = s.read_byte();
if (catch err = c)
{
case IoError.EOF: return len;
default: return err?;
}
*cptr = c;
len++;
}
return len;
}
macro void! @write_byte_using_write(&s, char c)
{
char[1] buff = { c };
(*s).write(&buff)!;
}
macro char! @read_byte_using_read(&s)
{
char[1] buffer;
usz read = (*s).read(&buffer)!;
if (read != 1) return IoError.EOF?;
return buffer[0];
}
def ReadByteFn = fn char!();
macro usz! @write_using_write_byte(&s, char[] bytes)
{
foreach (c : bytes) s.write_byte(self, c)!;
return bytes.len;
}
macro void! @pushback_using_seek(&s)
{
s.seek(-1, CURSOR)!;
}
fn usz! copy_to(InStream in, OutStream dst, char[] buffer = {})
{
if (buffer.len) return copy_through_buffer(in, dst, buffer);
if (&in.write_to) return in.write_to(dst);
if (&dst.read_to) return dst.read_to(in);
$switch (env::MEMORY_ENV)
$case NORMAL:
return copy_through_buffer(in, dst, &&char[4096]{});
$case SMALL:
return copy_through_buffer(in, dst, &&char[1024]{});
$case TINY:
$case NONE:
return copy_through_buffer(in, dst, &&(char[256]{}));
$endswitch
}
macro usz! copy_through_buffer(InStream in, OutStream dst, char[] buffer) @local
{
usz total_copied;
while (true)
{
usz! len = in.read(buffer);
if (catch err = len)
{
case IoError.EOF: return total_copied;
default: return err?;
}
if (!len) return total_copied;
usz written = dst.write(buffer[:len])!;
total_copied += len;
if (written != len) return IoError.INCOMPLETE_WRITE?;
}
}
const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };
/**
* @require @is_instream(stream)
* @require @typekind(x_ptr) == POINTER && $typeof(x_ptr).inner.kindof.is_int()
**/
macro usz! read_varint(stream, x_ptr)
{
var $Type = $typefrom($typeof(x_ptr).inner);
const MAX = MAX_VARS[$Type.sizeof];
$Type x;
uint shift;
usz n;
for (usz i = 0; i < MAX; i++)
{
char! c = stream.read_byte();
if (catch err = c)
{
case IoError.EOF:
return IoError.UNEXPECTED_EOF?;
default:
return err?;
}
n++;
if (c & 0x80 == 0)
{
if (i + 1 == MAX && c > 1) break;
x |= c << shift;
$if $Type.kindof == SIGNED_INT:
x = x & 1 == 0 ? x >> 1 : ~(x >> 1);
$endif
*x_ptr = x;
return n;
}
x |= (c & 0x7F) << shift;
shift += 7;
}
return MathError.OVERFLOW?;
}
/**
* @require @is_outstream(stream)
* @require @typekind(x).is_int()
**/
macro usz! write_varint(stream, x)
{
var $Type = $typeof(x);
const MAX = MAX_VARS[$Type.sizeof];
char[MAX] buffer @noinit;
usz i;
while (x >= 0x80)
{
buffer[i] = (char)(x | 0x80);
x >>= 7;
i++;
}
buffer[i] = (char)x;
return write_all(stream, buffer[:i + 1]);
}

133
lib/std/io/stream/buffer.c3 Normal file
View File

@@ -0,0 +1,133 @@
module std::io;
struct ReadBuffer (InStream)
{
InStream wrapped_stream;
char[] bytes;
usz read_idx;
usz write_idx;
}
/**
* Buffer reads from a stream.
* @param [inout] self
* @require bytes.len > 0
* @require self.bytes.len == 0 "Init may not run on already initialized data"
**/
fn ReadBuffer* ReadBuffer.init(&self, InStream wrapped_stream, char[] bytes)
{
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
return self;
}
fn String ReadBuffer.str_view(&self) @inline
{
return (String)self.bytes[self.read_idx:self.write_idx - self.read_idx];
}
fn void! ReadBuffer.close(&self) @dynamic
{
if (&self.wrapped_stream.close) self.wrapped_stream.close()!;
}
fn usz! ReadBuffer.read(&self, char[] bytes) @dynamic
{
if (self.read_idx == self.write_idx)
{
if (self.read_idx == 0 && bytes.len >= self.bytes.len)
{
// Read directly into the input buffer.
return self.wrapped_stream.read(bytes)!;
}
self.refill()!;
}
usz n = min(self.write_idx - self.read_idx, bytes.len);
bytes[:n] = self.bytes[self.read_idx:n];
self.read_idx += n;
return n;
}
fn char! ReadBuffer.read_byte(&self) @dynamic
{
if (self.read_idx == self.write_idx) self.refill()!;
if (self.read_idx == self.write_idx) return IoError.EOF?;
char c = self.bytes[self.read_idx];
self.read_idx++;
return c;
}
fn void! ReadBuffer.refill(&self) @local @inline
{
self.read_idx = 0;
self.write_idx = self.wrapped_stream.read(self.bytes)!;
}
struct WriteBuffer (OutStream)
{
OutStream wrapped_stream;
char[] bytes;
usz index;
}
/**
* Buffer writes to a stream. Call `flush` when done writing to the buffer.
* @param [inout] self
* @require bytes.len > 0 "Non-empty buffer required"
* @require self.bytes.len == 0 "Init may not run on already initialized data"
**/
fn WriteBuffer* WriteBuffer.init(&self, OutStream wrapped_stream, char[] bytes)
{
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
return self;
}
fn String WriteBuffer.str_view(&self) @inline
{
return (String)self.bytes[:self.index];
}
fn void! WriteBuffer.close(&self) @dynamic
{
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
fn void! WriteBuffer.flush(&self) @dynamic
{
self.write_pending()!;
if (&self.wrapped_stream.flush) self.wrapped_stream.flush()!;
}
fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic
{
usz n = self.bytes.len - self.index;
if (bytes.len < n)
{
// Enough room in the buffer.
self.bytes[self.index:bytes.len] = bytes[..];
self.index += bytes.len;
return bytes.len;
}
self.write_pending()!;
if (bytes.len >= self.bytes.len)
{
// Write directly to the stream.
return self.wrapped_stream.write(bytes);
}
// Buffer the data.
self.bytes[:bytes.len] = bytes[..];
self.index = bytes.len;
return bytes.len;
}
fn void! WriteBuffer.write_byte(&self, char c) @dynamic
{
usz n = self.bytes.len - self.index;
if (n == 0) self.write_pending()!;
self.bytes[0] = c;
self.index = 1;
}
fn void! WriteBuffer.write_pending(&self) @local
{
self.index -= self.wrapped_stream.write(self.bytes[:self.index])!;
if (self.index != 0) return IoError.INCOMPLETE_WRITE?;
}

View File

@@ -0,0 +1,148 @@
module std::io;
import std::math;
struct ByteBuffer (InStream, OutStream)
{
Allocator allocator;
usz max_read;
char[] bytes;
usz read_idx;
usz write_idx;
bool has_last;
}
/**
* ByteBuffer provides a streamable read/write buffer.
* max_read defines how many bytes might be kept before its internal buffer is shrinked.
* @require self.bytes.len == 0 "Buffer already initialized."
**/
fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap())
{
*self = { .allocator = allocator, .max_read = max_read };
initial_capacity = max(initial_capacity, 16);
self.grow(initial_capacity)!;
return self;
}
fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16)
{
return self.new_init(max_read, initial_capacity, allocator::temp());
}
/**
* @require buf.len > 0
* @require self.bytes.len == 0 "Buffer already initialized."
**/
fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf)
{
*self = { .max_read = buf.len, .bytes = buf };
return self;
}
fn void ByteBuffer.free(&self)
{
if (self.allocator) allocator::free(self.allocator, self.bytes);
*self = {};
}
fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic
{
usz cap = self.bytes.len - self.write_idx;
if (cap < bytes.len) self.grow(bytes.len)!;
self.bytes[self.write_idx:bytes.len] = bytes[..];
self.write_idx += bytes.len;
return bytes.len;
}
fn void! ByteBuffer.write_byte(&self, char c) @dynamic
{
usz cap = self.bytes.len - self.write_idx;
if (cap == 0) self.grow(1)!;
self.bytes[self.write_idx] = c;
self.write_idx++;
}
fn usz! ByteBuffer.read(&self, char[] bytes) @dynamic
{
usz readable = self.write_idx - self.read_idx;
if (readable == 0)
{
self.has_last = false;
return IoError.EOF?;
}
usz n = min(readable, bytes.len);
bytes[:n] = self.bytes[self.read_idx:n];
self.read_idx += n;
self.has_last = n > 0;
self.shrink();
return n;
}
fn char! ByteBuffer.read_byte(&self) @dynamic
{
usz readable = self.write_idx - self.read_idx;
if (readable == 0)
{
self.has_last = false;
return IoError.EOF?;
}
char c = self.bytes[self.read_idx];
self.read_idx++;
self.has_last = true;
self.shrink();
return c;
}
/*
* Only the last byte of a successful read can be pushed back.
*/
fn void! ByteBuffer.pushback_byte(&self) @dynamic
{
if (!self.has_last) return IoError.EOF?;
assert(self.read_idx > 0);
self.read_idx--;
self.has_last = false;
}
fn usz! ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic
{
switch (seek)
{
case SET:
if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?;
self.read_idx = offset;
return offset;
case CURSOR:
if ((offset < 0 && self.read_idx < -offset) ||
(offset > 0 && self.read_idx + offset > self.write_idx)) return IoError.INVALID_POSITION?;
self.read_idx += offset;
case END:
if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?;
self.read_idx = self.write_idx - offset;
}
return self.read_idx;
}
fn usz! ByteBuffer.available(&self) @inline @dynamic
{
return self.write_idx - self.read_idx;
}
fn void! ByteBuffer.grow(&self, usz n)
{
n = math::next_power_of_2(n);
char* p = allocator::realloc_aligned(self.allocator, self.bytes, n, .alignment = char.alignof)!;
self.bytes = p[:n];
}
macro ByteBuffer.shrink(&self)
{
if (self.read_idx >= self.max_read)
{
// Drop the read data besides the last byte (for pushback_byte).
usz readable = self.write_idx - self.read_idx;
self.bytes[:1 + readable] = self.bytes[self.read_idx - 1:1 + readable];
self.write_idx = 1 + readable;
self.read_idx = 1;
}
}

View File

@@ -0,0 +1,68 @@
module std::io;
struct ByteReader (InStream)
{
char[] bytes;
usz index;
}
fn usz ByteReader.len(&self) @dynamic
{
return self.bytes.len;
}
fn ByteReader* ByteReader.init(&self, char[] bytes)
{
*self = { .bytes = bytes };
return self;
}
fn usz! ByteReader.read(&self, char[] bytes) @dynamic
{
if (self.index >= self.bytes.len) return IoError.EOF?;
usz len = min(self.bytes.len - self.index, bytes.len);
if (len == 0) return 0;
mem::copy(bytes.ptr, &self.bytes[self.index], len);
self.index += len;
return len;
}
fn char! ByteReader.read_byte(&self) @dynamic
{
if (self.index >= self.bytes.len) return IoError.EOF?;
return self.bytes[self.index++];
}
fn void! ByteReader.pushback_byte(&self) @dynamic
{
if (!self.index) return IoError.INVALID_PUSHBACK?;
self.index--;
}
fn usz! ByteReader.seek(&self, isz offset, Seek seek) @dynamic
{
isz new_index;
switch (seek)
{
case SET: new_index = offset;
case CURSOR: new_index = self.index + offset;
case END: new_index = self.bytes.len + offset;
}
if (new_index < 0) return IoError.INVALID_POSITION?;
self.index = new_index;
return new_index;
}
fn usz! ByteReader.write_to(&self, OutStream writer) @dynamic
{
if (self.index >= self.bytes.len) return 0;
usz written = writer.write(self.bytes[self.index..])!;
self.index += written;
assert(self.index <= self.bytes.len);
return written;
}
fn usz! ByteReader.available(&self) @inline @dynamic
{
return max(0, self.bytes.len - self.index);
}

View File

@@ -0,0 +1,113 @@
module std::io;
import std::math;
struct ByteWriter (OutStream)
{
char[] bytes;
usz index;
Allocator allocator;
}
/**
* @param [&inout] self
* @param [&inout] allocator
* @require self.bytes.len == 0 "Init may not run on already initialized data"
* @ensure (bool)allocator, self.index == 0
**/
fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap())
{
*self = { .bytes = {}, .allocator = allocator };
return self;
}
/**
* @param [&inout] self
* @require self.bytes.len == 0 "Init may not run on already initialized data"
* @ensure self.index == 0
**/
fn ByteWriter* ByteWriter.temp_init(&self)
{
return self.new_init(allocator::temp()) @inline;
}
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
{
*self = { .bytes = data, .allocator = null };
return self;
}
fn void! ByteWriter.destroy(&self) @dynamic
{
if (!self.allocator) return;
if (void* ptr = self.bytes.ptr) allocator::free(self.allocator, ptr);
*self = { };
}
fn String ByteWriter.str_view(&self) @inline
{
return (String)self.bytes[:self.index];
}
fn void! ByteWriter.ensure_capacity(&self, usz len) @inline
{
if (self.bytes.len > len) return;
if (!self.allocator) return IoError.OUT_OF_SPACE?;
if (len < 16) len = 16;
usz new_capacity = math::next_power_of_2(len);
char* new_ptr = allocator::realloc_try(self.allocator, self.bytes.ptr, new_capacity)!;
self.bytes = new_ptr[:new_capacity];
}
fn usz! ByteWriter.write(&self, char[] bytes) @dynamic
{
self.ensure_capacity(self.index + bytes.len)!;
mem::copy(&self.bytes[self.index], bytes.ptr, bytes.len);
self.index += bytes.len;
return bytes.len;
}
fn void! ByteWriter.write_byte(&self, char c) @dynamic
{
self.ensure_capacity(self.index + 1)!;
self.bytes[self.index++] = c;
}
/**
* @param [&inout] self
* @param reader
**/
fn usz! ByteWriter.read_from(&self, InStream reader) @dynamic
{
usz start_index = self.index;
if (&reader.available)
{
while (usz available = reader.available()!)
{
self.ensure_capacity(self.index + available)!;
usz read = reader.read(self.bytes[self.index..])!;
self.index += read;
}
return self.index - start_index;
}
if (self.bytes.len == 0)
{
self.ensure_capacity(16)!;
}
while (true)
{
// See how much we can read.
usz len_to_read = self.bytes.len - self.index;
// Less than 16 bytes? Double the capacity
if (len_to_read < 16)
{
self.ensure_capacity(self.bytes.len * 2)!;
len_to_read = self.bytes.len - self.index;
}
// Read into the rest of the buffer
usz read = reader.read(self.bytes[self.index..])!;
self.index += read;
// Ok, we reached the end.
if (read < len_to_read) return self.index - start_index;
// Otherwise go another round
}
}

View File

@@ -0,0 +1,44 @@
module std::io;
struct LimitReader (InStream)
{
InStream wrapped_stream;
usz limit;
}
/**
* @param [&inout] wrapped_stream "The stream to read from"
* @param limit "The max limit to read"
**/
fn LimitReader* LimitReader.init(&self, InStream wrapped_stream, usz limit)
{
*self = { .wrapped_stream = wrapped_stream, .limit = limit };
return self;
}
fn void! LimitReader.close(&self) @dynamic
{
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
fn usz! LimitReader.read(&self, char[] bytes) @dynamic
{
if (self.limit == 0) return IoError.EOF?;
usz m = min(bytes.len, self.limit);
usz n = self.wrapped_stream.read(bytes[:m])!;
self.limit -= n;
return n;
}
fn char! LimitReader.read_byte(&self) @dynamic
{
if (self.limit == 0) return IoError.EOF?;
defer try self.limit--;
return self.wrapped_stream.read_byte();
}
fn usz! LimitReader.available(&self) @inline @dynamic
{
return self.limit;
}

View File

@@ -0,0 +1,124 @@
module std::io;
struct Scanner (InStream)
{
InStream wrapped_stream;
char[] buf;
usz pattern_idx;
usz read_idx;
}
/**
* Scanner provides a way to read delimited data (with newlines as the default).
* The supplied buffer must be at least as large as the expected data length
* including its pattern.
*
* @param [&in] stream "The stream to read data from."
* @require buffer.len > 0 "Non-empty buffer required."
**/
fn void Scanner.init(&self, InStream stream, char[] buffer)
{
*self = { .wrapped_stream = stream, .buf = buffer };
}
/**
* Return and clear any remaining unscanned data.
**/
fn char[] Scanner.flush(&self) @dynamic
{
assert(self.read_idx >= self.pattern_idx);
usz n = self.read_idx - self.pattern_idx;
char[] buf = self.buf[self.pattern_idx:n];
self.pattern_idx = 0;
self.read_idx = 0;
return buf;
}
fn void! Scanner.close(&self) @dynamic
{
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
/**
* Scan the stream for the next split character and return data up to the match.
* @require pattern.len > 0 "Non-empty pattern required."
* @require self.buf.len > pattern.len "Pattern too large."
**/
fn char[]! Scanner.scan(&self, String pattern = "\n")
{
if (self.read_idx == 0)
{
// First read.
self.read_idx = self.refill(self.buf)!;
self.pattern_idx = 0;
}
assert(self.read_idx >= self.pattern_idx);
usz n = self.read_idx - self.pattern_idx;
char[] buf = self.buf[self.pattern_idx:n];
if (try i = self.find(buf, pattern))
{
self.pattern_idx += i + pattern.len;
return buf[:i];
}
if (self.pattern_idx == 0 || self.read_idx < self.buf.len)
{
// Split pattern not found with maximized search, abort.
// Split pattern not found and already read as much as possible.
return SearchResult.MISSING?;
}
// Split pattern not found: maximize the search and try one more time.
self.buf[:n] = buf[..];
self.pattern_idx = 0;
buf = self.buf[n..];
usz p = self.refill(buf)!;
self.read_idx = n + p;
buf = buf[:p];
usz i = self.find(buf, pattern)!;
self.pattern_idx = n + i + pattern.len;
return self.buf[:n + i];
}
macro usz! Scanner.find(&self, buf, pattern) @private
{
return ((String)buf).index_of(pattern);
}
macro usz! Scanner.refill(&self, buf) @private
{
usz! n = self.wrapped_stream.read(buf);
if (catch err = n)
{
case IoError.EOF:
return SearchResult.MISSING?;
default:
return err?;
}
return n;
}
fn usz! Scanner.read(&self, char[] bytes) @dynamic
{
usz n;
if (self.pattern_idx < self.read_idx)
{
n = min(bytes.len, self.read_idx - self.pattern_idx);
bytes[:n] = self.buf[self.pattern_idx:n];
self.pattern_idx += n;
bytes = bytes[n..];
}
n += self.wrapped_stream.read(bytes)!;
return n;
}
fn char! Scanner.read_byte(&self) @dynamic
{
if (self.pattern_idx < self.read_idx)
{
return self.buf[self.pattern_idx++];
}
return self.wrapped_stream.read_byte();
}

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

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

View File

@@ -0,0 +1,22 @@
module libc;
import std::time;
/**
* @require self >= 0
**/
fn TimeSpec NanoDuration.to_timespec(self) @inline
{
CLong ns = (CLong)(self % 1000_000_000);
Time_t sec = (Time_t)(self / 1000_000_000);
return { .s = sec, .ns = ns };
}
/**
* @require self >= 0
**/
fn TimeSpec Duration.to_timespec(self) @inline
{
CLong ns = (CLong)(1000 * (self % time::SEC));
Time_t sec = (Time_t)(self / time::SEC);
return { .s = sec, .ns = ns };
}

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