Compare commits

..

133 Commits

Author SHA1 Message Date
Christoffer Lerno
058d637407 Additional codegen. 2024-12-28 16:46:12 +01:00
Christoffer Lerno
01335f6862 Additional codegen. 2024-12-28 16:23:25 +01:00
Christoffer Lerno
d20d957881 Add @enum_from_value 2024-12-27 23:21:36 +01:00
Christoffer Lerno
008274cda5 Update the release notes. 2024-12-27 22:35:28 +01:00
Alexey Kutepov
e07ab7547f Fix Segfault and UX of the c3c project add-target subcommand (#1729)
* Fix UX of the `c3c project add-target` subcommand

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

This enables the following workflow without friction and needless
crashes:

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

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

* Fix read_project() call in fetch_project()

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

* Clean up

* Implemented JSON for CONST_BYTES

* Handle empty byte string, slightly more concise

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

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

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

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

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

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

* math_tests: inverse trig and inverse hyperbolic trig

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

* math: cos macro missing values::promote_int

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

* math_tests: trig and exponential

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

* math: float comparison

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

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

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

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

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

* fix fnv64a

* Simplify code

---------

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

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

* sort: implement quickselect

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

* add quicksort benchmark

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

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

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

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

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

* HashMap.remove_entry_for_key: return false on unintialized

* test: switch to temp_init

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

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

* io: implement MultiWriter struct

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

* io: implement TeeReader struct

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

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

View File

@@ -51,7 +51,7 @@ jobs:
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
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32
dir build\llvm_ir
..\..\build\${{ matrix.build_type }}\c3c.exe clean
dir build\llvm_ir
@@ -61,13 +61,19 @@ jobs:
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
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv build hello_world_win32_lib
- name: Compile and run dynlib-test
run: |
cd resources/examples/dynlib-test
..\..\..\build\${{ matrix.build_type }}\c3c.exe dynamic-lib add.c3
..\..\..\build\${{ matrix.build_type }}\c3c.exe compile-run test.c3 -l ./add.lib
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv dynamic-lib add.c3
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
- name: Compile and run staticlib-test
run: |
cd resources/examples/staticlib-test
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv static-lib add.c3
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
- name: Vendor-fetch
run: |
@@ -126,8 +132,8 @@ jobs:
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
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-19.1.6-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-19.1.6-1-any.pkg.tar.zst
- name: CMake
run: |
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
@@ -147,7 +153,7 @@ jobs:
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
../../build/c3c run -vvv
- name: Vendor-fetch
run: |
@@ -156,7 +162,7 @@ jobs:
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --cc cc --debug-log
../../build/c3c build hello_world_lib --cc cc -vvv
- name: run compiler tests
run: |
@@ -203,12 +209,12 @@ jobs:
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
../../build/c3c run -vvv
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
../../build/c3c build hello_world_lib -vvv
- name: run compiler tests
run: |
@@ -222,7 +228,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20]
llvm_version: [17, 18, 19]
steps:
- uses: actions/checkout@v4
@@ -320,12 +326,21 @@ jobs:
- name: Compile and run dynlib-test
run: |
cd resources/examples/dynlib-test
../../../build/c3c dynamic-lib add.c3
../../../build/c3c -vv dynamic-lib add.c3
mv add.so libadd.so
cc test.c -L. -ladd -Wl,-rpath=.
./a.out
../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=.
- name: Compile and run staticlib-test
run: |
cd resources/examples/staticlib-test
../../../build/c3c -vv static-lib add.c3
mv add.a libadd.a
cc test.c -L. -ladd
./a.out
../../../build/c3c compile-run test.c3 -L . -l add
- name: Compile run unit tests
run: |
cd test
@@ -334,7 +349,7 @@ jobs:
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
../../build/c3c run -vvv
- name: Test WASM
run: |
@@ -353,7 +368,7 @@ jobs:
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --linker=builtin
../../build/c3c run -vvv --linker=builtin
- name: Init a library & a project
run: |
@@ -390,7 +405,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20]
llvm_version: [17, 18, 19]
steps:
- uses: actions/checkout@v4
- name: Install common deps
@@ -476,12 +491,12 @@ jobs:
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
../../build/c3c run -vvv
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --linker=builtin
../../build/c3c run -vvv --linker=builtin
- name: run compiler tests
run: |
@@ -511,7 +526,7 @@ jobs:
matrix:
ubuntu_version: [20.04, 22.04]
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20]
llvm_version: [17, 18, 19]
steps:
- uses: actions/checkout@v4
@@ -568,7 +583,7 @@ jobs:
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
../../build/c3c run -vvv
- name: Test WASM
run: |
@@ -578,7 +593,7 @@ jobs:
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --linker=builtin
../../build/c3c run -vvv --linker=builtin
- name: Init a library & a project
run: |
@@ -639,7 +654,7 @@ jobs:
- name: Compile and run dynlib-test
run: |
cd resources/examples/dynlib-test
../../../build/c3c dynamic-lib add.c3
../../../build/c3c -vv dynamic-lib add.c3
../../../build/c3c compile-run test.c3 -l ./add.dylib
- name: Compile run unit tests
@@ -647,20 +662,25 @@ jobs:
cd test
../build/c3c compile-test unit
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
../../build/c3c run -vvv
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --linker=builtin
../../build/c3c run -vvv --linker=builtin
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
../../build/c3c build hello_world_lib -vvv
- name: run compiler tests
run: |

4
.gitignore vendored
View File

@@ -19,6 +19,7 @@
# Libraries
*.lib
*.tlb
*.a
*.la
*.lo
@@ -75,3 +76,6 @@ TAGS
/.cache/
/compile_commands.json
# 'nix build' resulting symlink
result

View File

@@ -73,9 +73,9 @@ if(C3_USE_MIMALLOC)
option(MI_PADDING OFF)
option(MI_DEBUG_FULL OFF)
FetchContent_Declare(
mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
GIT_TAG ${C3_MIMALLOC_TAG}
mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
GIT_TAG ${C3_MIMALLOC_TAG}
)
FetchContent_MakeAvailable(mimalloc)
endif()
@@ -108,9 +108,9 @@ endif()
# Clangd LSP support
option(C3_ENABLE_CLANGD_LSP "Enable/Disable output of compile commands during generation." OFF)
if(C3_ENABLE_CLANGD_LSP)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
execute_process(
COMMAND ${CMAKE_COMMAND} -E create_symlink
COMMAND ${CMAKE_COMMAND} -E create_symlink
${CMAKE_BINARY_DIR}/compile_commands.json
${CMAKE_SOURCE_DIR}/compile_commands.json
)
@@ -119,15 +119,15 @@ endif(C3_ENABLE_CLANGD_LSP)
if(C3_WITH_LLVM)
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "18")
set(C3_LLVM_VERSION "19")
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
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt-dbg.7z
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
@@ -173,54 +173,54 @@ if(C3_WITH_LLVM)
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
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
)
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
message("C3_LLD_DIR: " ${C3_LLD_DIR})
set(LLVM_LIBRARY_DIRS
"${LLVM_LIBRARY_DIRS}"
"${C3_LLD_DIR}"
"${LLVM_LIBRARY_DIRS}"
"${C3_LLD_DIR}"
)
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
endif()
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
else()
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(llvm_libs ${LLVM})
@@ -244,13 +244,13 @@ endif()
if(C3_WITH_LLVM)
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
if (APPLE)
set(lld_libs ${lld_libs} xar)
@@ -264,7 +264,7 @@ if(C3_WITH_LLVM)
# Unused
# ${RT_UBSAN_DYNAMIC}
# ${RT_LSAN_DYNAMIC}
)
)
endif()
message(STATUS "linking to llvm libs ${lld_libs}")
@@ -343,10 +343,10 @@ add_executable(c3c
src/utils/cpus.c
src/utils/unzipper.c
src/compiler/decltable.c
src/compiler/mac_support.c
src/compiler/mac_support.c
src/compiler/windows_support.c
src/compiler/codegen_asm.c
src/compiler/asm_target.c
src/compiler/asm_target.c
src/compiler/expr.c
src/utils/time.c
src/utils/http.c
@@ -354,7 +354,7 @@ add_executable(c3c
src/build/common_build.c
src/compiler/sema_const.c
${CMAKE_BINARY_DIR}/git_hash.h
)
)
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
# We are inside of a git repository so rebuilding the hash every time something changes.
@@ -370,18 +370,19 @@ else()
endif()
if(C3_WITH_LLVM)
target_sources(c3c PRIVATE
src/compiler/llvm_codegen.c
src/compiler/llvm_codegen_debug_info.c
src/compiler/llvm_codegen_expr.c
src/compiler/llvm_codegen_function.c
src/compiler/llvm_codegen_instr.c
src/compiler/llvm_codegen_module.c
src/compiler/llvm_codegen_stmt.c
src/compiler/llvm_codegen_type.c
src/compiler/llvm_codegen_value.c
src/compiler/llvm_codegen_storeload.c
src/compiler/llvm_codegen_builtins.c)
target_sources(c3c PRIVATE
src/compiler/c_codegen.c
src/compiler/llvm_codegen.c
src/compiler/llvm_codegen_debug_info.c
src/compiler/llvm_codegen_expr.c
src/compiler/llvm_codegen_function.c
src/compiler/llvm_codegen_instr.c
src/compiler/llvm_codegen_module.c
src/compiler/llvm_codegen_stmt.c
src/compiler/llvm_codegen_type.c
src/compiler/llvm_codegen_value.c
src/compiler/llvm_codegen_storeload.c
src/compiler/llvm_codegen_builtins.c)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
@@ -409,7 +410,7 @@ if (C3_USE_TB)
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
@@ -447,7 +448,7 @@ if(C3_WITH_LLVM)
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
else()
else()
target_link_libraries(c3c ${llvm_libs} miniz ${lld_libs})
@@ -507,10 +508,10 @@ if(MSVC)
if(C3_WITH_LLVM)
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
set(sanitizer_runtime_libraries
${clang_lib_dir}/clang_rt.asan-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
${clang_lib_dir}/clang_rt.asan-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
endif()
else()
message(STATUS "using gcc/clang warning switches")
@@ -532,17 +533,17 @@ endif()
if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
add_custom_command(TARGET c3c POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
VERBATIM
COMMENT "Copying sanitizer runtime libraries to output directory")
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)
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)

View File

@@ -55,7 +55,7 @@ fn void Stack.push(Stack* this, Type element)
if (this.capacity == this.size)
{
this.capacity *= 2;
if (this.capacity < 16) this.capacity = 16;
if (this.capacity < 16) this.capacity = 16;
this.elems = realloc(this.elems, Type.sizeof * this.capacity);
}
this.elems[this.size++] = element;
@@ -138,7 +138,7 @@ fn void main()
### Current status
The current stable version of the compiler is **version 0.6.3**.
The current stable version of the compiler is **version 0.6.5**.
The upcoming 0.6.x releases will focus on expanding the standard library.
Follow the issues [here](https://github.com/c3lang/c3c/issues).
@@ -303,7 +303,7 @@ called `hello_world` or `hello_world.exe`depending on platform.
#### 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)
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++"
2. Install CMake
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`.

View File

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

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1730958623,
"narHash": "sha256-JwQZIGSYnRNOgDDoIgqKITrPVil+RMWHsZH1eE1VGN0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "85f7e662eda4fa3a995556527c87b2524b691933",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

39
flake.nix Normal file
View File

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

View File

@@ -22,7 +22,7 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
foreach (i, &value : self.values)
{
if (i != 0) formatter.print(", ")!;
n += formatter.printf("%s: %s", (Enum)i, *value)!;
n += formatter.printf("%s: %s", Enum.from_ordinal(i), *value)!;
}
n += formatter.print(" }")!;
return n;

View File

@@ -16,9 +16,9 @@ distinct EnumSet (Printable) = EnumSetType;
fn void EnumSet.add(&self, Enum v)
{
$if IS_CHAR_ARRAY:
(*self)[(usz)v / 8] |= (char)(1u << ((usz)v % 8));
(*self)[(usz)v.ordinal / 8] |= (char)(1u << ((usz)v.ordinal % 8));
$else
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v);
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v.ordinal);
$endif
}
@@ -35,11 +35,11 @@ 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));
(*self)[(usz)v.ordinal / 8] &= (char)~(1u << ((usz)v.ordinal % 8));
return true;
$else
EnumSetType old = (EnumSetType)*self;
EnumSetType new = old & ~(1u << (EnumSetType)v);
EnumSetType new = old & ~(1u << (EnumSetType)v.ordinal);
*self = (EnumSet)new;
return old != new;
$endif
@@ -48,9 +48,9 @@ fn bool EnumSet.remove(&self, Enum v)
fn bool EnumSet.has(&self, Enum v)
{
$if IS_CHAR_ARRAY:
return (bool)(((*self)[(usz)v / 8] << ((usz)v % 8)) & 0x01);
return (bool)(((*self)[(usz)v.ordinal / 8] << ((usz)v.ordinal % 8)) & 0x01);
$else
return ((EnumSetType)*self & (1u << (EnumSetType)v)) != 0;
return ((EnumSetType)*self & (1u << (EnumSetType)v.ordinal)) != 0;
$endif
}

View File

@@ -400,6 +400,21 @@ fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
return false;
}
fn HashMapIterator HashMap.iter(&self)
{
return { .map = self, .index = -1 };
}
fn HashMapValueIterator HashMap.value_iter(&self)
{
return { .map = self, .index = -1 };
}
fn HashMapKeyIterator HashMap.key_iter(&self)
{
return { .map = self, .index = -1 };
}
// --- private methods
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
@@ -455,8 +470,11 @@ 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);
while (e)
{
map.put_for_create(e.key, e.value);
e = e.next;
}
}
}
@@ -482,6 +500,7 @@ fn void HashMap.free_internal(&map, void* ptr) @inline @private
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
{
if (!map.count) return false;
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
Entry* prev = map.table[i];
@@ -528,3 +547,54 @@ fn void HashMap.free_entry(&self, Entry *entry) @local
self.free_internal(entry);
}
struct HashMapIterator
{
HashMap* map;
int top_index;
int index;
Entry* current_entry;
}
distinct HashMapValueIterator = HashMapIterator;
distinct HashMapKeyIterator = HashMapIterator;
<*
@require idx < self.map.count
*>
fn Entry HashMapIterator.get(&self, usz idx) @operator([])
{
if (idx < self.index)
{
self.top_index = 0;
self.current_entry = null;
self.index = -1;
}
while (self.index != idx)
{
if (self.current_entry)
{
self.current_entry = self.current_entry.next;
if (self.current_entry) self.index++;
continue;
}
self.current_entry = self.map.table[self.top_index++];
if (self.current_entry) self.index++;
}
return *self.current_entry;
}
fn Value HashMapValueIterator.get(&self, usz idx) @operator([])
{
return ((HashMapIterator*)self).get(idx).value;
}
fn Key HashMapKeyIterator.get(&self, usz idx) @operator([])
{
return ((HashMapIterator*)self).get(idx).key;
}
fn usz HashMapValueIterator.len(self) @operator(len) => self.map.count;
fn usz HashMapKeyIterator.len(self) @operator(len) => self.map.count;
fn usz HashMapIterator.len(self) @operator(len) => self.map.count;

View File

@@ -131,8 +131,11 @@ fn Map new_from_map(Map other_map, Allocator allocator = null)
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);
while (e)
{
map._put_for_create(e.key, e.value);
e = e.next;
}
}
return (Map)map;
}

View File

@@ -1,3 +1,6 @@
<*
@require values::@is_int(SIZE) &&& SIZE > 0 "The size must be positive integer"
*>
module std::collections::ringbuffer(<Type, SIZE>);
struct RingBuffer

View File

@@ -98,7 +98,7 @@ fn void*! SimpleHeapAllocator._alloc(&self, usz bytes) @local
return current + 1;
case current.size > aligned_bytes:
Header* unallocated = (Header*)((char*)current + aligned_bytes + Header.sizeof);
unallocated.size = current.size - aligned_bytes;
unallocated.size = current.size - aligned_bytes - Header.sizeof;
unallocated.next = current.next;
if (current == self.free_list)
{

View File

@@ -62,7 +62,7 @@ fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynami
{
if (allocation_in_stack_mem(self, old_pointer)) return;
on_stack_allocator_remove_chunk(self, old_pointer);
self.release(old_pointer, aligned);
self.backing_allocator.release(old_pointer, aligned);
}
fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local

View File

@@ -179,8 +179,10 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
{
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
}
void* start = mem;
mem += mem::aligned_offset(TempAllocatorPage.sizeof, alignment);
page = (TempAllocatorPage*)mem - 1;
page.start = mem;
page.start = start;
page.size = size | PAGE_IS_ALIGNED;
}
else

View File

@@ -17,15 +17,16 @@ macro index_of(array, element)
}
<*
@require @typekind(array) == VECTOR || @typekind(array) == ARRAY
@require @typekind(array[0]) == VECTOR || @typekind(array[0]) == ARRAY
@require @typekind(array_ptr) == POINTER
@require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY
@require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY
*>
macro slice2d(array, x = 0, xlen = 0, y = 0, ylen = 0)
macro slice2d(array_ptr, 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 };
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
var $ElementType = $typeof((*array_ptr)[0][0]);
return Slice2d(<$ElementType>) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
}
@@ -145,11 +146,41 @@ macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
<*
@require idy >= 0 && idy < self.ylen
*>
macro Type[] Slice2d.get(self, usz idy) @operator([])
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
{
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
}
macro Type Slice2d.get_coord(self, usz[<2>] coord)
{
return *self.get_coord_ref(coord);
}
macro Type Slice2d.get_xy(self, x, y)
{
return *self.get_xy_ref(x, y);
}
macro Type* Slice2d.get_xy_ref(self, x, y)
{
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
}
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
{
return self.get_xy_ref(coord.x, coord.y);
}
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
{
*self.get_coord_ref(coord) = value;
}
macro void Slice2d.set_xy(self, x, y, Type value)
{
*self.get_xy_ref(x, y) = value;
}
<*
@require y >= 0 && y < self.ylen
@require x >= 0 && x < self.xlen

View File

@@ -4,21 +4,22 @@
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();
<*
@@ -229,7 +230,25 @@ 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;
if (name == enum_name) return $Type.from_ordinal(i);
}
return SearchResult.MISSING?;
}
<*
@param $Type `The type of the enum`
@require $Type.kindof == ENUM `Only enums may be used`
@require $defined($Type.#value1) `Expected '#value' to match an enum associated value`
@require $assignable(value, $typeof($Type{}.#value)) `Expected the value to match the type of the associated value`
@ensure @typeis(return, $Type)
@return! SearchResult.MISSING
*>
macro @enum_from_value($Type, #value, value) @builtin
{
usz elements = $Type.elements;
foreach (e : $Type.values)
{
if (e.#value == value) return e;
}
return SearchResult.MISSING?;
}

View File

@@ -1,7 +1,8 @@
module std::core::dstring;
import std::io;
distinct DString (OutStream) = void*;
distinct DString (OutStream) = DStringOpaque*;
distinct DStringOpaque = void;
const usz MIN_CAPACITY @private = 16;
@@ -132,7 +133,7 @@ fn usz DString.capacity(self)
return self.data().capacity;
}
fn usz DString.len(&self) @dynamic
fn usz DString.len(&self) @dynamic @operator(len)
{
if (!*self) return 0;
return self.data().len;
@@ -154,6 +155,24 @@ fn String DString.str_view(self)
return (String)data.chars[:data.len];
}
<*
@require index < self.len()
@require self.data() "Empty string"
*>
fn char DString.char_at(self, usz index) @operator([])
{
return self.data().chars[index];
}
<*
@require index < self.len()
@require self.data() "Empty string"
*>
fn char* DString.char_ref(&self, usz index) @operator(&[])
{
return &self.data().chars[index];
}
fn usz DString.append_utf32(&self, Char32[] chars)
{
self.reserve(chars.len);
@@ -168,7 +187,7 @@ fn usz DString.append_utf32(&self, Char32[] chars)
<*
@require index < self.len()
*>
fn void DString.set(self, usz index, char c)
fn void DString.set(self, usz index, char c) @operator([]=)
{
self.data().chars[index] = c;
}

View File

@@ -117,13 +117,13 @@ enum ArchType
const String COMPILER_BUILD_HASH = $$BUILD_HASH;
const String COMPILER_BUILD_DATE = $$BUILD_DATE;
const OsType OS_TYPE = (OsType)$$OS_TYPE;
const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE;
const OsType OS_TYPE = OsType.from_ordinal($$OS_TYPE);
const ArchType ARCH_TYPE = ArchType.from_ordinal($$ARCH_TYPE);
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE;
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)$$COMPILER_OPT_LEVEL;
const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$COMPILER_OPT_LEVEL);
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
@@ -135,7 +135,7 @@ 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 MemoryEnvironment MEMORY_ENV = MemoryEnvironment.from_ordinal($$MEMORY_ENVIRONMENT);
const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING);
const bool X86_64 = ARCH_TYPE == X86_64;
const bool X86 = ARCH_TYPE == X86;

View File

@@ -224,7 +224,7 @@ enum AtomicOrdering : int
*>
macro @atomic_load(&x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
return $$atomic_load(x, $volatile, (int)$ordering);
return $$atomic_load(x, $volatile, $ordering.ordinal);
}
<*
@@ -239,7 +239,7 @@ macro @atomic_load(&x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = fa
*>
macro void @atomic_store(&x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
$$atomic_store(x, value, $volatile, (int)$ordering);
$$atomic_store(x, value, $volatile, $ordering.ordinal);
}
<*
@@ -455,19 +455,23 @@ macro void @scoped(Allocator allocator; @body())
<*
Run the tracking allocator in the scope, then
print out stats.
@param $enabled "Set to false to disable tracking"
*>
macro void @report_heap_allocs_in_scope(;@body())
macro void @report_heap_allocs_in_scope($enabled = true; @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();
}
$if $enabled:
TrackingAllocator tracker;
tracker.init(allocator::thread_allocator);
Allocator old_allocator = allocator::thread_allocator;
allocator::thread_allocator = &tracker;
defer
{
allocator::thread_allocator = old_allocator;
tracker.print_report();
tracker.free();
}
$endif
@body();
}
@@ -774,3 +778,41 @@ fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMEN
return allocator::temp().resize(ptr, size, alignment)!!;
}
module std::core::mem @if(env::NO_LIBC);
fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp")
{
char* p1 = s1;
char* p2 = s2;
for (usz i = 0; i < n; i++, p1++, p2++)
{
char c1 = *p1;
char c2 = *p2;
if (c1 < c2) return -1;
if (c1 > c2) return 1;
}
return 0;
}
fn void* __memset(void* str, CInt c, usz n) @weak @export("memset")
{
char* p = str;
char cc = (char)c;
for (usz i = 0; i < n; i++, p++)
{
*p = cc;
}
return str;
}
fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy")
{
char* d = dst;
char* s = src;
for (usz i = 0; i < n; i++, d++, s++)
{
*d = *s;
}
return dst;
}

View File

@@ -21,20 +21,20 @@ 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
@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
@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
@require ptr != null
*>
fn void release(void* ptr, bool aligned);
}

View File

@@ -25,6 +25,11 @@ enum X86Feature
{
ADX,
AES,
AMX_AVX512,
AMX_FP8,
AMX_MOVRS,
AMX_TF32,
AMX_TRANSPOSE,
AMX_BF16,
AMX_COMPLEX,
AMX_FP16,
@@ -34,6 +39,8 @@ enum X86Feature
AVX,
AVX10_1_256,
AVX10_1_512,
AVX10_2_256,
AVX10_2_512,
AVX2,
AVX5124FMAPS,
AVX5124VNNIW,
@@ -84,6 +91,7 @@ enum X86Feature
MOVBE,
MOVDIR64B,
MOVDIRI,
MOVRS,
MWAITX,
PCLMUL,
PCONFIG,

View File

@@ -84,7 +84,7 @@ macro bool address_is_poisoned(void* addr)
macro void* region_is_poisoned(void* beg, usz size)
{
$if env::ADDRESS_SANITIZER:
return __asan_region_is_poisoned(addr);
return __asan_region_is_poisoned(beg, size);
$else
return null;
$endif

View File

@@ -219,12 +219,14 @@ fn String String.strip_end(string, String needle)
@param [in] s
@param [in] needle
@param [&inout] allocator "The allocator to use for the String[]"
@param max "Max number of elements, 0 means no limit, defaults to 0"
@param skip_empty "True to skip empty elements"
@param [&inout] allocator "The allocator to use for the String[]"
@require needle.len > 0 "The needle must be at least 1 character long"
@ensure return.len > 0
*>
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap())
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap(), bool skip_empty = false)
{
usz capacity = 16;
usz i = 0;
@@ -244,6 +246,11 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
res = s;
no_more = true;
}
if (!res.len && skip_empty)
{
continue;
}
if (i == capacity)
{
capacity *= 2;
@@ -261,10 +268,11 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
@param [in] s
@param [in] needle
@param max "Max number of elements, 0 means no limit, defaults to 0"
@param skip_empty "True to skip empty elements"
@require needle.len > 0 "The needle must be at least 1 character long"
@ensure return.len > 0
*>
fn String[] String.new_split(s, String needle, usz max = 0) => s.split(needle, max, allocator::heap()) @inline;
fn String[] String.new_split(s, String needle, usz max = 0, bool skip_empty) => s.split(needle, max, allocator::heap(), skip_empty) @inline;
<*
This function is identical to String.split, but implicitly uses the
@@ -273,8 +281,54 @@ fn String[] String.new_split(s, String needle, usz max = 0) => s.split(needle, m
@param [in] s
@param [in] needle
@param max "Max number of elements, 0 means no limit, defaults to 0"
@param skip_empty "True to skip empty elements"
*>
fn String[] String.tsplit(s, String needle, usz max = 0) => s.split(needle, max, allocator::temp()) @inline;
fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(needle, max, allocator::temp(), skip_empty) @inline;
fault SplitResult { BUFFER_EXCEEDED }
<*
Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
@param [in] s
@param [in] needle
@param [inout] buffer
@param max "Max number of elements, 0 means no limit, defaults to 0"
@require needle.len > 0 "The needle must be at least 1 character long"
@ensure return.len > 0
@return! SplitResult.BUFFER_EXCEEDED `If there are more elements than would fit the buffer`
*>
fn String[]! String.split_to_buffer(s, String needle, String[] buffer, usz max = 0, bool skip_empty = false)
{
usz max_capacity = buffer.len;
usz i = 0;
bool no_more = false;
while (!no_more)
{
usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle);
String res @noinit;
if (try index)
{
res = s[:index];
s = s[index + needle.len..];
}
else
{
res = s;
no_more = true;
}
if (!res.len && skip_empty)
{
continue;
}
if (i == max_capacity)
{
return SplitResult.BUFFER_EXCEEDED?;
}
buffer[i++] = res;
}
return buffer[:i];
}
<*
Check if a substring is found in the string.
@@ -719,7 +773,12 @@ fn float! String.to_float(s) => s.to_real(float);
fn Splitter String.splitter(self, String split)
{
return Splitter { self, split, 0 };
return { .string = self, .split = split };
}
fn Splitter String.tokenize(self, String split)
{
return { .string = self, .split = split, .tokenize = true };
}
struct Splitter
@@ -727,6 +786,8 @@ struct Splitter
String string;
String split;
usz current;
bool tokenize;
int last_index;
}
fn void Splitter.reset(&self)
@@ -736,18 +797,22 @@ fn void Splitter.reset(&self)
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)
while (true)
{
defer self.current = current + next + self.split.len;
return remaining[:next];
usz len = self.string.len;
usz current = self.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
String remaining = self.string[current..];
usz! next = remaining.index_of(self.split);
if (try next)
{
self.current = current + next + self.split.len;
if (!next && self.tokenize) continue;
return remaining[:next];
}
self.current = len;
return remaining;
}
self.current = len;
return remaining;
}
macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
@@ -757,8 +822,8 @@ macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
{
s.new_init(allocator: mem);
io::fprint(&s, x)!!;
return s.copy_str(allocator);
};
return s.copy_str(allocator);
}
macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp());
macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp());

View File

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

View File

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

View File

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

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

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

View File

@@ -8,7 +8,7 @@ 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;
macro void @update(&h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV32A_MUL;
fn void Fnv32a.init(&self)
{
@@ -17,17 +17,17 @@ fn void Fnv32a.init(&self)
fn void Fnv32a.update(&self, char[] data)
{
uint h = (uint)*self;
Fnv32a h = *self;
foreach (char x : data)
{
@update(h, x);
}
*self = (Fnv32a)h;
*self = h;
}
macro void Fnv32a.update_char(&self, char c)
{
@update(*self, x);
@update(*self, c);
}
fn uint encode(char[] data)
@@ -38,4 +38,4 @@ fn uint encode(char[] data)
@update(h, x);
}
return h;
}
}

View File

@@ -8,7 +8,7 @@ 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;
macro void @update(&h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV64A_MUL;
fn void Fnv64a.init(&self)
{
@@ -17,17 +17,17 @@ fn void Fnv64a.init(&self)
fn void Fnv64a.update(&self, char[] data)
{
ulong h = (ulong)*self;
Fnv64a h = *self;
foreach (char x : data)
{
@update(h, x);
}
*self = (Fnv64a)h;
*self = h;
}
macro void Fnv64a.update_char(&self, char c)
{
@update(*self, x);
@update(*self, c);
}
fn ulong encode(char[] data)
@@ -38,4 +38,4 @@ fn ulong encode(char[] data)
@update(h, x);
}
return h;
}
}

View File

@@ -45,6 +45,10 @@ struct BitWriter
uint len;
}
// c3 doesn't allow to shift more than bit width of a variable,
// so use closest byte boundary of 24 instead of 32
const int WRITER_BITS = 24;
fn void BitWriter.init(&self, OutStream byte_writer)
{
*self = { .writer = byte_writer };
@@ -53,7 +57,9 @@ fn void BitWriter.init(&self, OutStream byte_writer)
fn void! BitWriter.flush(&self)
{
if (self.len == 0) return;
uint bits = self.bits << (32 - self.len);
int padding = ($sizeof(self.bits) * 8 - self.len);
uint bits = self.bits << padding;
uint n = (self.len + 7) / 8;
char[4] buffer;
bitorder::write(bits, &buffer, UIntBE);
@@ -62,24 +68,27 @@ fn void! BitWriter.flush(&self)
}
<*
@require nbits <= 8
@require nbits <= 32
*>
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)
while (self.len + nbits > WRITER_BITS)
{
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])!;
uint to_push = WRITER_BITS - self.len;
uint bits_to_push = (bits >> (nbits - to_push)) & ((1 << to_push) - 1);
self.bits <<= to_push;
self.bits |= bits_to_push;
self.len += to_push;
nbits -= to_push;
self.flush()!;
}
self.bits <<= left;
self.bits |= bits & ((1 << left) - 1);
self.len = left;
if (nbits == 0) return;
self.bits <<= nbits;
self.bits |= bits & ((1 << nbits) - 1);
self.len += nbits;
}

View File

@@ -182,6 +182,17 @@ fn char[]! load_temp(String filename)
return load_new(filename, allocator::temp());
}
fn void! save(String filename, char[] data)
{
File file = open(filename, "wb")!;
defer (void)file.close();
while (data.len)
{
usz written = file.write(data)!;
data = data[written..];
}
}
<*
@require self.file `File must be initialized`
*>

View File

@@ -28,7 +28,8 @@ macro bool is_struct_with_default_print($Type)
{
return $Type.kindof == STRUCT
&&& !$defined($Type.to_format)
&&& !$defined($Type.to_new_string);
&&& !$defined($Type.to_new_string)
&&& !$defined($Type.to_string);
}
<*
@@ -529,4 +530,4 @@ fn usz! Formatter.print(&self, String str)
}
foreach (c : str) self.out(c)!;
return self.idx;
}
}

View File

@@ -60,7 +60,7 @@ fn void*! native_freopen(void* file, String filename, String mode) @inline
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()?;
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()?;
}

View File

@@ -121,13 +121,16 @@ fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic
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;
if (n == 0)
{
self.write_pending()!;
}
self.bytes[self.index] = 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,70 @@
module std::io;
/* MultiReader implements the InStream interface and provides a logical
* concatenation of the provided readers. They are read sequentially. If all the
* data has been read, IoError.EOF is returned.
*/
struct MultiReader (InStream)
{
InStream[] readers;
usz index;
Allocator allocator;
}
<*
@param [&inout] self
@param [&inout] allocator
@require self.readers.len == 0 "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn MultiReader* MultiReader.new_init(&self, InStream... readers, Allocator allocator = allocator::heap())
{
InStream []copy = allocator::new_array(allocator, InStream, readers.len);
copy[..] = readers[..];
*self = { .readers = copy, .allocator = allocator };
return self;
}
<*
@param [&inout] self
@require self.readers.len == 0 "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn MultiReader* MultiReader.temp_init(&self, InStream... readers)
{
return self.new_init(...readers, allocator: allocator::temp());
}
fn void MultiReader.free(&self)
{
if (!self.allocator) return;
allocator::free(self.allocator, self.readers);
*self = {};
}
fn usz! MultiReader.read(&self, char[] bytes) @dynamic
{
InStream r = self.readers[self.index];
usz! n = r.read(bytes);
if (catch err = n)
{
case IoError.EOF:
self.index++;
if (self.index >= self.readers.len)
{
return IoError.EOF?;
}
return self.read(bytes);
default:
return err?;
}
return n;
}
fn char! MultiReader.read_byte(&self) @dynamic
{
char[1] data;
self.read(data[..])!;
return data[0];
}

View File

@@ -0,0 +1,59 @@
module std::io;
/* MultiWriter implements the OutStream interface and duplicates any write
* operation to all the wrapped writers.
*/
struct MultiWriter (OutStream)
{
OutStream[] writers;
Allocator allocator;
}
<*
@param [&inout] self
@param [&inout] allocator
@require writers.len > 0
@require self.writers.len == 0 "Init may not run on already initialized data"
*>
fn MultiWriter* MultiWriter.new_init(&self, OutStream... writers, Allocator allocator = allocator::heap())
{
OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len);
copy[..] = writers[..];
*self = { .writers = copy, .allocator = allocator };
return self;
}
<*
@param [&inout] self
@require writers.len > 0
@require self.writers.len == 0 "Init may not run on already initialized data"
*>
fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers)
{
return self.new_init(...writers, allocator: allocator::temp());
}
fn void MultiWriter.free(&self)
{
if (!self.allocator) return;
allocator::free(self.allocator, self.writers);
*self = {};
}
fn usz! MultiWriter.write(&self, char[] bytes) @dynamic
{
usz n;
foreach (w : self.writers)
{
n = w.write(bytes)!;
if (n != bytes.len) return IoError.INCOMPLETE_WRITE?;
}
return bytes.len;
}
fn void! MultiWriter.write_byte(&self, char c) @dynamic
{
char[1] data;
data[0] = c;
self.write(data[..])!;
}

View File

@@ -0,0 +1,42 @@
module std::io;
struct TeeReader (InStream)
{
InStream r;
OutStream w;
}
<* Returns a reader that implements InStream and that will write any data read
from the wrapped reader r to the writer w. There is no internal buffering.
@param [&inout] r "Stream r to read from."
@param [&inout] w "Stream w to write to what it reads from r."
*>
macro TeeReader tee_reader(InStream r, OutStream w) => { r, w };
<*
@param [&inout] self
@param [&inout] r "Stream r to read from."
@param [&inout] w "Stream w to write to what it reads from r."
*>
fn TeeReader* TeeReader.init(&self, InStream r, OutStream w)
{
*self = tee_reader(r, w);
return self;
}
fn usz! TeeReader.read(&self, char[] bytes) @dynamic
{
usz nr, nw;
nr = self.r.read(bytes)!;
nw = self.w.write(bytes[:nr])!;
if (nr != nw) return IoError.GENERAL_ERROR?;
return nr;
}
fn char! TeeReader.read_byte(&self) @dynamic
{
char[1] data;
self.read(data[..])!;
return data[0];
}

View File

@@ -63,12 +63,6 @@ const bool BSD_FLAVOR_SIG @local = env::DARWIN || env::BSD_FAMILY;
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();

View File

@@ -17,10 +17,10 @@ struct Stat
Gid_t st_gid;
Dev_t st_rdev;
Timespec st_atimespec; // time of last access
Timespec st_mtimespec; // time of last data modification
Timespec st_ctimespec; // time of last status change
Timespec st_birthtimespec; // time of file creation(birth)
TimeSpec st_atimespec; // time of last access
TimeSpec st_mtimespec; // time of last data modification
TimeSpec st_ctimespec; // time of last status change
TimeSpec st_birthtimespec; // time of file creation(birth)
Off_t st_size; // file size, in bytes
Blkcnt_t st_blocks; // blocks allocated for file
Blksize_t st_blocksize; // optimal blocksize for I/O

View File

@@ -328,7 +328,7 @@ fn BigInt BigInt.unary_minus(&self)
}
macro void BigInt.div(self, BigInt other)
macro BigInt BigInt.div(self, BigInt other)
{
self.div_this(other);
return self;
@@ -831,6 +831,14 @@ fn BigInt BigInt.gcd(&self, BigInt other)
return g;
}
fn BigInt BigInt.lcm(&self, BigInt other)
{
BigInt x = self.abs();
BigInt y = other.abs();
BigInt g = y.mult(x);
return g.div(x.gcd(y));
}
<*
@require bits >> 5 < MAX_LEN "Required bits > maxlength"
*>

View File

@@ -131,6 +131,28 @@ macro deg_to_rad(x) {
*>
macro abs(x) => $$abs(x);
<*
@require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
@require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value"
*>
macro is_approx(x, y, eps)
{
if (x == y) return true;
if (is_nan(x) || is_nan(y)) return false;
return abs(x-y) <= eps;
}
<*
@require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
@require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value"
*>
macro is_approx_rel(x, y, eps)
{
if (x == y) return true;
if (is_nan(x) || is_nan(y)) return false;
return abs(x-y) <= eps * max(abs(x), abs(y));
}
<*
@require values::@is_int(x) `The input must be an integer`
*>
@@ -290,7 +312,7 @@ macro copysign(mag, sgn) => $$copysign(values::promote_int_same(mag, sgn), ($typ
<*
@require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
*>
macro cos(x) => $$cos(x);
macro cos(x) => $$cos(values::promote_int(x));
<*
@require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
@@ -1183,4 +1205,62 @@ macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, di
@require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
*>
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
<*
@require types::is_int($typeof(a)) `The input must be an integer`
@require types::is_int($typeof(b)) `The input must be an integer`
*>
macro _gcd(a, b) @private
{
if (a == 0) return b;
if (b == 0) return a;
var $Type = $typeof(a);
$Type r, aa, ab;
aa = abs(a);
ab = abs(b);
while (ab != 0)
{
r = aa % ab;
aa = ab;
ab = r;
}
return aa;
}
<*
Calculate the least common multiple for the provided arguments.
@require $vacount >= 2 `At least two arguments are required.`
*>
macro lcm(...)
{
$typeof($vaarg[0]) result = $vaarg[0];
$for (var $i = 1; $i < $vacount; $i++)
$if $defined(result.lcm):
result = result.lcm($vaarg[$i]);
$else
result = (abs($vaarg[$i]) * abs(result)) / (_gcd($vaarg[$i], result));
$endif
$endfor
return result;
}
<*
Calculate the greatest common divisor for the provided arguments.
@require $vacount >= 2 `At least two arguments are required.`
*>
macro gcd(...)
{
$typeof($vaarg[0]) result = $vaarg[0];
$for (var $i = 1; $i < $vacount; $i++)
$if $defined(result.gcd):
result = result.gcd($vaarg[$i]);
$else
result = _gcd($vaarg[$i], result);
$endif
$endfor
return result;
}

View File

@@ -9,8 +9,8 @@ union Complex
Real[<2>] v;
}
const Complex IDENTITY = { 1, 0 };
const Complex IMAGINARY = { 0, 1 };
macro Complex Complex.add(self, Complex b) => Complex { .v = self.v + b.v };
macro Complex Complex.add_each(self, Real b) => Complex { .v = self.v + b };
macro Complex Complex.sub(self, Complex b) => Complex { .v = self.v - b.v };
@@ -22,3 +22,10 @@ macro Complex Complex.div(self, Complex b)
Real div = b.v.dot(b.v);
return Complex{ (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
}
macro Complex Complex.inverse(self)
{
Real sqr = self.v.dot(self.v);
return Complex{ self.r / sqr, -self.c / sqr };
}
macro Complex Complex.conjugate(self) => Complex { .r = self.r, .c = -self.c };
macro bool Complex.equals(self, Complex b) => self.v == b.v;

View File

@@ -197,7 +197,7 @@ fn Real Matrix4x4.determinant(&self)
fn Matrix2x2 Matrix2x2.adjoint(&self)
{
return { self.m00, -self.m01, -self.m10, self.m11 };
return { self.m11, -self.m01, -self.m10, self.m00 };
}
fn Matrix3x3 Matrix3x3.adjoint(&self)

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */
/*

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */
/*

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
union DoubleInternal
{

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */
/*

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */
/*

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */
/*

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/k_tanf.c */
/*

View File

@@ -0,0 +1,146 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
const PIO2_HI @local = 1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */
const PIO2_LO @local = 6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */
const PS0 @local = 1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */
const PS1 @local = -3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */
const PS2 @local = 2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */
const PS3 @local = -4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */
const PS4 @local = 7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */
const PS5 @local = 3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */
const QS1 @local = -2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */
const QS2 @local = 2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */
const QS3 @local = -6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */
const QS4 @local = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
fn double _r(double z) @local
{
double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
double q = 1.0 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
return p / q;
}
fn double _acos(double x) @weak @extern("acos") @nostrip
{
uint hx = x.high_word();
uint ix = hx & 0x7fffffff;
switch
{
/* |x| >= 1 or nan */
case ix >= 0x3ff00000:
uint lx = x.low_word();
if ((ix - 0x3ff00000 | lx) == 0)
{
/* acos(1)=0, acos(-1)=pi */
if (hx >> 31) return 2. * PIO2_HI + 0x1p-120f;
return 0.;
}
return double.nan;
/* |x| < 0.5 */
case ix < 0x3fe00000:
/* |x| < 2**-57 */
if (ix <= 0x3c600000) return PIO2_HI + 0x1p-120f;
return PIO2_HI - (x - (PIO2_LO - x * _r(x * x)));
/* x < -0.5 */
case (hx >> 31) != 0:
double z = (1. + x) * 0.5;
double s = math::sqrt(z);
double w = _r(z) * s - PIO2_LO;
return 2. * (PIO2_HI - (s + w));
/* x > 0.5 */
default:
double z = (1. - x) * 0.5;
double s = math::sqrt(z);
double df @noinit;
{
ulong rep = bitcast(s, ulong);
rep &= 0xffffffff00000000;
df = bitcast(rep, double);
}
double c = (z - df * df) / (s + df);
double w = _r(z) * s + c;
return 2. * (df + w);
}
}
/* origin: FreeBSD /usr/src/lib/msun/src/e_acosf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
*/
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
const float PIO2_HI_F @local = 1.5707962513e+00; /* 0x3fc90fda */
const float PIO2_LO_F @local = 7.5497894159e-08; /* 0x33a22168 */
const float PS0_F @local = 1.6666586697e-01;
const float PS1_F @local = -4.2743422091e-02;
const float PS2_F @local = -8.6563630030e-03;
const float QS1_F @local = -7.0662963390e-01;
fn float _r_f(float z) @local
{
float p = z * ( PS0_F + z * (PS1_F + z * PS2_F));
float q = 1.0f + z * QS1_F;
return p / q;
}
fn float _acosf(float x) @weak @extern("acosf") @nostrip
{
uint hx = bitcast(x, uint);
uint ix = hx & 0x7fffffff;
switch
{
/* |x| >= 1 or nan */
case ix >= 0x3f800000:
if (ix == 0x3f800000)
{
if (hx >> 31) return 2.f * PIO2_HI_F + 0x1p-120f;
return 0;
}
return float.nan;
/* |x| < 0.5 */
case ix < 0x3f000000:
/* |x| < 2**-26 */
if (ix <= 0x32800000) return PIO2_HI_F + 0x1p-120f;
return PIO2_HI_F - (x - (PIO2_LO_F - x * _r_f(x * x)));
/* x < -0.5 */
case (hx >> 31) != 0:
float z = (1.f + x) * 0.5f;
float s = math::sqrt(z);
float w = _r_f(z) * s - PIO2_LO_F;
return 2.f * (PIO2_HI_F - (s + w));
/* x > 0.5 */
default:
float z = (1.f - x) * 0.5f;
float s = math::sqrt(z);
float df @noinit;
{
uint rep = bitcast(s, uint);
rep &= 0xfffff000;
df = bitcast(rep, float);
}
float c = (z - df * df) / (s + df);
float w = _r_f(z) * s + c;
return 2.f * (df + w);
}
}

View File

@@ -0,0 +1,136 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
const PIO2_HI @local = 1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */
const PIO2_LO @local = 6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */
const PS0 @local = 1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */
const PS1 @local = -3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */
const PS2 @local = 2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */
const PS3 @local = -4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */
const PS4 @local = 7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */
const PS5 @local = 3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */
const QS1 @local = -2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */
const QS2 @local = 2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */
const QS3 @local = -6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */
const QS4 @local = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
fn double _r(double z) @local
{
double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
double q = 1.0 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
return p / q;
}
fn double _asin(double x) @weak @extern("asin") @nostrip
{
uint hx = x.high_word();
uint ix = hx & 0x7fffffff;
switch
{
/* |x| >= 1 or nan */
case ix >= 0x3ff00000:
uint lx = x.low_word();
if ((ix-0x3ff00000 | lx) == 0)
{
/* asin(1) = +-pi/2 with inexact */
return x * PIO2_HI + 0x1p-120f;
}
return double.nan;
/* |x| < 0.5 */
case ix < 0x3fe00000:
/* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
if (ix < 0x3e500000 && ix >= 0x00100000) return x;
return x + x * _r(x * x);
/* 1 > |x| >= 0.5 */
default:
double z = (1. - math::abs(x)) * 0.5;
double s = math::sqrt(z);
double r = _r(z);
/* if |x| > 0.975 */
if (ix >= 0x3fef3333)
{
x = PIO2_HI - (2. * (s + s * r) - PIO2_LO);
}
else
{
double f @noinit;
{
ulong rep = bitcast(s, ulong);
rep &= 0xffffffff00000000;
f = bitcast(rep, double);
}
double c = (z - f * f) / (s + f);
x = 0.5 * PIO2_HI - (2. * s * r - (PIO2_LO - 2. * c) - (0.5 * PIO2_HI - 2. * f));
}
if (hx >> 31 != 0) return -x;
return x;
}
}
/* origin: FreeBSD /usr/src/lib/msun/src/e_asinf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
*/
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
const PIO2 @local = 1.570796326794896558e+00;
const float PS0_F @local = 1.6666586697e-01;
const float PS1_F @local = -4.2743422091e-02;
const float PS2_F @local = -8.6563630030e-03;
const float QS1_F @local = -7.0662963390e-01;
fn float _r_f(float z) @local
{
float p = z * ( PS0_F + z * (PS1_F + z * PS2_F));
float q = 1.0f + z * QS1_F;
return p / q;
}
fn float _asinf(float x) @weak @extern("asinf") @nostrip
{
uint hx = bitcast(x, uint);
uint ix = hx & 0x7fffffff;
switch
{
/* |x| >= 1 */
case ix >= 0x3f800000:
if (ix == 0x3f800000)
{
/* asin(+-1) = +-pi/2 with inexact */
return x * (float)PIO2 + 0x1p-120f;
}
return float.nan;
/* |x| < 0.5 */
case ix < 0x3f000000:
/* if 0x1p-126 <= |x| < 0x1p-12, avoid raising underflow */
if (ix < 0x39800000 && ix >= 0x00800000) return x;
return x + x * _r_f(x * x);
/* 1 > |x| >= 0.5 */
default:
float z = (1.f - math::abs(x)) * 0.5f;
float s = math::sqrt(z);
x = (float)PIO2 - 2.f * (s + s * _r_f(z));
if (hx >> 31) return -x;
return x;
}
}

View File

@@ -1,4 +1,16 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
const double[*] ATANHI @private = {
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
@@ -89,6 +101,21 @@ fn double _atan(double x) @weak @extern("atan") @nostrip
return sign ? -z : z;
}
/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
*/
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
const float[*] ATANHIF @private = {
4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
@@ -170,11 +197,24 @@ fn float _atanf(float x) @weak @extern("atanf") @nostrip
/* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
float s1 = z * (ATF[0] + w * (ATF[2] + w * ATF[4]));
float s2 = w * (ATF[1] + w * ATF[3]);
if (id < 0) return x - x * (s1 + s2) * 10000;
if (id < 0) return x - x * (s1 + s2);
z = ATANHIF[id] - ((x * (s1 + s2) - ATANLOF[id]) - x);
return sign ? -z : z;
}
/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*
*/
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
macro void extract_words(double d, uint* hi, uint* lo) @private
@@ -252,6 +292,21 @@ fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
}
}
/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2f.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
*/
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
const float PI_F @private = 3.1415927410e+00; /* 0x40490fdb */
const float PI_LO_F @private = -8.7422776573e-08; /* 0xb3bbbd2e */

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _ceil(double x) @weak @extern("ceil") @nostrip
{

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn float _cosf(float x) @extern("cosf") @weak @nostrip
{
@@ -51,7 +51,7 @@ fn float _cosf(float x) @extern("cosf") @weak @nostrip
* ====================================================
*/
fn double _cos(double x) @weak @nostrip
fn double _cos(double x) @extern("cos") @weak @nostrip
{
// High word of x.
uint ix = (uint)(bitcast(x, ulong) >> 32);

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
macro uint _top12f(float x) @private => bitcast(x, uint) >> 20;

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _floor(double x) @weak @extern("floor") @nostrip
{

View File

@@ -1,4 +1,4 @@
module std::math::nolibc;
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
const double TOINT = 1 / math::DOUBLE_EPSILON;
const double TOINT15 = 1.5 / math::DOUBLE_EPSILON;

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn float powf_broken(float x, float f) @extern("powf") @weak @nostrip
{

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
import std::math;
/* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2f.c */
@@ -23,11 +23,11 @@ import std::math;
* use __rem_pio2_large() for large x
*/
<*
/*
* invpio2: 53 bits of 2/pi
* pio2_1: first 25 bits of pi/2
* pio2_1t: pi/2 - pio2_1
*>
*/
fn int __rem_pio2f(float x, double *y)
{
const double PIO4 = 0x1.921fb6p-1;

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _round(double x) @extern("round") @weak @nostrip
{

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _scalbn(double x, int n) @weak @extern("scalbn") @nostrip
{

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */
/*

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */
/*

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
/* origin: FreeBSD /usr/src/lib/msun/src/s_tan.c */
/*

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double sincos_broken(double x) @extern("sincos") @weak @nostrip
{

View File

@@ -1,4 +1,4 @@
module std::math::nolibc @if(env::NO_LIBC);
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _trunc(double x) @weak @extern("trunc") @nostrip
{

View File

@@ -13,6 +13,21 @@ macro quicksort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @built
qs::qsort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, cmp, context);
}
<*
Select the (k+1)th smallest element in an unordered list using Hoare's
selection algorithm (Quickselect). k should be between 0 and len-1. The data
list will be partially sorted.
@require @is_sortable(list) "The list must be indexable and support .len or .len()"
@require @is_valid_cmp_fn(cmp, list, context) "expected a comparison function which compares values"
@require @is_valid_context(cmp, context) "Expected a valid context"
*>
macro quickselect(list, isz k, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
{
usz len = sort::@len_from_list(list);
return qs::qselect(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, k, cmp, context);
}
module std::sort::qs(<Type, CmpFn, Context>);
def ElementType = $typeof(Type{}[0]);
@@ -29,10 +44,6 @@ def Stack = StackElementItem[64] @private;
fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
{
var $has_cmp = @is_valid_macro_slot(cmp);
var $has_context = @is_valid_macro_slot(context);
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
if (low >= 0 && high >= 0 && low < high)
{
Stack stack;
@@ -48,34 +59,7 @@ fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
if (l < h)
{
ElementType pivot = list[l];
while (l < h)
{
$switch
$case $cmp_by_value && $has_context:
while (cmp(list[h], pivot, context) >= 0 && l < h) h--;
if (l < h) list[l++] = list[h];
while (cmp(list[l], pivot, context) <= 0 && l < h) l++;
$case $cmp_by_value:
while (cmp(list[h], pivot) >= 0 && l < h) h--;
if (l < h) list[l++] = list[h];
while (cmp(list[l], pivot) <= 0 && l < h) l++;
$case $has_cmp && $has_context:
while (cmp(&list[h], &pivot, context) >= 0 && l < h) h--;
if (l < h) list[l++] = list[h];
while (cmp(&list[l], &pivot, context) <= 0 && l < h) l++;
$case $has_cmp:
while (cmp(&list[h], &pivot) >= 0 && l < h) h--;
if (l < h) list[l++] = list[h];
while (cmp(&list[l], &pivot) <= 0 && l < h) l++;
$default:
while (greater_eq(list[h], pivot) && l < h) h--;
if (l < h) list[l++] = list[h];
while (less_eq(list[l], pivot) && l < h) l++;
$endswitch
if (l < h) list[h--] = list[l];
}
list[l] = pivot;
l = @partition(list, l, h, cmp, context);
stack[i + 1].low = l + 1;
stack[i + 1].high = stack[i].high;
stack[i++].high = l;
@@ -91,3 +75,71 @@ fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
}
}
}
<*
@require low <= k "kth smalles element is smaller than lower bounds"
@require k <= high "kth smalles element is larger than upper bounds"
*>
fn ElementType! qselect(Type list, isz low, isz high, isz k, CmpFn cmp, Context context)
{
if (low >= 0 && high >= 0 && low < high)
{
isz l = low;
isz h = high;
isz pivot;
usz max_retries = 64;
while (l <= h && max_retries--)
{
pivot = @partition(list, l, h, cmp, context);
if (k == pivot) return list[k];
if (k < pivot)
{
h = pivot - 1;
}
else
{
l = pivot + 1;
}
}
}
return SearchResult.MISSING?;
}
macro @partition(Type list, isz l, isz h, CmpFn cmp, Context context)
{
var $has_cmp = @is_valid_macro_slot(cmp);
var $has_context = @is_valid_macro_slot(context);
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
ElementType pivot = list[l];
while (l < h)
{
$switch
$case $cmp_by_value && $has_context:
while (cmp(list[h], pivot, context) >= 0 && l < h) h--;
if (l < h) list[l++] = list[h];
while (cmp(list[l], pivot, context) <= 0 && l < h) l++;
$case $cmp_by_value:
while (cmp(list[h], pivot) >= 0 && l < h) h--;
if (l < h) list[l++] = list[h];
while (cmp(list[l], pivot) <= 0 && l < h) l++;
$case $has_cmp && $has_context:
while (cmp(&list[h], &pivot, context) >= 0 && l < h) h--;
if (l < h) list[l++] = list[h];
while (cmp(&list[l], &pivot, context) <= 0 && l < h) l++;
$case $has_cmp:
while (cmp(&list[h], &pivot) >= 0 && l < h) h--;
if (l < h) list[l++] = list[h];
while (cmp(&list[l], &pivot) <= 0 && l < h) l++;
$default:
while (greater_eq(list[h], pivot) && l < h) h--;
if (l < h) list[l++] = list[h];
while (less_eq(list[l], pivot) && l < h) l++;
$endswitch
if (l < h) list[h--] = list[l];
}
list[l] = pivot;
return l;
}

59
lib/std/sort/sorted.c3 Normal file
View File

@@ -0,0 +1,59 @@
module std::sort;
<*
Returns true if list is sorted in either ascending or descending order.
@require @is_sortable(list) "The list must be indexable and support .len or .len()"
@require @is_valid_cmp_fn(cmp, list, ctx) "Expected a comparison function which compares values"
@require @is_valid_context(cmp, ctx) "Expected a valid context"
*>
macro bool is_sorted(list, cmp = EMPTY_MACRO_SLOT, ctx = EMPTY_MACRO_SLOT) @builtin
{
var $Type = $typeof(list);
usz len = sort::@len_from_list(list);
if (len <= 1) return true;
var check_sort = fn bool($Type list, usz start, usz end, $typeof(cmp) cmp, $typeof(ctx) ctx)
{
usz i;
int sort_order;
// determine sort order (ascending or descending)
for (i = start; i < end && sort_order == 0; i++)
{
sort_order = @sort_cmp(list, i, cmp, ctx);
}
// no sort order found, all elements are the same, consider list sorted
if (sort_order == 0) return true;
// compare adjacent elements to the sort order
for (; i < end; i++)
{
if (sort_order * @sort_cmp(list, i, cmp, ctx) < 0) return false;
}
return true;
};
return check_sort(list, 0, len - 1, cmp, ctx);
}
macro int @sort_cmp(list, pos, cmp, ctx) @local
{
var $has_cmp = @is_valid_macro_slot(cmp);
var $has_context = @is_valid_macro_slot(ctx);
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom($typeof(cmp).paramsof[0].type));
var a = list[pos];
var b = list[pos+1];
$switch
$case $cmp_by_value && $has_context:
return cmp(a, b);
$case $cmp_by_value:
return cmp(a, b);
$case $has_cmp && $has_context:
return cmp(&a, &b, ctx);
$case $has_cmp:
return cmp(&a, &b);
$default:
return compare_to(a,b);
$endswitch
}

View File

@@ -44,9 +44,9 @@ fn TzDateTime DateTime.to_local(&self)
dt.min = (char)tm.tm_min;
dt.hour = (char)tm.tm_hour;
dt.day = (char)tm.tm_mday;
dt.month = (Month)tm.tm_mon;
dt.month = Month.from_ordinal(tm.tm_mon);
dt.year = tm.tm_year + 1900;
dt.weekday = !tm.tm_wday ? Weekday.SUNDAY : (Weekday)tm.tm_wday + 1;
dt.weekday = !tm.tm_wday ? Weekday.SUNDAY : Weekday.from_ordinal(tm.tm_wday - 1);
dt.year_day = (ushort)tm.tm_yday;
dt.time = self.time;
$if $defined(tm.tm_gmtoff):
@@ -142,9 +142,9 @@ fn void DateTime.set_time(&self, Time time)
self.min = (char)tm.tm_min;
self.hour = (char)tm.tm_hour;
self.day = (char)tm.tm_mday;
self.month = (Month)tm.tm_mon;
self.month = Month.from_ordinal(tm.tm_mon);
self.year = tm.tm_year + 1900;
self.weekday = !tm.tm_wday ? Weekday.SUNDAY : (Weekday)tm.tm_wday + 1;
self.weekday = !tm.tm_wday ? Weekday.SUNDAY : Weekday.from_ordinal(tm.tm_wday - 1);
self.year_day = (ushort)tm.tm_yday;
self.time = time;
}
@@ -183,7 +183,7 @@ fn DateTime DateTime.add_months(&self, int months)
year += month / 12;
month %= 12;
}
return from_date(year, (Month)month, self.day, self.hour, self.min, self.sec, self.usec);
return from_date(year, Month.from_ordinal(month), self.day, self.hour, self.min, self.sec, self.usec);
}

View File

@@ -234,10 +234,19 @@ lib = list((OUTPUT / "VC/Tools/MSVC/").glob("*/lib"))[0]
SDK_OUTPUT.mkdir(exist_ok=True)
def copy(src, dst):
low = dst.lower()
base = os.path.basename(low)
if base == "msvcrt.lib" or base == "oldnames.lib":
base = base[:-3].upper() + "lib"
path = os.path.join(os.path.dirname(low), base);
shutil.copy(src, path)
shutil.copy(src, low)
for arch in archs:
out_dir = SDK_OUTPUT / arch
shutil.copytree(ucrt / arch, out_dir, dirs_exist_ok=True)
shutil.copytree(um / arch, out_dir, dirs_exist_ok=True)
shutil.copytree(lib / arch, out_dir, dirs_exist_ok=True)
shutil.copytree(ucrt / arch, out_dir, copy_function=copy, dirs_exist_ok=True)
shutil.copytree(um / arch, out_dir, copy_function=copy, dirs_exist_ok=True)
shutil.copytree(lib / arch, out_dir, copy_function=copy, dirs_exist_ok=True)
print("Congratulations! The 'msvc_sdk' directory was successfully generated.")

85
nix/default.nix Normal file
View File

@@ -0,0 +1,85 @@
{
lib,
llvmPackages,
cmake,
python3,
curl,
libxml2,
libffi,
xar,
debug ? false,
checks ? false,
}: let
inherit (builtins) baseNameOf toString readFile elemAt;
inherit (lib.sources) cleanSourceWith cleanSource;
inherit (lib.lists) findFirst;
inherit (lib.asserts) assertMsg;
inherit (lib.strings) hasInfix hasSuffix splitString removeSuffix removePrefix optionalString;
in
llvmPackages.stdenv.mkDerivation (finalAttrs: {
pname = "c3c${optionalString debug "-debug"}";
version = let
findLine = findFirst (x: hasInfix "COMPILER_VERSION" x) "none";
foundLine = findLine ( splitString "\n" ( readFile ../src/version.h ) );
version = removeSuffix "\"" ( removePrefix "\"" ( elemAt ( splitString " " foundLine ) 2 ) );
in
assert assertMsg (foundLine != "none") "No COMPILER_VERSION substring was found in version.h";
version;
src = cleanSourceWith {
filter = _path: _type: !(hasSuffix ".nix" (baseNameOf(toString _path)));
src = cleanSource ../.;
};
postPatch = ''
substituteInPlace CMakeLists.txt \
--replace-fail "\''${LLVM_LIBRARY_DIRS}" "${llvmPackages.lld.lib}/lib ${llvmPackages.llvm.lib}/lib"
'';
cmakeBuildType = if debug then "Debug" else "Release";
cmakeFlags = [
"-DC3_ENABLE_CLANGD_LSP=${if debug then "ON" else "OFF"}"
];
nativeBuildInputs = [ cmake ];
postBuild = optionalString debug ''
mkdir $out
substituteInPlace compile_commands.json \
--replace "/build/source/" "$src/"
cp compile_commands.json $out/compile_commands.json
'';
buildInputs = [
llvmPackages.llvm
llvmPackages.lld
curl
libxml2
libffi
] ++ lib.optionals llvmPackages.stdenv.hostPlatform.isDarwin [ xar ];
nativeCheckInputs = [ python3 ];
doCheck = llvmPackages.stdenv.system == "x86_64-linux" && checks;
checkPhase = ''
runHook preCheck
( cd ../resources/testproject; ../../build/c3c build )
( cd ../test; python src/tester.py ../build/c3c test_suite )
runHook postCheck
'';
meta = with lib; {
description = "Compiler for the C3 language";
homepage = "https://github.com/c3lang/c3c";
license = licenses.lgpl3Only;
maintainers = with maintainers; [
luc65r
anas
];
platforms = platforms.all;
mainProgram = "c3c";
};
})

20
nix/shell.nix Normal file
View File

@@ -0,0 +1,20 @@
{
mkShell,
clang-tools,
c3c,
}:
mkShell {
inputsFrom = [
c3c
];
packages = [
clang-tools
c3c
];
shellHook = ''
ln -sf ${c3c}/compile_commands.json compile_commands.json
'';
}

View File

@@ -1,5 +1,94 @@
# C3C Release Notes
## 0.6.6 Change list
### Changes / improvements
- Split help into normal and "full" help, #1703
- Removed 'headers' command line option.
- Add `enum.from_ordinal` and `fault.from_ordinal`
- Deprecate cast-style conversion from integer <-> enum.
- Make deprecation an error in test mode.
- Add `--win-vs-dirs` to override VS detection dirs.
- Add `"name"` project property to override the name of the resulting binary. #1719
- Improved `add-project` to take arguments.
### Fixes
- Fix case trying to initialize a `char[*]*` from a String.
- Fix Map & HashMap `put_all_for_create` not copying all elements, causing `init_from_map` to create incomplete copy.
- Fix bug when a macro calling an extern function was called in another module also declaring and calling the same function. #1690
- `static-lib` and `dynamic-lib` options from the command line now produces headers.
- Fix bug outputting exported functions without predefined extname.
- Fix problem where crt1 was linked for dynamic libraries on Linux and BSD. #1710
- Fix CRT detection on Arch Linux.
- Fix lexer allowing a trailing underscore (_) with hex and binary literals.
- Fix `--list-operators` CLI command printing underscore (_) and hash (#).
- Fix bug in temp allocator when temp memory is exhausted and allocation needs overaligned mem. #1715
- Incorrectly handles distinct enums and pointers with '+=' and '-=' #1717.
- Prevent DString from being initialized with "".
- Fix bug in OnStackAllocator when freeing overallocated data. #1720
- Use `weak_odr` rather than `weak` on Windows which seems to prevent issues such as #1704.
- Use `weak` on dyn-symbols on Linux.
- Fix crash on project.json not having an empty set of targets.
### Stdlib changes
- Increase BitWriter.write_bits limit up to 32 bits.
- Updates to `Slice2d`, like `get_xy` and others.
- Added `iter()` `value_iter()` and `key_iter()` to HashMap.
- Add "tokenizer" to String.
- Add "skip_empty" to split methods. Add split_to_buffer method.
- Add `@enum_from_value`.
## 0.6.5 Change list
### Changes / improvements
- Allow splat in initializers.
- Init command will now add `test-sources` to `project.json` #1520
- `a++` may be discarded if `a` is optional and ++/-- works for overloaded operators.
- Improve support for Windows cross compilation on targets with case sensitive file systems.
- Add "sources" support to library `manifest.json`, defaults to root folder if unspecified.
- Add char_at method in DString and operators [], len, []= and &[].
- Add `-q` option, make `--run-once` implicitly `-q`.
- Add `-v`, `-vv` and `-vvv` options for increasing verbosity, replacing debug-log and debug-stats options.
### Fixes
- Fix bug where `a > 0 ? f() : g()` could cause a compiler crash if both returned `void!`.
- `@builtin` was not respected for generic modules #1617.
- Fix issue writing a single byte in the WriteBuffer
- A distinct inline pointer type can now participate in pointer arithmetics.
- Support &a[0] returning the distinct type when applying it to a distinct of a pointer.
- Fix error when calling `HashMap.remove` on uninitialized `HashMap`.
- Fix issue with resolved try-unwrap in defer.
- Fix issue with overloaded subscript and ++/-- and assign ops (e.g. `*=`)
- Fix issue with properties in different targets not being respected #1633.
- Indexing an Optional slice would crash in codegen #1636.
- SimpleHeapAllocator bug when splitting blocks allowed memory overrun.
- Not possible to alias or take reference for extension methods on non-user defined types. #1637
- Prevent methods from using names of properties or fields. #1638
- b64 / hex data strings can now be used with \` as well.
- Contracts on generic modules would evaluate too late, sometimes not catching the error until it already occurred elsewhere.
- Fix bug preventing optionals from being used in ranges or as indices.
- Crash compiling for arm64 when returning 16 byte and smaller structs by value not a power of 2 #1649.
- Enforce single module compilation for static libraries to make constructors run properly.
- Crash when using --no-obj without compile-only. #1653
- Do not produce expression locations for windows.
- Issue where multiple methods were accepted for the same type.
- Issue where a method was linked to a type alias instead of the underlying type.
- Fix Fnv1a encoding.
- Fix issue with accessing arrays in access-overloaded types, e.g. `list[1][2]` #1665.
- Cast removing arbitrary array indices and converting them to pointers should always be fine #1664
- Incorrect "no-libc" definition of `cos`, making it unavailable for wasm.
- Fix issue with the adjoint and inverse calculations for `Matrix2x2`.
- It was possible to create 0 length arrays using byte literals. #1678
- Crash when a constant null typeid is checked for properties. #1679
### Stdlib changes
- Add `io::MultiReader`, `io::MultiWriter`, and `io::TeeReader` structs.
- Updated Base32 API.
- Add `file::save`.
- Add `memcpy` / `memset` / `memcmp` to nolibc.
- Add `sort::quickselect` to find the k-th smallest element in an unordered list.
- Add `sort::is_sorted` to determine if a list is sorted.
## 0.6.4 Change list
### Changes / improvements

View File

@@ -1,31 +1,196 @@
Some short names:
1. expl - explicit
2. sw/sn - simple widening + subexpression narrowing
3. yes - always allowed
4. explptr - explicit if pointer sized
5. ptrconv - to/from void* is fine, other cast must be explicit
6. saconv - explicit if same element size
7. explbase - explicit to base disregaring sign
8. distel - explicit to same element disregarding distinct
9. inline - implicit if type subtype
10. cond - implicit in cond, explicit otherwise
11. edist - explicit to anything underlying type can convert to, if inline as underlying
12. arve - if array or vec ptr
## Special rules for distinct types
| from, to | bool | int | float | pointer | subarr | vec | bits | distc | array | struct | union | any | fault | enum | typeid |
|----------|--------|----------|--------|---------|--------|----------|----------|-------|----------|--------|--------|--------|--------|--------|--------|
| bool | n/a | expl | expl | no | no | expand | no | edist | no | no | no | no | no | no | no |
| int | cond | sw/sn | always | explptr | no | expand | explbase | edist | no | no | no | no | no | edist | no |
| float | cond | expl | sw/sn | no | no | expand | no | edist | no | no | no | no | no | no | no |
| pointer | cond | explptr | no | ptrconv | arve | expand | no | edist | no | no | no | yes | expl | no | expl |
| slice | cond | no | no | no | saconv | no | no | edist | no? | no | no | no | no | no | no |
| vec | cond | no | no | no | no | as base | no | edist | expl | no | no | no | no | no | no |
| bits | no | explbase | no | no | no | no | no? | edist | explbase | no | no | no | no | no | no |
| distc | edist | edist | edist | edist | edist | edist | edist | edist | edist | edist | edist | edist | edist | edist | edist |
| array | no | no | no | no | no | explbase | explbase | edist | distel | no | no | no | no | no | no |
| struct | inline | inline | inline | inline | inline | inline | inline | edist | inline | inline | inline | inline | inline | inline | inline |
| union | no | no | no | no | no | no | no | edist | no | no | no | no | no | no | no |
| any | cond | no | no | expl | no | no | no | edist | no | no | no | n/a | no | no | no |
| fault | cond | explptr | no | expl | no | no | no | edist | no | no | no | no | anyf | no | no |
| enum | no | expl | no | no | no | expand | no | edist | no | no | no | no | no | no | no |
| typeid | cond | no | no | expl | no | no | no | edist | no | no | no | no | no | no | n/a |
1. Implicit conversion will happen from constant values that can be implicitly converted to the underlying type.
2. Implicit conversion will happen to the underlying type if inline.
## Special rules for vectors
1. Implicit conversion will happen for non-vector numeric types, bools and pointers for init only.
2. Explicit conversion is available in all other cases.
## Special rules for bool conversion in conditionals
Types that may convert to a bool, will implicitly do so in a conditional.
### Bool
#### Implicit conversion
None
#### Explicit conversion
1. To float => 0.0 and 1.0
2. To int => 0 and 1 (note, there is an argument for ~0 instead)
### Int
#### Implicit conversion
1. To float if simple expression
2. To wider int if simple expression
#### Explicit conversion
1. To bool/float/int always works
2. To pointer if the int is pointer sized
3. To enum (const will be range checked)
4. To bitstruct if size matches
5. To bool
### Float
#### Implicit conversion
1. To wider float if simple expression
#### Explicit conversion
1. To int / float
2. To bool
### Non "void\*" pointer
#### Implicit conversion
1. To void*
2. To `any`
3. To slice if it is a pointer to a vector or array
4. To an interface if the pointee implements the interface.
#### Explicit conversion
1. To any pointer
2. To any interface
3. To an int size pointer
4. To bool
### "void\*" pointer
#### Implicit conversion
1. To any pointer
#### Explicit conversion
1. To a pointer sized int.
2. To bool
### Slice
#### Implicit conversion
1. To a pointer of the same base pointer. For constant strings the ichar and char are equivalent.
2. To an array or vector of the same compile time known length if the array/vector conversion exists.
#### Explicit conversion
1. To a pointer of the same structure.
2. To a slice of the same structure.
3. To an array of the vector of the same compile time known length if the explicit array/vector conversion exists.
### Vector
#### Implicit conversion
1. To a slice of the same type *if* the vector is constant.
2. To another vector if the base type conversion is implicit
3. To an array if the conversion would work if this was an array -> array conversion.
#### Explicit conversion
1. To a slice of the same structural equivalent type *if* the vector is constant.
2. To another vector if the base type conversion is valid.
3. To an array if the conversion would work if this was an explicit array -> array conversion.
### Array
#### Implicit conversion
1. To a slice of the same type *if* the array is constant.
2. To another array of the same length but with compatible types.
3. To a vector if the conversion would work if this was an array -> array conversion.
#### Explicit conversion
1. To a slice of the same structural equivalent type *if* the array is constant.
2. To another array of the same length with structurally equivalent elements.
3. To a vector if the conversion would work if this was an explicit array -> array conversion.
### Bitstruct
#### Implicit conversion
None
#### Explicit conversion
1. To the underlying type (integer or char array).
2. To bool
### Struct
#### Implicit conversion
1. To inline member
#### Explicit conversion
None
### Union
No conversions
### "any"
#### Implicit conversion
1. To `void*`
#### Explicit conversion
1. To any interface
2. To any pointer
3. To bool
### Interface
#### Implicit conversion
1. To `void*`
2. To `any`
2. To a parent interface
#### Explicit conversion
1. To any other interface
2. To any pointer
3. To bool
### Fault
#### Implicit conversion
1. To `anyfault`
#### Explicit conversion
1. To a pointer sized int
2. To bool
3. To pointer
### "anyfault"
#### Explicit conversion
1. To a pointer sized int
2. To bool
3. To pointer
4. To a `fault`
### "typeid"
#### Explicit conversion
1. To a pointer size int
2. To bool
3. To pointer

View File

@@ -124,12 +124,12 @@ fn void update_game()
if (rl::isKeyPressed(rl::KEY_RIGHT) && allow_move)
{
snake_direction = (SnakeDirection)((snake_direction.ordinal + 1) % 4);
snake_direction = SnakeDirection.from_ordinal((snake_direction.ordinal + 1) % 4);
allow_move = false;
}
if (rl::isKeyPressed(rl::KEY_LEFT) && allow_move)
{
snake_direction = (SnakeDirection)((snake_direction.ordinal + 3) % 4);
snake_direction = SnakeDirection.from_ordinal((snake_direction.ordinal + 3) % 4);
allow_move = false;
}

View File

@@ -0,0 +1,19 @@
module add;
import std;
struct Foo(Printable)
{
int a;
}
fn usz! Foo.to_format(&self, Formatter* f) @dynamic
{
return f.printf("Foo[%d]", self.a);
}
fn int add(int a, int b) @export("adder")
{
io::printn("In adder");
Foo x = { a };
io::printfn("Print foo: %s", x);
return a + b;
}

View File

@@ -0,0 +1,8 @@
#include <stdio.h>
extern int adder(int a, int b);
int main()
{
printf("%d\n", adder(1, 4));
return 0;
}

View File

@@ -0,0 +1,9 @@
module add;
import std;
extern fn int adder(int a, int b);
fn int main()
{
io::printn(adder(1, 4));
return 0;
}

View File

@@ -17,8 +17,9 @@
typedef enum
{
BACKEND_LLVM = 1,
BACKEND_TB = 2
BACKEND_LLVM = 0,
BACKEND_TB = 1,
BACKEND_C = 2,
} CompilerBackend;
typedef enum
@@ -28,7 +29,6 @@ typedef enum
COMMAND_COMPILE_ONLY,
COMMAND_COMPILE_BENCHMARK,
COMMAND_COMPILE_TEST,
COMMAND_GENERATE_HEADERS,
COMMAND_INIT,
COMMAND_INIT_LIB,
COMMAND_BUILD,
@@ -416,6 +416,7 @@ typedef struct BuildOptions_
{
const char *sdk;
const char *def;
const char *vs_dirs;
WinCrtLinking crt_linking;
} win;
struct
@@ -453,6 +454,7 @@ typedef struct BuildOptions_
ProjectSubcommand command;
const char *target_name;
TargetType target_type;
const char **sources;
} project_options;
CompileOption compile_option;
TrustLevel trust_level;
@@ -479,6 +481,7 @@ typedef struct BuildOptions_
bool print_output;
bool print_input;
bool run_once;
int verbosity_level;
const char *panicfn;
const char *benchfn;
const char *testfn;
@@ -523,6 +526,7 @@ typedef struct
const char *cc;
const char *cflags;
WinCrtLinking win_crt;
const char **source_dirs;
const char **csource_dirs;
const char **csources;
const char **cinclude_dirs;
@@ -540,6 +544,7 @@ typedef struct Library__
const char **execs;
const char *cc;
const char *cflags;
const char **source_dirs;
const char **csource_dirs;
const char **cinclude_dirs;
WinCrtLinking win_crt;
@@ -553,6 +558,7 @@ typedef struct
Library **library_list;
LibraryTarget **ccompiling_libraries;
const char *name;
const char *output_name;
const char *version;
const char *langrev;
const char **source_dirs;
@@ -587,6 +593,7 @@ typedef struct
bool emit_object_files;
bool benchmarking;
bool testing;
bool silent;
bool read_stdin;
bool print_output;
bool print_input;
@@ -594,6 +601,7 @@ typedef struct
bool no_entry;
bool kernel_build;
bool silence_deprecation;
bool print_stats;
int build_threads;
TrustLevel trust_level;
OptimizationSetting optsetting;
@@ -657,6 +665,7 @@ typedef struct
{
const char *sdk;
const char *def;
const char *vs_dirs;
WinCrtLinking crt_linking;
bool use_win_subsystem;
} win;

View File

@@ -88,6 +88,12 @@ static const char *optlevels[4] = {
[OPTIMIZATION_AGGRESSIVE] = "max",
};
static const char *backends[3] = {
[BACKEND_LLVM] = "llvm",
[BACKEND_TB] = "tb",
[BACKEND_C] = "c",
};
static const char *backtrace_levels[2] = {
[SHOW_BACKTRACE_OFF] = "off",
[SHOW_BACKTRACE_ON] = "on",

View File

@@ -40,10 +40,10 @@ const char *trust_level[3];
#define EOUTPUT(string, ...) fprintf(stderr, string "\n", ##__VA_ARGS__) // NOLINT
#define PRINTF(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__) // NOLINT
#define FAIL_WITH_ERR(string, ...) do { fprintf(stderr, "Error: " string "\n\n", ##__VA_ARGS__); usage(); exit_compiler(EXIT_FAILURE); } while (0) /* NOLINT */
#define FAIL_WITH_ERR(string, ...) do { fprintf(stderr, "Error: " string "\n\n", ##__VA_ARGS__); usage(false); exit_compiler(EXIT_FAILURE); } while (0) /* NOLINT */
#define PROJECT_FAIL_WITH_ERR(string, ...) do { fprintf(stderr, "Error: " string "\n\n", ##__VA_ARGS__); project_usage(); exit_compiler(EXIT_FAILURE); } while (0) /* NOLINT */
static void usage(void)
static void usage(bool full)
{
PRINTF("Usage: %s [<options>] <command> [<args>]", args[0]);
PRINTF("");
@@ -71,18 +71,11 @@ static void usage(void)
PRINTF(" vendor-fetch <library> ... Fetches one or more libraries from the vendor collection.");
PRINTF(" project <subcommand> ... Manipulate or view project files.");
PRINTF("");
PRINTF("Options:");
PRINTF(" --stdlib <dir> - Use this directory as the C3 standard library path.");
PRINTF(" --no-entry - Do not generate (or require) a main function.");
PRINTF(" --libdir <dir> - Add this directory to the C3 library search paths.");
PRINTF(" --lib <name> - Add this library to the compilation.");
PRINTF(" --path <dir> - Use this as the base directory for the current command.");
PRINTF(" --template <template> - Select template for 'init': \"exe\", \"static-lib\", \"dynamic-lib\" or a path.");
PRINTF(" --about - Prints a short description of C3.");
PRINTF(" --symtab <value> - Sets the preferred symtab size.");
PRINTF(" --max-mem <value> - Sets the preferred max memory size.");
PRINTF(" --run-once - After running the output file, delete it immediately.");
PRINTF(full ? "Options:" : "Common options:");
PRINTF(" -h -hh --help - Print the help, -h for the normal options, -hh for the full help.");
PRINTF(" -V --version - Print version information.");
PRINTF(" -q --quiet - Silence unnecessary output.");
PRINTF(" -v -vv -vvv - Verbose output, -v for default, -vv and -vvv gives more information.");
PRINTF(" -E - Lex only.");
PRINTF(" -P - Only parse and output the AST as JSON.");
PRINTF(" -C - Only lex, parse and check.");
@@ -98,26 +91,40 @@ static void usage(void)
PRINTF(" -Oz - Unsafe, high optimization, tiny code, single module, no debug info, no panic messages, no backtrace.");
PRINTF(" -D <name> - Add feature flag <name>.");
PRINTF(" -U <name> - Remove feature flag <name>.");
PRINTF(" --trust=<option> - Trust level: none (default), include ($include allowed), full ($exec / exec allowed).");
PRINTF(" --output-dir <dir> - Override general output directory.");
PRINTF(" --build-dir <dir> - Override build output directory.");
PRINTF(" --obj-out <dir> - Override object file output directory.");
PRINTF(" --script-dir <dir> - Override the base directory for $exec.");
PRINTF(" --llvm-out <dir> - Override llvm output directory for '--emit-llvm'.");
PRINTF(" --emit-llvm - Emit LLVM IR as a .ll file per module.");
PRINTF(" --asm-out <dir> - Override asm output directory for '--emit-asm'.");
PRINTF(" --emit-asm - Emit asm as a .s file per module.");
PRINTF(" --obj - Emit object files. (Enabled by default)");
PRINTF(" --no-obj - Do not output object files, this is only valid for `compile-only`.");
PRINTF(" --no-headers - Do not generate C headers when building a library.");
PRINTF(" --target <target> - Compile for a particular architecture + OS target.");
PRINTF(" --threads <number> - Set the number of threads to use for compilation.");
PRINTF(" --safe=<yes|no> - Turn safety (contracts, runtime bounds checking, null pointer checks etc) on or off.");
PRINTF(" --panic-msg=<yes|no> - Turn panic message output on or off.");
PRINTF(" --optlevel=<option> - Code optimization level: none, less, more, max.");
PRINTF(" --optsize=<option> - Code size optimization: none, small, tiny.");
PRINTF(" --single-module=<yes|no> - Compile all modules together, enables more inlining.");
PRINTF(" --show-backtrace=<yes|no> - Show detailed backtrace on segfaults.");
PRINTF("");
PRINTF(" --about - Prints a short description of C3.");
PRINTF(" --libdir <dir> - Add this directory to the c3l library search paths.");
PRINTF(" --lib <name> - Add this c3l library to the compilation.");
if (full)
{
PRINTF(" --stdlib <dir> - Use this directory as the C3 standard library path.");
PRINTF(" --no-entry - Do not generate (or require) a main function.");
PRINTF(" --path <dir> - Use this as the base directory for the current command.");
PRINTF(" --template <template> - Select template for 'init': \"exe\", \"static-lib\", \"dynamic-lib\" or a path.");
PRINTF(" --symtab <value> - Sets the preferred symtab size.");
PRINTF(" --max-mem <value> - Sets the preferred max memory size.");
PRINTF(" --run-once - After running the output file, delete it immediately.");
PRINTF(" --trust=<option> - Trust level: none (default), include ($include allowed), full ($exec / exec allowed).");
PRINTF(" --output-dir <dir> - Override general output directory.");
PRINTF(" --build-dir <dir> - Override build output directory.");
PRINTF(" --obj-out <dir> - Override object file output directory.");
PRINTF(" --script-dir <dir> - Override the base directory for $exec.");
PRINTF(" --llvm-out <dir> - Override llvm output directory for '--emit-llvm'.");
PRINTF(" --emit-llvm - Emit LLVM IR as a .ll file per module.");
PRINTF(" --asm-out <dir> - Override asm output directory for '--emit-asm'.");
PRINTF(" --emit-asm - Emit asm as a .s file per module.");
PRINTF(" --obj - Emit object files. (Enabled by default)");
PRINTF(" --no-obj - Do not output object files, this is only valid for `compile-only`.");
PRINTF(" --no-headers - Do not generate C headers when building a library.");
PRINTF(" --target <target> - Compile for a particular architecture + OS target.");
PRINTF(" --threads <number> - Set the number of threads to use for compilation.");
PRINTF(" --safe=<yes|no> - Turn safety (contracts, runtime bounds checking, null pointer checks etc) on or off.");
PRINTF(" --panic-msg=<yes|no> - Turn panic message output on or off.");
PRINTF(" --optlevel=<option> - Code optimization level: none, less, more, max.");
PRINTF(" --optsize=<option> - Code size optimization: none, small, tiny.");
PRINTF(" --single-module=<yes|no> - Compile all modules together, enables more inlining.");
PRINTF(" --show-backtrace=<yes|no> - Show detailed backtrace on segfaults.");
}
PRINTF("");
PRINTF(" -g - Emit debug info.");
PRINTF(" -g0 - Emit no debug info.");
@@ -125,60 +132,65 @@ static void usage(void)
PRINTF(" -l <library> - Link with the library provided.");
PRINTF(" -L <library dir> - Append the directory to the linker search paths.");
PRINTF(" -z <argument> - Send the <argument> as a parameter to the linker.");
PRINTF(" --cc <path> - Set C compiler (for C files in projects and use as system linker).");
PRINTF(" --linker=<option> [<path>] - Linker: builtin, cc, custom (default is 'cc'), 'custom' requires a path.");
PRINTF("");
PRINTF(" --use-stdlib=<yes|no> - Include the standard library (default: yes).");
PRINTF(" --link-libc=<yes|no> - Link libc other default libraries (default: yes).");
PRINTF(" --emit-stdlib=<yes|no> - Output files for the standard library. (default: yes)");
PRINTF(" --panicfn <name> - Override the panic function name.");
PRINTF(" --testfn <name> - Override the test runner function name.");
PRINTF(" --benchfn <name> - Override the benchmark runner function name.");
PRINTF("");
PRINTF(" --reloc=<option> - Relocation model: none, pic, PIC, pie, PIE.");
PRINTF(" --x86cpu=<option> - Set general level of x64 cpu: baseline, ssse3, sse4, avx1, avx2-v1, avx2-v2 (Skylake/Zen1+), avx512 (Icelake/Zen4+), native.");
PRINTF(" --x86vec=<option> - Set max type of vector use: none, mmx, sse, avx, avx512, default.");
PRINTF(" --riscvfloat=<option> - Set type of RISC-V float support: none, float, double");
PRINTF(" --memory-env=<option> - Set the memory environment: normal, small, tiny, none.");
PRINTF(" --strip-unused=<yes|no> - Strip unused code and globals from the output. (default: yes)");
PRINTF(" --fp-math=<option> - FP math behaviour: strict, relaxed, fast.");
PRINTF(" --win64-simd=<option> - Win64 SIMD ABI: array, full.");
PRINTF("");
PRINTF(" --debug-stats - Print debug statistics.");
PRINTF(" --print-linking - Print linker arguments.");
#ifndef NDEBUG
PRINTF(" --debug-log - Print debug logging to stdout.");
#endif
PRINTF("");
PRINTF(" --benchmarking - Run built-in benchmarks.");
PRINTF(" --testing - Run built-in tests.");
PRINTF("");
PRINTF(" --list-attributes - List all attributes.");
PRINTF(" --list-builtins - List all builtins.");
PRINTF(" --list-keywords - List all keywords.");
PRINTF(" --list-operators - List all operators.");
PRINTF(" --list-precedence - List operator precedence order.");
PRINTF(" --list-project-properties - List all available keys used in project.json files.");
PRINTF(" --list-manifest-properties - List all available keys used in manifest.json files.");
PRINTF(" --list-targets - List all architectures the compiler supports.");
PRINTF(" --list-type-properties - List all type properties.");
PRINTF("");
PRINTF(" --print-output - Print the object files created to stdout.");
PRINTF(" --print-input - Print inputted C3 files to stdout.");
PRINTF("");
PRINTF(" --winsdk <dir> - Set the directory for Windows system library files for cross compilation.");
PRINTF(" --wincrt=<option> - Windows CRT linking: none, static-debug, static, dynamic-debug (default if debug info enabled), dynamic (default).");
PRINTF(" --windef <file> - Use Windows 'def' file for function exports instead of 'dllexport'.");
PRINTF("");
PRINTF(" --macossdk <dir> - Set the directory for the MacOS SDK for cross compilation.");
PRINTF(" --macos-min-version <ver> - Set the minimum MacOS version to compile for.");
PRINTF(" --macos-sdk-version <ver> - Set the MacOS SDK compiled for.");
PRINTF("");
PRINTF(" --linux-crt <dir> - Set the directory to use for finding crt1.o and related files.");
PRINTF(" --linux-crtbegin <dir> - Set the directory to use for finding crtbegin.o and related files.");
PRINTF("");
PRINTF(" --vector-conv=<option> - Set vector conversion behaviour: default, old.");
PRINTF(" --sanitize=<option> - Enable sanitizer: address, memory, thread.");
if (full)
{
PRINTF(" --cc <path> - Set C compiler (for C files in projects and use as system linker).");
PRINTF(" --linker=<option> [<path>] - Linker: builtin, cc, custom (default is 'cc'), 'custom' requires a path.");
PRINTF("");
PRINTF(" --use-stdlib=<yes|no> - Include the standard library (default: yes).");
PRINTF(" --link-libc=<yes|no> - Link libc other default libraries (default: yes).");
PRINTF(" --emit-stdlib=<yes|no> - Output files for the standard library. (default: yes)");
PRINTF(" --panicfn <name> - Override the panic function name.");
PRINTF(" --testfn <name> - Override the test runner function name.");
PRINTF(" --benchfn <name> - Override the benchmark runner function name.");
PRINTF("");
PRINTF(" --reloc=<option> - Relocation model: none, pic, PIC, pie, PIE.");
PRINTF(" --x86cpu=<option> - Set general level of x64 cpu: baseline, ssse3, sse4, avx1, avx2-v1, avx2-v2 (Skylake/Zen1+), avx512 (Icelake/Zen4+), native.");
PRINTF(" --x86vec=<option> - Set max type of vector use: none, mmx, sse, avx, avx512, default.");
PRINTF(" --riscvfloat=<option> - Set type of RISC-V float support: none, float, double");
PRINTF(" --memory-env=<option> - Set the memory environment: normal, small, tiny, none.");
PRINTF(" --strip-unused=<yes|no> - Strip unused code and globals from the output. (default: yes)");
PRINTF(" --fp-math=<option> - FP math behaviour: strict, relaxed, fast.");
PRINTF(" --win64-simd=<option> - Win64 SIMD ABI: array, full.");
PRINTF("");
PRINTF(" --print-linking - Print linker arguments.");
PRINTF("");
PRINTF(" --benchmarking - Run built-in benchmarks.");
PRINTF(" --testing - Run built-in tests.");
PRINTF("");
PRINTF(" --list-attributes - List all attributes.");
PRINTF(" --list-builtins - List all builtins.");
PRINTF(" --list-keywords - List all keywords.");
PRINTF(" --list-operators - List all operators.");
PRINTF(" --list-precedence - List operator precedence order.");
PRINTF(" --list-project-properties - List all available keys used in project.json files.");
PRINTF(" --list-manifest-properties - List all available keys used in manifest.json files.");
PRINTF(" --list-targets - List all architectures the compiler supports.");
PRINTF(" --list-type-properties - List all type properties.");
PRINTF("");
PRINTF(" --print-output - Print the object files created to stdout.");
PRINTF(" --print-input - Print inputted C3 files to stdout.");
PRINTF("");
PRINTF(" --winsdk <dir> - Set the directory for Windows system library files for cross compilation.");
PRINTF(" --wincrt=<option> - Windows CRT linking: none, static-debug, static, dynamic-debug (default if debug info enabled), dynamic (default).");
PRINTF(" --windef <file> - Use Windows 'def' file for function exports instead of 'dllexport'.");
PRINTF(" --win-vs-dirs <dir>;<dir> - Override Windows VS detection.");
PRINTF("");
PRINTF(" --macossdk <dir> - Set the directory for the MacOS SDK for cross compilation.");
PRINTF(" --macos-min-version <ver> - Set the minimum MacOS version to compile for.");
PRINTF(" --macos-sdk-version <ver> - Set the MacOS SDK compiled for.");
PRINTF("");
PRINTF(" --linux-crt <dir> - Set the directory to use for finding crt1.o and related files.");
PRINTF(" --linux-crtbegin <dir> - Set the directory to use for finding crtbegin.o and related files.");
PRINTF("");
PRINTF(" --vector-conv=<option> - Set vector conversion behaviour: default, old.");
PRINTF(" --sanitize=<option> - Enable sanitizer: address, memory, thread.");
}
if (!full)
{
PRINTF("");
PRINTF("Use -hh to view the full list of options.");
}
}
static void project_usage()
@@ -186,8 +198,8 @@ static void project_usage()
PRINTF("Usage: %s [<options>] project <subcommand> [<args>]", args[0]);
PRINTF("");
PRINTF("Project Subcommands:");
PRINTF(" view view the current projects structure");
PRINTF(" add-target <name> <target_type> add a new target to the project");
PRINTF(" view view the current projects structure");
PRINTF(" add-target <name> <target_type> [sources...] add a new target to the project");
#if FETCH_AVAILABLE
PRINTF(" fetch fetch missing project libraries");
#endif
@@ -210,6 +222,11 @@ static void parse_project_subcommand(BuildOptions *options)
if (at_end() || next_is_opt()) error_exit("Expected a target type like 'executable' or 'static-lib'");
options->project_options.target_type = (TargetType)get_valid_enum_from_string(next_arg(), "type", targets, ELEMENTLEN(targets), "a target type like 'executable' or 'static-lib'");
while (!at_end() && !next_is_opt())
{
vec_add(options->project_options.sources, next_arg());
}
return;
}
if (arg_match("fetch"))
@@ -276,11 +293,6 @@ static void parse_command(BuildOptions *options)
options->command = COMMAND_COMPILE_ONLY;
return;
}
if (arg_match("headers"))
{
options->command = COMMAND_GENERATE_HEADERS;
return;
}
if (arg_match("static-lib"))
{
options->command = COMMAND_STATIC_LIB;
@@ -318,6 +330,7 @@ static void parse_command(BuildOptions *options)
{
options->command = COMMAND_TEST;
options->testing = true;
parse_optional_target(options);
return;
}
if (arg_match("run"))
@@ -407,12 +420,41 @@ static void parse_option(BuildOptions *options)
case '\0':
options->read_stdin = true;
return;
case '?':
if (match_shortopt("?"))
case 'h':
if (match_shortopt("hh"))
{
usage();
usage(true);
exit_compiler(COMPILER_SUCCESS_EXIT);
}
if (match_shortopt("h"))
{
usage(false);
exit_compiler(COMPILER_SUCCESS_EXIT);
}
break;
case 'q':
if (match_shortopt("q"))
{
options->verbosity_level = -1;
return;
}
break;
case 'v':
if (match_shortopt("vvv"))
{
options->verbosity_level = 3;
return;
}
if (match_shortopt("vv"))
{
options->verbosity_level = 2;
return;
}
if (match_shortopt("v"))
{
options->verbosity_level = 1;
return;
}
break;
case 'V':
if (match_shortopt("V"))
@@ -438,8 +480,6 @@ static void parse_option(BuildOptions *options)
return;
}
FAIL_WITH_ERR("Unknown debug argument -%s.", &current_arg[1]);
case 'h':
break;
case 'z':
if (match_shortopt("z"))
{
@@ -604,14 +644,25 @@ static void parse_option(BuildOptions *options)
options->symtab_size = next_highest_power_of_2(symtab);
return;
}
if (match_longopt("quiet"))
{
options->verbosity_level = -1;
return;
}
if (match_longopt("version"))
{
print_version();
exit_compiler(COMPILER_SUCCESS_EXIT);
}
if ((argopt = match_argopt("backend")))
{
options->backend = (CompilerBackend)parse_multi_option(argopt, 3, backends);
return;
}
if (match_longopt("run-once"))
{
options->run_once = true;
if (!options->verbosity_level) options->verbosity_level = -1;
return;
}
if ((argopt = match_argopt("fp-math")))
@@ -731,17 +782,6 @@ static void parse_option(BuildOptions *options)
options->no_headers = true;
return;
}
if (match_longopt("debug-log"))
{
debug_log = true;
debug_stats = true;
return;
}
if (match_longopt("debug-stats"))
{
debug_stats = true;
return;
}
if (match_longopt("print-linking"))
{
options->print_linking = true;
@@ -893,6 +933,10 @@ static void parse_option(BuildOptions *options)
}
if (match_longopt("winsdk"))
{
if (options->win.vs_dirs)
{
error_exit("error: --winsdk cannot be combined with --win-vs-dirs.");
}
if (at_end() || next_is_opt()) error_exit("error: --winsdk needs a directory.");
options->win.sdk = check_dir(next_arg());
return;
@@ -918,6 +962,16 @@ static void parse_option(BuildOptions *options)
options->win.crt_linking = (WinCrtLinking)parse_multi_option(argopt, 5, wincrt_linking);
return;
}
if (match_longopt("win-vs-dirs"))
{
if (options->win.sdk)
{
error_exit("error: --win-vs-dirs cannot be combined with --winsdk.");
}
if (at_end() || next_is_opt()) error_exit("error: --win-vs-dirs needs to followed by the directories.");
options->win.vs_dirs = next_arg();
return;
}
if ((argopt = match_argopt("sanitize")))
{
options->sanitize_mode = (SanitizeMode)parse_multi_option(argopt, 4, sanitize_modes);
@@ -1058,7 +1112,7 @@ static void parse_option(BuildOptions *options)
}
if (match_longopt("help"))
{
usage();
usage(true);
exit_compiler(COMPILER_SUCCESS_EXIT);
}
break;
@@ -1076,7 +1130,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
if (argc < 2)
{
usage();
usage(false);
exit_compiler(COMPILER_SUCCESS_EXIT);
}
@@ -1155,7 +1209,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
parse_command(&build_options);
continue;
}
if (command_accepts_files(build_options.command) || build_options.command == COMMAND_GENERATE_HEADERS)
if (command_accepts_files(build_options.command))
{
append_file(&build_options);
continue;
@@ -1166,6 +1220,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
{
FAIL_WITH_ERR("Missing a compiler command such as 'compile' or 'build'.");
}
debug_log = build_options.verbosity_level > 2;
return build_options;
}
@@ -1202,7 +1257,7 @@ static inline bool at_end()
static inline const char *next_arg()
{
assert(!at_end());
ASSERT0(!at_end());
current_arg = args[++arg_index];
return current_arg;
}

View File

@@ -73,7 +73,6 @@ bool command_accepts_files(CompilerCommand command)
case COMMAND_UNIT_TEST:
return true;
case COMMAND_MISSING:
case COMMAND_GENERATE_HEADERS:
case COMMAND_INIT:
case COMMAND_INIT_LIB:
case COMMAND_BUILD:
@@ -109,7 +108,6 @@ bool command_passes_args(CompilerCommand command)
case COMMAND_COMPILE_TEST:
case COMMAND_UNIT_TEST:
case COMMAND_MISSING:
case COMMAND_GENERATE_HEADERS:
case COMMAND_INIT:
case COMMAND_INIT_LIB:
case COMMAND_BUILD:
@@ -297,6 +295,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
break;
case COMMAND_STATIC_LIB:
target->type = TARGET_TYPE_STATIC_LIB;
target->single_module = true;
break;
default:
target->run_after_compile = false;
@@ -308,7 +307,8 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
case COMMAND_BUILD:
target->output_headers = (target->type == TARGET_TYPE_DYNAMIC_LIB || target->type == TARGET_TYPE_STATIC_LIB) && !options->no_headers;
break;
case COMMAND_GENERATE_HEADERS:
case COMMAND_STATIC_LIB:
case COMMAND_DYNAMIC_LIB:
target->output_headers = true;
break;
default:
@@ -363,8 +363,8 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
if (options->arch_os_target_override != ARCH_OS_TARGET_DEFAULT) target->arch_os_target = options->arch_os_target_override;
if (options->reloc_model != RELOC_DEFAULT) target->reloc_model = options->reloc_model;
if (options->symtab_size) target->symtab_size = options->symtab_size;
if (options->silence_deprecation) target->silence_deprecation = options->silence_deprecation;
target->print_linking = options->print_linking;
if (options->silence_deprecation) target->silence_deprecation = options->silence_deprecation || options->verbosity_level < 0;
target->print_linking = options->print_linking || options->verbosity_level > 1;
for (size_t i = 0; i < options->linker_arg_count; i++)
{
@@ -394,15 +394,18 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
target->emit_llvm = options->emit_llvm;
target->build_threads = options->build_threads;
target->emit_asm = options->emit_asm;
target->print_stats = options->verbosity_level >= 2;
if (options->output_dir) target->output_dir = options->output_dir;
if (options->panicfn) target->panicfn = options->panicfn;
if (options->testfn) target->testfn = options->testfn;
if (options->benchfn) target->benchfn = options->benchfn;
target->benchmarking = options->benchmarking;
target->testing = options->testing;
target->silent = options->verbosity_level < 0;
target->vector_conv = options->vector_conv;
if (options->macos.sysroot) target->macos.sysroot = options->macos.sysroot;
if (options->win.sdk) target->win.sdk = options->win.sdk;
if (options->win.vs_dirs) target->win.vs_dirs = options->win.vs_dirs;
if (options->macos.min_version) target->macos.min_version = options->macos.min_version;
if (options->macos.sdk_version) target->macos.sdk_version = options->macos.sdk_version;
if (options->win.crt_linking != WIN_CRT_DEFAULT) target->win.crt_linking = options->win.crt_linking;
@@ -518,6 +521,7 @@ void init_default_build_target(BuildTarget *target, BuildOptions *options)
*target = default_build_target;
target->source_dirs = options->files;
target->name = options->output_name;
target->output_name = options->output_name;
update_build_target_from_options(target, options);
}

View File

@@ -4,6 +4,7 @@
#define MANIFEST_FILE "manifest.json"
const char *manifest_default_keys[][2] = {
{"sources", "Paths to library sources for targets, such as interface files."},
{"c-sources", "Set the C sources to be compiled."},
{"c-include-dirs", "Set the include directories for C sources."},
{"cc", "Set C compiler (defaults to 'cc')."},
@@ -19,6 +20,8 @@ const char *manifest_default_keys[][2] = {
const int manifest_default_keys_count = ELEMENTLEN(manifest_default_keys);
const char *manifest_target_keys[][2] = {
{"sources", "Additional library sources to be compiled for this target."},
{"sources-override", "Paths to library sources for this target, overriding global settings."},
{"c-sources", "Additional C sources to be compiled for the target."},
{"c-sources-override", "C sources to be compiled, overriding global settings."},
{"c-include-dirs", "C source include directories for the target."},
@@ -86,9 +89,11 @@ static inline void parse_library_target(Library *library, LibraryTarget *target,
target->execs = get_string_array(library->dir, target_name, object, "exec", false);
target->cc = get_string(library->dir, target_name, object, "cc", library->cc);
target->cflags = get_cflags(library->dir, target_name, object, library->cflags);
target->source_dirs = library->source_dirs;
target->csource_dirs = library->csource_dirs;
target->cinclude_dirs = library->cinclude_dirs;
target->win_crt = (WinCrtLinking)get_valid_string_setting(library->dir, target_name, object, "wincrt", wincrt_linking, 0, 3, "'none', 'static' or 'dynamic'.");
get_list_append_strings(library->dir, target_name, object, &target->source_dirs, "sources", "sources-override", "sources-add");
get_list_append_strings(library->dir, target_name, object, &target->csource_dirs, "c-sources", "c-sources-override", "c-sources-add");
get_list_append_strings(library->dir, target_name, object, &target->cinclude_dirs, "c-include-dirs", "c-include-dirs-override", "c-include-dirs-add");
}
@@ -112,6 +117,7 @@ static Library *add_library(JSONObject *object, const char *dir)
library->cc = get_optional_string(dir, NULL, object, "cc");
library->cflags = get_cflags(library->dir, NULL, object, NULL);
library->win_crt = (WinCrtLinking)get_valid_string_setting(library->dir, NULL, object, "wincrt", wincrt_linking, 0, 3, "'none', 'static' or 'dynamic'.");
get_list_append_strings(library->dir, NULL, object, &library->source_dirs, "sources", "sources-override", "sources-add");
get_list_append_strings(library->dir, NULL, object, &library->csource_dirs, "c-sources", "c-sources-override", "c-sources-add");
get_list_append_strings(library->dir, NULL, object, &library->cinclude_dirs, "c-include-dirs", "c-include-dirs-override", "c-include-dirs-add");
parse_library_type(library, &library->targets, json_map_get(object, "targets"));
@@ -291,7 +297,19 @@ void resolve_libraries(BuildTarget *build_target)
{
vec_add(build_target->ccompiling_libraries, target);
}
file_add_wildcard_files(&build_target->sources, library->dir, false, c3_suffix_list, 3);
if (target->source_dirs)
{
const char **files = target_expand_source_names(library->dir, target->source_dirs, c3_suffix_list, &build_target->object_files, 3, true);
FOREACH(const char *, file, files)
{
vec_add(build_target->sources, file);
}
}
else
{
// fallback if sources doesn't exist
file_add_wildcard_files(&build_target->sources, library->dir, false, c3_suffix_list, 3);
}
vec_add(build_target->library_list, library);
const char *libdir = file_append_path(library->dir, arch_os_target[build_target->arch_os_target]);
if (file_is_dir(libdir)) vec_add(build_target->linker_libdirs, libdir);
@@ -312,4 +330,4 @@ void resolve_libraries(BuildTarget *build_target)
puts(execute_cmd(exec, false, NULL));
}
}
}
}

View File

@@ -44,6 +44,7 @@ const char *project_default_keys[][2] = {
{"single-module", "Compile all modules together, enables more inlining."},
{"soft-float", "Output soft-float functions."},
{"sources", "Paths to project sources for all targets."},
{"test-sources", "Paths to project test sources for all targets."},
{"strip-unused", "Strip unused code and globals from the output. (default: true)"},
{"symtab", "Sets the preferred symtab size."},
{"target", "Compile for a particular architecture + OS target."},
@@ -100,6 +101,7 @@ const char* project_target_keys[][2] = {
{"macos-sdk-version", "Set the MacOS SDK compiled for." },
{"macossdk", "Set the directory for the MacOS SDK for cross compilation."},
{"memory-env", "Set the memory environment: normal, small, tiny, none."},
{"name", "Set the name to be different from the target name."},
{"no-entry", "Do not generate (or require) a main function."},
{"opt", "Optimization setting: O0, O1, O2, O3, O4, O5, Os, Oz."},
{"optlevel", "Code optimization level: none, less, more, max."},
@@ -116,6 +118,8 @@ const char* project_target_keys[][2] = {
{"soft-float", "Output soft-float functions."},
{"sources", "Additional paths to project sources for the target."},
{"sources-override", "Paths to project sources for this target, overriding global settings."},
{"test-sources", "Additional paths to project test sources for the target."},
{"test-sources-override", "Paths to project test sources for this target, overriding global settings."},
{"strip-unused", "Strip unused code and globals from the output. (default: true)"},
{"symtab", "Sets the preferred symtab size."},
{"target", "Compile for a particular architecture + OS target."},
@@ -244,6 +248,9 @@ static void load_into_build_target(const char *filename, JSONObject *json, const
target->feature.panic_level = (PanicLevel)get_valid_bool(filename, target_name, json, "panic-msg",
target->feature.panic_level);
// Overridden name
target->output_name = get_optional_string(filename, target_name, json, "name");
// Single module
target->single_module = (SingleModule) get_valid_bool(filename, target_name, json, "single-module", target->single_module);
@@ -331,6 +338,9 @@ static void load_into_build_target(const char *filename, JSONObject *json, const
RiscvFloatCapability riscv_float = GET_SETTING(RiscvFloatCapability, "riscvfloat", riscv_capability, "`none`, `float` or `double`.");
if (riscv_float != RISCVFLOAT_DEFAULT) target->feature.riscv_float_capability = riscv_float;
// winsdk
target->win.vs_dirs = get_string(filename, target_name, json, "win-vs-dirs", target->win.vs_dirs);
// winsdk
target->win.sdk = get_string(filename, target_name, json, "winsdk", target->win.sdk);
@@ -421,12 +431,37 @@ static void load_into_build_target(const char *filename, JSONObject *json, const
target->feature.pass_win64_simd_as_arrays);
}
static void duplicate_prop(const char ***prop_ref)
{
if (!*prop_ref) return;
const char **copy = NULL;
FOREACH(const char *, str, *prop_ref)
{
vec_add(copy, str);
}
*prop_ref = copy;
}
static void project_add_target(const char *filename, Project *project, BuildTarget *default_target, JSONObject *json,
const char *name, const char *type, TargetType target_type)
{
assert(json->type == J_OBJECT);
ASSERT0(json->type == J_OBJECT);
BuildTarget *target = CALLOCS(BuildTarget);
*target = *default_target;
duplicate_prop(&target->args);
duplicate_prop(&target->csource_dirs);
duplicate_prop(&target->csources);
duplicate_prop(&target->cinclude_dirs);
duplicate_prop(&target->exec);
duplicate_prop(&target->feature_list);
duplicate_prop(&target->sources);
duplicate_prop(&target->source_dirs);
duplicate_prop(&target->test_source_dirs);
duplicate_prop(&target->libdirs);
duplicate_prop(&target->libs);
duplicate_prop(&target->linker_libdirs);
duplicate_prop(&target->linker_libs);
duplicate_prop(&target->link_args);
vec_add(project->targets, target);
target->name = name;
target->type = target_type;
@@ -444,7 +479,7 @@ static void project_add_target(const char *filename, Project *project, BuildTarg
static void project_add_targets(const char *filename, Project *project, JSONObject *project_data)
{
assert(project_data->type == J_OBJECT);
ASSERT0(project_data->type == J_OBJECT);
BuildTarget default_target = default_build_target;
load_into_build_target(filename, project_data, NULL, &default_target);

View File

@@ -2,7 +2,6 @@
#include "../utils/json.h"
const char** get_project_dependency_directories();
const char** get_project_dependencies();
static void print_vec(const char *header, const char **vec, bool opt);

View File

@@ -20,6 +20,8 @@ const char* JSON_EXE =
" \"version\": \"0.1.0\",\n"
" // Sources compiled for all targets.\n"
" \"sources\": [ \"src/**\" ],\n"
" // Test sources compiled for all targets.\n"
" \"test-sources\": [ \"test/**\" ],\n"
" // C sources if the project also compiles C sources\n"
" // relative to the project file.\n"
" // \"c-sources\": [ \"csource/**\" ],\n"
@@ -63,6 +65,8 @@ const char* JSON_STATIC =
" \"version\": \"0.1.0\",\n"
" // Sources compiled for all targets.\n"
" \"sources\": [ \"src/**\" ],\n"
" // Test sources compiled for all targets.\n"
" \"test-sources\": [ \"test/**\" ],\n"
" // C sources if the project also compiles C sources\n"
" // relative to the project file.\n"
" // \"c-sources\": [ \"csource/**\" ],\n"
@@ -104,6 +108,8 @@ const char* JSON_DYNAMIC =
" \"version\": \"0.1.0\",\n"
" // Sources compiled for all targets.\n"
" \"sources\": [ \"src/**\" ],\n"
" // Test sources compiled for all targets.\n"
" \"test-sources\": [ \"test/**\" ],\n"
" // C sources if the project also compiles C sources\n"
" // relative to the project file.\n"
" // \"c-sources\": [ \"csource/**\" ],\n"
@@ -132,6 +138,7 @@ const char* JSON_DYNAMIC =
const char *MANIFEST_TEMPLATE =
"{\n"
" \"provides\" : \"%s\",\n"
" // \"sources\" : [ \"src/**\" ],\n"
" \"targets\" : {\n"
"%s"
" }\n"
@@ -213,9 +220,11 @@ void create_library(BuildOptions *build_options)
}
chdir_or_fail(build_options, dir);
create_file_or_fail(build_options, "LICENSE", NULL);
create_file_or_fail(build_options, "README.md", LIB_README, build_options->project_name);
mkdir_or_fail(build_options, "scripts");
scratch_buffer_clear();
scratch_buffer_printf("%s.c3i", build_options->project_name);
const char *interface_file = scratch_buffer_copy();

View File

@@ -3,12 +3,24 @@
#define PRINTFN(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__) // NOLINT
#define PRINTF(string, ...) fprintf(stdout, string, ##__VA_ARGS__) // NOLINT
static JSONObject *read_project(const char **file_used)
static JSONObject *read_project(const char **file_used, bool assume_empty_if_not_exists)
{
size_t size;
const char *project_filename = file_exists(PROJECT_JSON5) ? PROJECT_JSON5 : PROJECT_JSON;
*file_used = project_filename;
char *read = file_read_all(project_filename, &size);
char *read;
if (assume_empty_if_not_exists)
{
// If project file does not exist assume the project simply being empty instead of
// failing. This is useful for such commands as `project add-target`. It enables
// them to update otherwise non-existing project files reducing the friction.
read = "{}";
if (file_exists(project_filename)) read = file_read_all(project_filename, &size);
}
else
{
read = file_read_all(project_filename, &size);
}
JsonParser parser;
json_init_string(&parser, read);
JSONObject *json = json_parse(&parser);
@@ -26,7 +38,7 @@ static JSONObject *read_project(const char **file_used)
const char** get_project_dependency_directories()
{
const char *filename;
JSONObject *json = read_project(&filename);
JSONObject *json = read_project(&filename, false);
const char *target = NULL;
const char **deps_dirs = NULL;
@@ -57,7 +69,7 @@ const char** get_project_dependencies()
const char *filename;
const char** dependencies = NULL;
JSONObject *project_json = read_project(&filename);
JSONObject *project_json = read_project(&filename, false);
JSONObject *dependencies_json = json_map_get(project_json, "dependencies");
FOREACH(JSONObject *, element, dependencies_json->elements)
@@ -209,6 +221,7 @@ static void view_target(const char *filename, const char *name, JSONObject *targ
print_opt_str("\tName", name);
TARGET_VIEW_MANDATORY_STRING("Type", "type");
TARGET_VIEW_STRING("Target language target", "langrev");
TARGET_VIEW_STRING("Target output name", "name");
TARGET_VIEW_STRING_ARRAY("Warnings used", "warnings");
TARGET_VIEW_STRING_ARRAY("Additional c3l library search paths", "dependency-search-paths");
TARGET_VIEW_STRING_ARRAY("c3l library search paths (override)", "dependency-search-paths-override");
@@ -289,7 +302,7 @@ void fetch_project(BuildOptions* options)
const char **libdirs = get_project_dependency_directories();
const char **deps = get_project_dependencies();
const char *filename;
JSONObject *project_json = read_project(&filename);
JSONObject *project_json = read_project(&filename, false);
JSONObject *targets_json = json_map_get(project_json, "targets");
@@ -358,7 +371,7 @@ void add_libraries_to_project_file(const char** libs, const char* target_name) {
//TODO! Target name option not implemented
const char *filename;
JSONObject *project_json = read_project(&filename);
JSONObject *project_json = read_project(&filename, false);
// TODO! check if target is specified and exists (NULL at the moment)
JSONObject *libraries_json = json_map_get(project_json, "dependencies");
@@ -395,9 +408,15 @@ void add_libraries_to_project_file(const char** libs, const char* target_name) {
void add_target_project(BuildOptions *build_options)
{
const char *filename;
JSONObject *project_json = read_project(&filename);
JSONObject *project_json = read_project(&filename, true);
JSONObject *targets_json = json_map_get(project_json, "targets");
if (targets_json == NULL)
{
targets_json = json_new_object(J_OBJECT);
json_map_set(project_json, "targets", targets_json);
}
if (json_map_get(targets_json, build_options->project_options.target_name) != NULL)
{
error_exit("Target with name '%s' already exists", build_options->project_options.target_name);
@@ -407,6 +426,12 @@ void add_target_project(BuildOptions *build_options)
JSONObject *new_target = json_new_map();
json_map_set(new_target, "type", target_type_obj);
JSONObject *target_sources = json_new_object(J_ARRAY);
FOREACH(const char *, source, build_options->project_options.sources)
{
vec_add(target_sources->elements, json_new_string(source));
}
json_map_set(new_target, "sources", target_sources);
json_map_set(targets_json, build_options->project_options.target_name, new_target);
@@ -418,7 +443,7 @@ void add_target_project(BuildOptions *build_options)
void view_project(BuildOptions *build_options)
{
const char *filename;
JSONObject *project_json = read_project(&filename);
JSONObject *project_json = read_project(&filename, false);
/* General information */
VIEW_MANDATORY_STRING_ARRAY("Authors", "authors");

View File

@@ -61,10 +61,10 @@ bool abi_arg_is_indirect(ABIArgInfo *info)
ABIArgInfo *abi_arg_new_indirect_realigned(AlignSize alignment, Type *by_val_type)
{
assert(alignment > 0);
ASSERT0(alignment > 0);
ABIArgInfo *info = abi_arg_new(ABI_ARG_INDIRECT);
info->indirect.alignment = alignment;
assert(info->indirect.alignment);
ASSERT0(info->indirect.alignment);
info->attributes.realign = true;
info->indirect.type = by_val_type;
info->attributes.by_val = true;
@@ -77,7 +77,7 @@ ABIArgInfo *abi_arg_new_indirect_by_val(Type *by_val_type)
info->indirect.alignment = type_abi_alignment(by_val_type);
info->indirect.type = by_val_type;
info->attributes.by_val = true;
assert(info->indirect.alignment);
ASSERT0(info->indirect.alignment);
return info;
}
@@ -85,7 +85,7 @@ ABIArgInfo *abi_arg_new_indirect_not_by_val(Type *type)
{
ABIArgInfo *info = abi_arg_new(ABI_ARG_INDIRECT);
info->indirect.alignment = type_abi_alignment(type);
assert(info->indirect.alignment);
ASSERT0(info->indirect.alignment);
info->indirect.type = type;
info->attributes.by_val = false;
return info;
@@ -175,7 +175,7 @@ ABIArgInfo *abi_arg_new_direct_coerce_int(void)
ABIArgInfo *abi_arg_new_direct_coerce_type(Type *type)
{
assert(type);
ASSERT0(type);
ABIArgInfo *info = abi_arg_new(ABI_ARG_DIRECT_COERCE);
info->direct_coerce_type = type->canonical;
return info;
@@ -191,7 +191,7 @@ ABIArgInfo *abi_arg_new_direct_struct_expand_i32(uint8_t elements)
void c_abi_func_create(FunctionPrototype *proto)
{
assert(!proto->is_resolved);
ASSERT0(!proto->is_resolved);
proto->is_resolved = true;
switch (compiler.platform.abi)
{

View File

@@ -57,7 +57,7 @@ ABIArgInfo *aarch64_coerce_illegal_vector(Type *type)
UNREACHABLE
}*/
}
assert(type->type_kind == TYPE_VECTOR);
ASSERT0(type->type_kind == TYPE_VECTOR);
TypeSize size = type_size(type);
// CLANG: Android promotes char[<2>] to ushort, not uint
@@ -107,7 +107,7 @@ ABIArgInfo *aarch64_classify_argument_type(Type *type)
unsigned members = 0;
if (type_is_homogenous_aggregate(type, &base, &members))
{
assert(members < 128);
ASSERT0(members < 128);
if (members > 1)
{
return abi_arg_new_direct_coerce_type(type_get_array(base, members));
@@ -134,7 +134,7 @@ ABIArgInfo *aarch64_classify_argument_type(Type *type)
size = aligned_offset(size, alignment);
// We use a pair of i64 for 16-byte aggregate with 8-byte alignment.
// For aggregates with 16-byte alignment, we use i128.
assert(alignment == 8 || alignment == 16);
ASSERT0(alignment == 8 || alignment == 16);
if (alignment == 16) return abi_arg_new_direct_coerce_type(type_u128);
ArraySize m = size / alignment;
@@ -196,7 +196,7 @@ ABIArgInfo *aarch64_classify_return_type(Type *type, bool variadic)
if (size <= 8 && !compiler.platform.big_endian)
{
return abi_arg_new_direct_coerce_type(type_int_unsigned_by_bitsize(size * 8));
return abi_arg_new_direct_coerce_int();
}
unsigned alignment = type_abi_alignment(type);

View File

@@ -7,13 +7,13 @@
static ABIArgInfo *riscv_coerce_and_expand_fpcc_struct(AbiType field1, unsigned field1_offset, AbiType field2, unsigned field2_offset)
{
assert(abi_type_is_type(field1));
ASSERT0(abi_type_is_type(field1));
if (!abi_type_is_valid(field2))
{
return abi_arg_new_direct_coerce_type(field1.type);
}
assert(abi_type_is_type(field2));
ASSERT0(abi_type_is_type(field2));
Type *type2 = field2.type;
ByteSize abi_type_size = type_size(type2);
// Not on even offset, use packed semantics.
@@ -132,10 +132,10 @@ static bool riscv_detect_fpcc_struct(Type *type, AbiType *field1_ref, unsigned *
static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsigned *gprs, unsigned *fprs)
{
assert(type == type->canonical);
ASSERT0(type == type->canonical);
unsigned xlen = compiler.platform.riscv.xlen;
assert(is_power_of_two(xlen));
ASSERT0(is_power_of_two(xlen));
ByteSize size = type_size(type);

View File

@@ -122,7 +122,7 @@ ABIArgInfo *x64_indirect_result(Type *type, unsigned free_int_regs)
*/
ABIArgInfo *x64_classify_reg_call_struct_type_check(Type *type, Registers *needed_registers)
{
assert(x64_type_is_structure(type));
ASSERT0(x64_type_is_structure(type));
// These are all passed in two registers.
if (type->type_kind == TYPE_SLICE || type->type_kind == TYPE_ANY)
@@ -132,7 +132,7 @@ ABIArgInfo *x64_classify_reg_call_struct_type_check(Type *type, Registers *neede
}
// Struct, err type handled =>
assert(type->type_kind == TYPE_STRUCT);
ASSERT0(type->type_kind == TYPE_STRUCT);
// Variable array structs are always passed by pointer.
if (type->decl->has_variable_array) return x64_indirect_return_result(type);
@@ -175,7 +175,7 @@ static X64Class x64_merge(X64Class accum, X64Class field)
// 6. SSE
// Accum should never be memory (we should have returned) or
assert(accum != CLASS_MEMORY);
ASSERT0(accum != CLASS_MEMORY);
if (accum == field) return accum;
// Swap
@@ -305,7 +305,7 @@ void x64_classify_array(Type *type, ByteSize offset_base, X64Class *current, X64
if (*lo_class == CLASS_MEMORY || *hi_class == CLASS_MEMORY) break;
}
x64_classify_post_merge(size, lo_class, hi_class);
assert(*hi_class != CLASS_SSEUP || *lo_class == CLASS_SSE);
ASSERT0(*hi_class != CLASS_SSEUP || *lo_class == CLASS_SSE);
}
void x64_classify_vector(Type *type, ByteSize offset_base, X64Class *current, X64Class *lo_class, X64Class *hi_class,
@@ -365,7 +365,7 @@ static Decl *x64_get_member_at_offset(Decl *decl, unsigned offset)
if (member->offset > (ArrayIndex)offset) break;
last_match = member;
}
assert(last_match);
ASSERT0(last_match);
return last_match;
}
@@ -616,7 +616,7 @@ AbiType x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_typ
break;
}
ByteSize size = type_size(source_type);
assert(size != source_offset);
ASSERT0(size != source_offset);
if (size - source_offset > 8) return abi_type_get(type_ulong);
return abi_type_get_int_bits((size - source_offset) * 8);
}
@@ -649,7 +649,7 @@ static AbiType x64_get_byte_vector_type(Type *type)
unsigned size = type_size(type);
assert(size == 16 || size == 32 || size == 64);
ASSERT0(size == 16 || size == 32 || size == 64);
// Return a vector type based on the size.
return abi_type_get(type_get_vector(type_double, size / 8));
@@ -659,7 +659,7 @@ static ABIArgInfo *x64_get_argument_pair_return(AbiType low_type, AbiType high_t
{
TypeSize low_size = abi_type_size(low_type);
unsigned hi_start = aligned_offset(low_size, abi_type_abi_alignment(high_type));
assert(hi_start == 8 && "Expected aligned with C-style structs.");
ASSERT0(hi_start == 8 && "Expected aligned with C-style structs.");
return abi_arg_new_direct_pair(low_type, high_type);
}
@@ -673,8 +673,8 @@ ABIArgInfo *x64_classify_return(Type *return_type)
x64_classify(return_type, 0, &lo_class, &hi_class, NAMED);
// Invariants
assert(hi_class != CLASS_MEMORY || lo_class == CLASS_MEMORY);
assert(hi_class != CLASS_SSEUP || lo_class == CLASS_SSE);
ASSERT0(hi_class != CLASS_MEMORY || lo_class == CLASS_MEMORY);
ASSERT0(hi_class != CLASS_SSEUP || lo_class == CLASS_SSE);
AbiType result_type = ABI_TYPE_EMPTY;
switch (lo_class)
@@ -685,7 +685,7 @@ ABIArgInfo *x64_classify_return(Type *return_type)
return abi_arg_ignore();
}
// If low part is padding, keep type null
assert(hi_class == CLASS_SSE || hi_class == CLASS_INTEGER);
ASSERT0(hi_class == CLASS_SSE || hi_class == CLASS_INTEGER);
break;
case CLASS_SSEUP:
UNREACHABLE
@@ -717,11 +717,11 @@ ABIArgInfo *x64_classify_return(Type *return_type)
// Previously handled.
break;
case CLASS_INTEGER:
assert(lo_class != CLASS_NO_CLASS);
ASSERT0(lo_class != CLASS_NO_CLASS);
high_part = x64_get_int_type_at_offset(return_type, 8, return_type, 8);
break;
case CLASS_SSE:
assert(lo_class != CLASS_NO_CLASS);
ASSERT0(lo_class != CLASS_NO_CLASS);
high_part = abi_type_get(x64_get_sse_type_at_offset(return_type, 8, return_type, 8));
break;
case CLASS_SSEUP:
@@ -730,7 +730,7 @@ ABIArgInfo *x64_classify_return(Type *return_type)
// vector register.
//
// SSEUP should always be preceded by SSE, just widen.
assert(lo_class == CLASS_SSE && "Unexpected SSEUp classification.");
ASSERT0(lo_class == CLASS_SSE && "Unexpected SSEUp classification.");
result_type = x64_get_byte_vector_type(return_type);
break;
}
@@ -748,7 +748,7 @@ ABIArgInfo *x64_classify_return(Type *return_type)
}
return abi_arg_new_direct_coerce_type(result_type.type->canonical);
}
assert(result_type.int_bits_plus_1 - 1 == type_size(return_type) * 8);
ASSERT0(result_type.int_bits_plus_1 - 1 == type_size(return_type) * 8);
return abi_arg_new_direct_coerce_int();
}
@@ -764,14 +764,14 @@ ABIArgInfo *x64_classify_return(Type *return_type)
*/
static ABIArgInfo *x64_classify_argument_type(Type *type, unsigned free_int_regs, Registers *needed_registers, NamedArgument is_named)
{
assert(type == type_lowering(type));
ASSERT0(type == type_lowering(type));
X64Class hi_class;
X64Class lo_class;
x64_classify(type, 0, &lo_class, &hi_class, is_named);
// Invariants
assert(hi_class != CLASS_MEMORY || lo_class == CLASS_MEMORY);
assert(hi_class != CLASS_SSEUP || lo_class == CLASS_SSE);
ASSERT0(hi_class != CLASS_MEMORY || lo_class == CLASS_MEMORY);
ASSERT0(hi_class != CLASS_SSEUP || lo_class == CLASS_SSE);
AbiType result_type;
*needed_registers = (Registers) { 0, 0 };
@@ -781,7 +781,7 @@ static ABIArgInfo *x64_classify_argument_type(Type *type, unsigned free_int_regs
{
case CLASS_NO_CLASS:
// Only C++ would leave 8 bytes of padding, so we can ignore that case.
assert(hi_class == CLASS_NO_CLASS);
ASSERT0(hi_class == CLASS_NO_CLASS);
return abi_arg_ignore();
case CLASS_SSEUP:
UNREACHABLE
@@ -792,7 +792,7 @@ static ABIArgInfo *x64_classify_argument_type(Type *type, unsigned free_int_regs
result_type = x64_get_int_type_at_offset(type, 0, type, 0);
if (hi_class == CLASS_NO_CLASS && type_is_promotable_int_bool(type))
{
assert(abi_type_is_type(result_type));
ASSERT0(abi_type_is_type(result_type));
return abi_arg_new_direct_coerce_int_ext(result_type.type);
}
break;
@@ -814,15 +814,15 @@ static ABIArgInfo *x64_classify_argument_type(Type *type, unsigned free_int_regs
needed_registers->int_registers++;
high_part = x64_get_int_type_at_offset(type, 8, type, 8);
// Return directly into high part.
assert(lo_class != CLASS_NO_CLASS && "empty first 8 bytes not allowed, this is C++ stuff.");
ASSERT0(lo_class != CLASS_NO_CLASS && "empty first 8 bytes not allowed, this is C++ stuff.");
break;
case CLASS_SSE:
needed_registers->sse_registers++;
high_part = abi_type_get(x64_get_sse_type_at_offset(type, 8, type, 8));
assert(lo_class != CLASS_NO_CLASS && "empty first 8 bytes not allowed, this is C++ stuff");
ASSERT0(lo_class != CLASS_NO_CLASS && "empty first 8 bytes not allowed, this is C++ stuff");
break;
case CLASS_SSEUP:
assert(lo_class == CLASS_SSE && "Unexpected SSEUp classification.");
ASSERT0(lo_class == CLASS_SSE && "Unexpected SSEUp classification.");
result_type = x64_get_byte_vector_type(type);
break;
}
@@ -843,7 +843,7 @@ static ABIArgInfo *x64_classify_argument_type(Type *type, unsigned free_int_regs
}
return abi_arg_new_direct_coerce_type(result);
}
assert(result_type.int_bits_plus_1 - 1 == type_size(type) * 8);
ASSERT0(result_type.int_bits_plus_1 - 1 == type_size(type) * 8);
return abi_arg_new_direct_coerce_int();
}

View File

@@ -92,7 +92,7 @@ static ABIArgInfo *create_indirect_return_x86(Type *type, Regs *regs)
static bool x86_should_return_type_in_reg(Type *type)
{
assert(type->canonical == type);
ASSERT0(type->canonical == type);
ByteSize size = type_size(type);
if (size > 8) return false;
@@ -226,7 +226,7 @@ static inline bool x86_is_mmxtype(Type *type)
static inline bool x86_can_expand_indirect_aggregate_arg(Type *type)
{
assert(type_is_abi_aggregate(type));
ASSERT0(type_is_abi_aggregate(type));
// Test whether an argument type which is to be passed indirectly (on the
// stack) would have the equivalent layout if it was expanded into separate
@@ -373,7 +373,7 @@ static inline ABIArgInfo *x86_classify_vector(Regs *regs, Type *type)
static inline ABIArgInfo *x86_classify_aggregate(CallABI call, Regs *regs, Type *type)
{
// Only called for aggregates.
assert(type_is_abi_aggregate(type));
ASSERT0(type_is_abi_aggregate(type));
if (type_is_union_or_strukt(type) && type->decl->has_variable_array)
{
@@ -390,7 +390,7 @@ static inline ABIArgInfo *x86_classify_aggregate(CallABI call, Regs *regs, Type
// Here we coerce the aggregate into a struct { i32, i32, ... }
// but we do not generate this struct immediately here.
unsigned size_in_regs = (size + 3) / 4;
assert(size_in_regs < 8);
ASSERT0(size_in_regs < 8);
ABIArgInfo *info;
if (size_in_regs > 1)
{

View File

@@ -175,7 +175,7 @@ static inline void reg_instr_clob(PlatformTarget *target, const char *name, Clob
unsigned param_count = 0;
while (args && args[0] != 0)
{
assert(param_count <= MAX_ASM_INSTRUCTION_PARAMS);
ASSERT0(param_count <= MAX_ASM_INSTRUCTION_PARAMS);
instr->param[param_count++] = decode_arg_type(&args);
}
instr->param_count = param_count;
@@ -188,7 +188,7 @@ static inline void reg_instr(PlatformTarget *target, const char *name, const cha
int param_count = 0;
while (args && args[0] != 0)
{
assert(param_count <= MAX_ASM_INSTRUCTION_PARAMS);
ASSERT0(param_count <= MAX_ASM_INSTRUCTION_PARAMS);
instr->param[param_count++] = decode_arg_type(&args);
}
instr->param_count = param_count;

View File

@@ -174,7 +174,7 @@ Decl *decl_new_generated_var(Type *type, VarDeclKind kind, SourceSpan span)
decl->var.kind = kind;
decl->type = type;
decl->alignment = type ? type_alloca_alignment(type) : 0;
assert(!type || !type_is_user_defined(type) || type->decl->resolve_status == RESOLVE_DONE);
ASSERT0(!type || !type_is_user_defined(type) || type->decl->resolve_status == RESOLVE_DONE);
decl->var.type_info = type_info_id_new_base(type, span);
decl->resolve_status = RESOLVE_DONE;
return decl;
@@ -428,7 +428,7 @@ AlignSize decl_find_member_offset(Decl *decl, Decl *member)
default:
return NO_MATCH;
}
assert(members);
ASSERT0(members);
unsigned list = vec_size(members);
for (unsigned i = 0; i < list; i++)
{

View File

@@ -41,7 +41,7 @@ UNUSED static char digit_to_char(uint8_t digit, bool upper)
char *i128_to_string(Int128 op, uint64_t base, bool is_signed, bool use_prefix)
{
assert(base >= 2 && base <= 16);
ASSERT0(base >= 2 && base <= 16);
static char digits[16] = "0123456789ABCDEF";
char buffer[130];
char *loc = buffer;
@@ -322,7 +322,7 @@ Int128 i128_from_float_unsigned(Real d)
UNUSED bool i128_get_bit(const Int128 *op, int bit)
{
assert(bit < 128 && bit >= 0);
ASSERT0(bit < 128 && bit >= 0);
if (bit > 63)
{
return (op->high >> (bit - 64)) & 1;
@@ -761,7 +761,7 @@ unsigned int_bits_needed(Int op)
Int int_add(Int op1, Int op2)
{
assert(op1.type == op2.type);
ASSERT0(op1.type == op2.type);
return (Int){ i128_extend(i128_add(op1.i, op2.i), op1.type), op1.type };
}
@@ -772,7 +772,7 @@ Int int_add64(Int op1, uint64_t op2)
Int int_sub(Int op1, Int op2)
{
assert(op1.type == op2.type);
ASSERT0(op1.type == op2.type);
return (Int){ i128_extend(i128_sub(op1.i, op2.i), op1.type), op1.type };
}
@@ -783,7 +783,7 @@ Int int_sub64(Int op1, uint64_t op2)
Int int_mul(Int op1, Int op2)
{
assert(op1.type == op2.type);
ASSERT0(op1.type == op2.type);
return (Int){ i128_extend(i128_mult(op1.i, op2.i), op1.type), op1.type };
}
@@ -821,7 +821,7 @@ Int int_conv(Int op, TypeKind to_type)
Int int_div(Int op1, Int op2)
{
assert(op1.type == op2.type);
ASSERT0(op1.type == op2.type);
Int128 res;
if (type_kind_is_signed(op1.type))
{
@@ -836,7 +836,7 @@ Int int_div(Int op1, Int op2)
Int int_rem(Int op1, Int op2)
{
assert(op1.type == op2.type);
ASSERT0(op1.type == op2.type);
Int128 res;
if (type_kind_is_signed(op1.type))
{
@@ -851,19 +851,19 @@ Int int_rem(Int op1, Int op2)
Int int_and(Int op1, Int op2)
{
assert(op1.type == op2.type);
ASSERT0(op1.type == op2.type);
return (Int){ i128_and(op1.i, op2.i), op1.type };
}
Int int_or(Int op1, Int op2)
{
assert(op1.type == op2.type);
ASSERT0(op1.type == op2.type);
return (Int){ i128_or(op1.i, op2.i), op1.type };
}
Int int_xor(Int op1, Int op2)
{
assert(op1.type == op2.type);
ASSERT0(op1.type == op2.type);
return (Int){ i128_xor(op1.i, op2.i), op1.type };
}

1078
src/compiler/c_codegen.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
#include "codegen_internal.h"

View File

@@ -7,6 +7,13 @@
#define CAST_AND_EXTEND(x, n) \
(((int64_t)((x) << (64 - (n)))) >> (64 - (n)))
INLINE bool codegen_asm_label(Ast *ast)
{
if (ast->ast_kind != AST_ASM_LABEL) return false;
scratch_buffer_printf("${:private}%s.${:uid}:\n", ast->asm_label);
return true;
}
static inline void codegen_create_x86att_arg(AsmInlineBlock *block, unsigned input_offset, Expr *expr)
{
ExprAsmArg *arg = &expr->expr_asm_arg;
@@ -177,6 +184,7 @@ static inline char *codegen_create_x86_att_asm(AsmInlineBlock *block)
{
Ast *ast = astptr(next);
next = ast->next;
if (codegen_asm_label(ast)) continue;
scratch_buffer_append(ast->asm_stmt.instruction);
Expr** args = ast->asm_stmt.args;
unsigned arg_count = vec_size(args);
@@ -201,6 +209,7 @@ static inline char *codegen_create_aarch64_asm(AsmInlineBlock *block)
{
Ast *ast = astptr(next);
next = ast->next;
if (codegen_asm_label(ast)) continue;
scratch_buffer_append(ast->asm_stmt.instruction);
Expr** args = ast->asm_stmt.args;
unsigned arg_count = vec_size(args);
@@ -225,6 +234,7 @@ static inline char *codegen_create_riscv_asm(AsmInlineBlock *block)
{
Ast *ast = astptr(next);
next = ast->next;
if (codegen_asm_label(ast)) continue;
scratch_buffer_append(ast->asm_stmt.instruction);
Expr** args = ast->asm_stmt.args;
unsigned arg_count = vec_size(args);
@@ -242,7 +252,7 @@ static inline char *codegen_create_riscv_asm(AsmInlineBlock *block)
const char *codegen_create_asm(Ast *ast)
{
assert(ast->ast_kind == AST_ASM_BLOCK_STMT);
ASSERT0(ast->ast_kind == AST_ASM_BLOCK_STMT);
scratch_buffer_clear();
AsmInlineBlock *block = ast->asm_block_stmt.block;
if (compiler.platform.arch == ARCH_TYPE_X86_64 || compiler.platform.arch == ARCH_TYPE_X86)

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