Compare commits

..

50 Commits

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

* Change matrix identity macros to constants

---------

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

* Fixed typo: missing "is".
2023-04-02 17:25:16 +02:00
456 changed files with 11809 additions and 13057 deletions

View File

@@ -7,7 +7,7 @@ on:
branches: [ master ]
env:
LLVM_RELEASE_VERSION: 15
LLVM_RELEASE_VERSION: 16
jobs:
@@ -34,6 +34,7 @@ jobs:
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\hello_world_many.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\time.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
@@ -101,6 +102,7 @@ jobs:
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
@@ -127,7 +129,7 @@ jobs:
build-msys2-clang:
runs-on: windows-latest
if: ${{ false }}
#if: ${{ false }}
strategy:
# Don't abort runners if a single one fails
fail-fast: false
@@ -155,6 +157,7 @@ jobs:
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
@@ -181,7 +184,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [15, 16, 17]
llvm_version: [16, 17]
steps:
- uses: actions/checkout@v3
@@ -218,7 +221,28 @@ jobs:
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
../build/c3c compile examples/factorial_macro.c3
../build/c3c compile examples/fasta.c3
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/hash.c3
../build/c3c compile examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/nbodies.c3
../build/c3c compile examples/spectralnorm.c3
../build/c3c compile examples/swap.c3
../build/c3c compile examples/contextfree/boolerr.c3
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
@@ -244,7 +268,7 @@ jobs:
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
if: matrix.llvm_version == 16
run: |
mkdir linux
cp -r lib linux
@@ -253,12 +277,117 @@ jobs:
tar czf c3-linux-${{matrix.build_type}}.tar.gz linux
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
if: matrix.llvm_version == 16
uses: actions/upload-artifact@v3
with:
name: c3-linux-${{matrix.build_type}}
path: c3-linux-${{matrix.build_type}}.tar.gz
build-linux-ubuntu20:
runs-on: ubuntu-20.04
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [16]
steps:
- uses: actions/checkout@v3
- name: Install common deps
run: |
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
- name: Install Clang ${{matrix.llvm_version}}
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
if [[ "${{matrix.llvm_version}}" < 17 ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
else
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
fi
sudo apt-get update
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
- name: CMake
run: |
cmake -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
../build/c3c compile examples/factorial_macro.c3
../build/c3c compile examples/fasta.c3
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/hash.c3
../build/c3c compile examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/nbodies.c3
../build/c3c compile examples/spectralnorm.c3
../build/c3c compile examples/swap.c3
../build/c3c compile examples/contextfree/boolerr.c3
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit -g1 --safe
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --forcelinker
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == 16
run: |
mkdir linux
cp -r lib linux
cp msvc_build_libraries.py linux
cp build/c3c linux
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz linux
- name: upload artifacts
if: matrix.llvm_version == 16
uses: actions/upload-artifact@v3
with:
name: c3-ubuntu-20-${{matrix.build_type}}
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
build-mac:
runs-on: macos-latest
@@ -267,12 +396,11 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [15]
llvm_version: [15, 16]
steps:
- uses: actions/checkout@v3
- name: Download LLVM
run: |
brew update
brew install llvm@${{ matrix.llvm_version }} ninja curl
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
@@ -291,6 +419,7 @@ jobs:
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
@@ -426,6 +555,27 @@ jobs:
asset_name: c3-linux-debug.tar.gz
asset_content_type: application/gzip
- name: upload ubuntu 20
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz
asset_name: c3-ubuntu-20.tar.gz
asset_content_type: application/gzip
- name: upload ubuntu 20 debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz
asset_name: c3-ubuntu-20-debug.tar.gz
asset_content_type: application/gzip
- name: upload macos
uses: actions/upload-release-asset@v1
env:

View File

@@ -83,15 +83,15 @@ endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "15")
set(C3_LLVM_VERSION "16")
endif()
FetchContent_Declare(
LLVM_Windows
URL https://github.com/c3lang/win-llvm/releases/download/llvm_15_0_6/llvm-15.0.6-windows-amd64-msvc17-libcmt.7z
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm_15_0_6/llvm-15.0.6-windows-amd64-msvc17-libcmt-dbg.7z
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
@@ -171,12 +171,7 @@ if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_DRIVER}
${LLD_READER_WRITER}
${LLD_LOONG}
${LLD_MACHO}
${LLD_YAML}
${LLD_CORE}
)
else()
set(lld_libs
@@ -185,11 +180,7 @@ else()
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_DRIVER}
${LLD_READER_WRITER}
${LLD_MACHO}
${LLD_YAML}
${LLD_CORE}
)
endif()

View File

@@ -54,6 +54,7 @@ fn void Stack.push(Stack* this, Type element)
if (this.capacity == this.size)
{
this.capacity *= 2;
if (this.capacity < 16) this.capacity = 16;
this.elems = mem::realloc(this.elems, Type.sizeof * this.capacity);
}
this.elems[this.size++] = element;
@@ -78,9 +79,9 @@ import stack;
// Define our new types, the first will implicitly create
// a complete copy of the entire Stack module with "Type" set to "int"
define IntStack = Stack<int>;
def IntStack = Stack<int>;
// The second creates another copy with "Type" set to "double"
define DoubleStack = Stack<double>;
def DoubleStack = Stack<double>;
// If we had added "define IntStack2 = Stack<int>"
// no additional copy would have been made (since we already
@@ -91,7 +92,7 @@ define DoubleStack = Stack<double>;
// here is an example of importing libc's printf:
extern fn int printf(char* format, ...);
fn void test()
fn void main()
{
IntStack stack;
// Note that C3 uses zero initialization by default
@@ -111,12 +112,12 @@ fn void test()
dstack.push(2.3);
dstack.push(3.141);
dstack.push(1.1235);
// Prints pop: 1.1235
// Prints pop: 1.123500
printf("pop: %f\n", dstack.pop());
}
```
### In what ways do C3 differ from C?
### In what ways does C3 differ from C?
- No mandatory header files
- New semantic macro system
@@ -138,7 +139,7 @@ fn void test()
The current version of the compiler is alpha release 0.4.
Design work on C3 complete aside from fleshing out details, such as
Design work on C3 is complete aside from fleshing out details, such as
inline asm. As the standard library work progresses, changes and improvements
to the language will happen continuously.
Follow the issues [here](https://github.com/c3lang/c3c/issues).
@@ -311,4 +312,4 @@ Editor plugins can be found at https://github.com/c3lang/editor-plugins.
2. Make sure that the test functions have the `@test` attribute.
3. Run tests and see that they pass. (Recommended settings: `c3c compile-test --safe -g1 -O0 test/unit`.
- in this example `test/unit/` is the relative path to the test directory, so adjust as required)
4. Make a pull request for the new tests.
4. Make a pull request for the new tests.

View File

@@ -8,32 +8,35 @@
module std::collections::enumset<Enum>;
const IS_CHAR_ARRAY = Enum.elements > 128;
$switch
$case (Enum.elements > 128):
typedef EnumSetType @private = char[(Enum.elements + 7) / 8];
const IS_CHAR_ARRAY = true;
def EnumSetType @private = char[(Enum.elements + 7) / 8];
$case (Enum.elements > 64):
typedef EnumSetType @private = uint128;
const IS_CHAR_ARRAY = false;
def EnumSetType @private = uint128;
$case (Enum.elements > 32 || $$C_INT_SIZE > 32):
typedef EnumSetType @private = ulong;
const IS_CHAR_ARRAY = false;
def EnumSetType @private = ulong;
$case (Enum.elements > 16 || $$C_INT_SIZE > 16):
typedef EnumSetType @private = uint;
const IS_CHAR_ARRAY = false;
def EnumSetType @private = uint;
$case (Enum.elements > 8 || $$C_INT_SIZE > 8):
typedef EnumSetType @private = ushort;
const IS_CHAR_ARRAY = false;
def EnumSetType @private = ushort;
$default:
typedef EnumSetType @private = char;
const IS_CHAR_ARRAY = false;
def EnumSetType @private = char;
$endswitch
typedef EnumSet = distinct EnumSetType;
def EnumSet = distinct EnumSetType;
fn void EnumSet.add(EnumSet* this, Enum v)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
(*this)[v / 8] |= (char)(1u << (v % 8));
$else
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
@@ -42,7 +45,7 @@ $endif
fn void EnumSet.clear(EnumSet* this)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
*this = {};
$else
*this = 0;
@@ -51,7 +54,7 @@ $endif
fn bool EnumSet.remove(EnumSet* this, Enum v)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
if (!this.has(v) @inline) return false;
(*this)[v / 8] &= (char)~(1 << (v % 8));
return true;
@@ -65,7 +68,7 @@ $endif
fn bool EnumSet.has(EnumSet* this, Enum v)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
return (bool)(((*this)[v / 8] << (v % 8)) & 0x01);
$else
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
@@ -74,7 +77,7 @@ $endif
fn void EnumSet.add_all(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*this)[i] |= c;
$else
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
@@ -83,7 +86,7 @@ $endif
fn void EnumSet.retain_all(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*this)[i] &= c;
$else
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
@@ -92,7 +95,7 @@ $endif
fn void EnumSet.remove_all(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*this)[i] &= ~c;
$else
*this = (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
@@ -101,7 +104,7 @@ $endif
fn EnumSet EnumSet.and_of(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
copy.retain_all(s);
return copy;
@@ -112,7 +115,7 @@ $endif
fn EnumSet EnumSet.or_of(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
copy.add_all(s);
return copy;
@@ -124,7 +127,7 @@ $endif
fn EnumSet EnumSet.diff_of(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
copy.remove_all(s);
return copy;
@@ -135,7 +138,7 @@ $endif
fn EnumSet EnumSet.xor_of(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY)
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
foreach (i, c : s) copy[i] ^= c;
return copy;

View File

@@ -87,13 +87,13 @@ fn Type! peek_last(LinkedList* list) => list.last() @inline;
fn Type! LinkedList.first(LinkedList *list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
return list._first.value;
}
fn Type! LinkedList.last(LinkedList* list)
{
if (!list._last) return IteratorResult.NO_MORE_ELEMENT!;
if (!list._last) return IteratorResult.NO_MORE_ELEMENT?;
return list._last.value;
}
@@ -240,7 +240,7 @@ fn bool LinkedList.remove_last_value(LinkedList* list, Type t)
**/
fn Type! LinkedList.pop(LinkedList* list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
defer list.unlink_first();
return list._first.value;
}
@@ -250,7 +250,7 @@ fn Type! LinkedList.pop(LinkedList* list)
**/
fn void! LinkedList.remove_last(LinkedList* list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
list.unlink_last();
}
@@ -259,7 +259,7 @@ fn void! LinkedList.remove_last(LinkedList* list)
**/
fn void! LinkedList.remove_first(LinkedList* list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
list.unlink_first();
}

View File

@@ -4,6 +4,8 @@
module std::collections::list<Type>;
import std::math;
def ElementPredicate = fn bool(Type *type);
struct List
{
usz size;
@@ -36,6 +38,26 @@ fn void List.tinit(List* list, usz initial_capacity = 16)
list.init(initial_capacity, mem::temp()) @inline;
}
fn String List.to_string(List* list, Allocator* using = mem::heap()) @dynamic
{
if (!list.size) return "[]".copy(using);
if (list.size == 1) return string::printf("[%s]", list.entries[0], .using = using);
@stack_mem(512 + 128; Allocator* mem)
{
DString str;
str.init(512, mem);
str.append("[");
foreach (i, element : list.entries[:list.size])
{
if (i != 0) str.append(", ");
str.printf("%s", element);
}
str.printf("]");
return str.copy_str(using);
};
}
fn void List.push(List* list, Type element) @inline
{
list.append(element);
@@ -79,6 +101,56 @@ fn void List.remove_at(List* list, usz index)
list.size--;
}
fn void List.add_all(List* list, List* other_list)
{
if (!other_list.size) return;
list.reserve(other_list.size);
foreach (&value : other_list)
{
list.entries[list.size++] = *value;
}
}
fn Type[] List.to_array(List* list, Allocator* using = mem::heap())
{
if (!list.size) return Type[] {};
Type[] result = malloc(Type, list.size, .using = using);
result[..] = list.entries[:list.size];
return result;
}
/**
* Reverse the elements in a list.
*
* @param [&inout] list "The list to reverse"
**/
fn void List.reverse(List* list)
{
if (list.size < 2) return;
usz half = list.size / 2U;
usz end = list.size - 1;
for (usz i = 0; i < half; i++)
{
@swap(list.entries[i], list.entries[end - i]);
}
}
fn Type[] List.array_view(List* list)
{
return list.entries[:list.size];
}
fn void List.add_array(List* list, Type[] array)
{
if (!array.len) return;
list.reserve(array.len);
foreach (&value : array)
{
list.entries[list.size++] = *value;
}
}
fn void List.push_front(List* list, Type type) @inline
{
list.insert_at(0, type);
@@ -152,6 +224,47 @@ fn void List.swap(List* list, usz i, usz j)
@swap(list.entries[i], list.entries[j]);
}
/**
* @param [&inout] list "The list to remove elements from"
* @param filter "The function to determine if it should be removed or not"
* @return "the number of deleted elements"
**/
fn usz List.remove_if(List* list, ElementPredicate filter)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (filter(&list.entries[i - 1])) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
/**
* @param [&inout] list "The list to remove elements from"
* @param selection "The function to determine if it should be kept or not"
* @return "the number of deleted elements"
**/
fn usz List.retain_if(List* list, ElementPredicate selection)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (!selection(&list.entries[i - 1])) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
/**
* Reserve at least min_capacity
**/
@@ -175,11 +288,121 @@ fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
return &list.entries[index];
}
fn void List.ensure_capacity(List* list) @inline @private
fn void List.ensure_capacity(List* list, usz added = 1) @inline @private
{
if (list.capacity == list.size)
{
list.reserve(list.capacity ? 2 * list.capacity : 16);
}
usz new_size = list.size + added;
if (list.capacity > new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = list.capacity ? 2U * list.capacity : 16U;
while (new_size >= new_capacity) new_capacity *= 2U;
list.reserve(new_capacity);
}
// Functions for equatable types
$if types::is_equatable_type(Type):
fn usz! List.index_of(List* list, Type type)
{
foreach (i, v : list)
{
if (v == type) return i;
}
return SearchResult.MISSING?;
}
fn usz! List.rindex_of(List* list, Type type)
{
foreach_r (i, v : list)
{
if (v == type) return i;
}
return SearchResult.MISSING?;
}
fn bool List.equals(List* list, List other_list)
{
if (list.size != other_list.size) return false;
foreach (i, v : list)
{
if (v != other_list.entries[i]) return false;
}
return true;
}
/**
* Check for presence of a value in a list.
*
* @param [&in] list "the list to find elements in"
* @param value "The value to search for"
* @return "True if the value is found, false otherwise"
**/
fn bool List.contains(List* list, Type value)
{
foreach (i, v : list)
{
if (v == value) return true;
}
return false;
}
/**
* @param [&inout] list "The list to remove elements from"
* @param value "The value to remove"
* @return "the number of deleted elements."
**/
fn usz List.remove(List* list, Type value)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (list.entries[i - 1] != value) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
fn void List.remove_all(List* list, List* other_list)
{
if (!other_list.size) return;
foreach (v : other_list) list.remove(v);
}
$endif
$if Type.kindof == POINTER:
/**
* @param [&in] list
* @return "The number non-null values in the list"
**/
fn usz List.compact_count(List* list)
{
usz vals = 0;
foreach (v : list) if (v) vals++;
return vals;
}
fn usz List.compact(List* list)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (list.entries[i - 1]) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
$endif

View File

@@ -45,6 +45,17 @@ fn void HashMap.tinit(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, fl
map.init(capacity, load_factor, mem::temp());
}
/**
* Has this hash map been initialized yet?
*
* @param [&in] map "The hash map we are testing"
* @return "Returns true if it has been initialized, false otherwise"
**/
fn bool HashMap.is_initialized(HashMap* map)
{
return map.allocator != null;
}
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* using = mem::heap())
{
map.init(other_map.table.len, other_map.load_factor, using);
@@ -63,24 +74,24 @@ fn bool HashMap.is_empty(HashMap* map) @inline
fn Value*! HashMap.get_ref(HashMap* map, Key key)
{
if (!map.count) return SearchResult.MISSING!;
if (!map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return SearchResult.MISSING!;
return SearchResult.MISSING?;
}
fn Entry*! HashMap.get_entry(HashMap* map, Key key)
{
if (!map.count) return SearchResult.MISSING!;
if (!map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return SearchResult.MISSING!;
return SearchResult.MISSING?;
}
/**
@@ -112,7 +123,7 @@ fn Value! HashMap.get(HashMap* map, Key key) @operator([])
fn bool HashMap.has_key(HashMap* map, Key key)
{
return try? map.get_ref(key);
return @ok(map.get_ref(key));
}
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
@@ -138,7 +149,7 @@ fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
fn void! HashMap.remove(HashMap* map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING!;
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
}
fn void HashMap.clear(HashMap* map)
@@ -205,7 +216,7 @@ fn Value[] HashMap.value_list(HashMap* map, Allocator* using = mem::heap())
return list;
}
$if (types::is_equatable(Value))
$if types::is_equatable(Value):
fn bool HashMap.has_value(HashMap* map, Value v)
{
if (!map.count) return false;

View File

@@ -26,56 +26,52 @@ struct Object
}
}
static initialize
{
io::formatter_register_type(Object);
}
fn void! Object.to_format(Object* o, Formatter* formatter)
fn void! Object.to_format(Object* o, Formatter* formatter) @dynamic
{
switch (o.type)
{
case void:
formatter.printf("{}")?;
formatter.printf("{}")!;
case void*:
formatter.printf("null")?;
formatter.printf("null")!;
case String:
formatter.printf(`"%s"`, o.s)?;
formatter.printf(`"%s"`, o.s)!;
case bool:
formatter.printf(o.b ? "true" : "false")?;
formatter.printf(o.b ? "true" : "false")!;
case ObjectInternalList:
formatter.printf("[")?;
formatter.printf("[")!;
foreach (i, ol : o.array)
{
formatter.printf(i == 0 ? " " : ", ")?;
ol.to_format(formatter)?;
formatter.printf(i == 0 ? " " : ", ")!;
ol.to_format(formatter)!;
}
formatter.printf(" ]")?;
formatter.printf(" ]")!;
case ObjectInternalMap:
formatter.printf("{")?;
formatter.printf("{")!;
@pool()
{
foreach (i, key : o.map.key_tlist())
{
formatter.printf(i == 0 ? " " : ", ")?;
formatter.printf(`"%s": `, key)?;
o.map.get(key).to_format(formatter)?;
formatter.printf(i == 0 ? " " : ", ")!;
formatter.printf(`"%s": `, key)!;
o.map.get(key).to_format(formatter)!;
}
};
formatter.printf(" }")?;
formatter.printf(" }")!;
default:
switch (o.type.kindof)
{
case SIGNED_INT:
formatter.printf("%d", o.i)?;
formatter.printf("%d", o.i)!;
case UNSIGNED_INT:
formatter.printf("%d", (uint128)o.i)?;
formatter.printf("%d", (uint128)o.i)!;
case FLOAT:
formatter.printf("%d", o.f)?;
formatter.printf("%d", o.f)!;
case ENUM:
formatter.printf("%d", o.i)?;
formatter.printf("%d", o.i)!;
default:
formatter.printf("<>")?;
formatter.printf("<>")!;
}
}
}
@@ -230,7 +226,7 @@ $switch
$case $checks(String s = value):
return new_string(value);
$default:
$assert(false, "Unsupported object type.");
$error "Unsupported object type.";
$endswitch
}
@@ -266,7 +262,7 @@ macro Object* Object.append(Object* o, value)
/**
* @require o.is_keyable()
**/
fn Object*! Object.get(Object* o, String key) => o.is_empty() ? SearchResult.MISSING! : o.map.get(key);
fn Object*! Object.get(Object* o, String key) => o.is_empty() ? SearchResult.MISSING? : o.map.get(key);
fn bool Object.has_key(Object* o, String key) => o.is_map() && o.map.has_key(key);
@@ -315,13 +311,13 @@ macro get_integer_value(Object* value, $Type)
}
if (value.is_string())
{
$if ($Type.kindof == TypeKind.SIGNED_INT)
return ($Type)str::to_int128(value.s);
$if $Type.kindof == TypeKind.SIGNED_INT:
return ($Type)value.s.to_int128();
$else
return ($Type)str::to_uint128(value.s);
return ($Type)value.s.to_uint128();
$endif
}
if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER!;
if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER?;
return ($Type)value.i;
}
@@ -370,7 +366,7 @@ fn uint128! Object.get_uint128_at(Object* o, usz index) => o.get_integer_at(uint
**/
fn String! Object.get_string(Object* o, String key)
{
Object* value = o.get(key)?;
Object* value = o.get(key)!;
assert(value.is_string());
return value.s;
}
@@ -390,7 +386,7 @@ fn String Object.get_string_at(Object* o, usz index)
**/
macro String! Object.get_enum(Object* o, $EnumType, String key)
{
Object value = o.get(key)?;
Object value = o.get(key)!;
assert($EnumType.typeid == value.type);
return ($EnumType)value.i;
}
@@ -410,7 +406,7 @@ macro String Object.get_enum_at(Object* o, $EnumType, usz index)
**/
fn bool! Object.get_bool(Object* o, String key)
{
Object* value = o.get(key)?;
Object* value = o.get(key)!;
assert(value.is_bool());
return value.b;
}
@@ -431,7 +427,7 @@ fn bool Object.get_bool_at(Object* o, usz index)
**/
fn double! Object.get_float(Object* o, String key)
{
Object* value = o.get(key)?;
Object* value = o.get(key)!;
switch (value.type.kindof)
{
case SIGNED_INT:
@@ -472,7 +468,7 @@ fn Object* Object.get_or_create_obj(Object* o, String key)
return container;
}
typedef ObjectInternalMap @private = HashMap<String, Object*>;
typedef ObjectInternalList @private = List<Object*>;
typedef ObjectInternalMapEntry @private = Entry<String, Object*>;
def ObjectInternalMap @private = HashMap<String, Object*>;
def ObjectInternalList @private = List<Object*>;
def ObjectInternalMapEntry @private = Entry<String, Object*>;

View File

@@ -23,7 +23,7 @@
module std::collections::priorityqueue<Type>;
import std::collections::list;
typedef Heap = List<Type>;
def Heap = List<Type>;
struct PriorityQueue
{
@@ -55,7 +55,7 @@ fn Type! PriorityQueue.pop(PriorityQueue* pq)
{
usz i = 0;
usz len = pq.heap.len() @inline;
if (!len) return IteratorResult.NO_MORE_ELEMENT!;
if (!len) return IteratorResult.NO_MORE_ELEMENT?;
usz newCount = len - 1;
pq.heap.swap(0, newCount);
while ((2 * i + 1) < newCount)
@@ -84,7 +84,7 @@ fn Type! PriorityQueue.pop(PriorityQueue* pq)
*/
fn Type! PriorityQueue.peek(PriorityQueue* pq)
{
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT!;
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT?;
return pq.heap.get(0);
}

View File

@@ -54,7 +54,7 @@ fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
alignment = alignment_for_allocation(alignment);
void* mem = arena._alloc(size, alignment, offset)?;
void* mem = arena._alloc(size, alignment, offset)!;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
case ALIGNED_REALLOC:
@@ -62,7 +62,7 @@ fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
alignment = alignment_for_allocation(alignment);
return arena._realloc(old_pointer, size, alignment, offset)?;
return arena._realloc(old_pointer, size, alignment, offset)!;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
@@ -96,14 +96,14 @@ fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz
fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usz size, usz alignment, usz offset) @private
{
usz total_len = this.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
void* start_mem = this.data.ptr;
void* unaligned_pointer_to_offset = start_mem + this.used + ArenaAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - this.data.ptr) + size - offset;
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY!;
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
this.used = end;
void *mem = aligned_pointer_to_offset - offset;
void* mem = aligned_pointer_to_offset - offset;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size;
return mem;
@@ -123,7 +123,7 @@ fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz s
{
assert(old_pointer >= this.data.ptr, "Pointer originates from a different allocator.");
usz total_len = this.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
usz old_size = header.size;
// Do last allocation and alignment match?
@@ -136,14 +136,14 @@ fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz s
else
{
usz new_used = this.used + size - old_size;
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY!;
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY?;
this.used = new_used;
}
header.size = size;
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = this._alloc(size, alignment, offset)?;
void* mem = this._alloc(size, alignment, offset)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -17,19 +17,19 @@ struct DynamicArenaAllocator
* @require page_size >= 128
* @require this != null
**/
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usz page_size, Allocator* backing_allocator = mem::heap())
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usz page_size, Allocator* using = mem::heap())
{
this.function = &dynamic_arena_allocator_function;
this.page = null;
this.unused_page = null;
this.page_size = page_size;
this.backing_allocator = backing_allocator;
this.backing_allocator = using;
}
/**
* @require this != null
**/
fn void DynamicArenaAllocator.destroy(DynamicArenaAllocator* this)
fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this)
{
DynamicArenaPage* page = this.page;
while (page)
@@ -67,7 +67,7 @@ struct DynamicArenaChunk @local
* @require ptr && this
* @require this.page `tried to free pointer on invalid allocator`
*/
fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* ptr) @private
fn void DynamicArenaAllocator.free_ptr(DynamicArenaAllocator* this, void* ptr) @private
{
DynamicArenaPage* current_page = this.page;
if (ptr == current_page.last_ptr)
@@ -106,7 +106,7 @@ fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, void* old_
current_page.used += add_size;
return old_pointer;
}
void* new_mem = this._alloc(size, alignment, offset)?;
void* new_mem = this._alloc(size, alignment, offset)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
@@ -137,12 +137,12 @@ fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this, usz size
usz page_size = max(this.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
// Grab the page without alignment (we do it ourselves)
void* mem = this.backing_allocator.alloc(page_size)?;
void* mem = this.backing_allocator.alloc(page_size)!;
DynamicArenaPage*! page = malloc(DynamicArenaPage, .using = this.backing_allocator);
if (catch err = page)
{
free(mem, .using = this.backing_allocator);
return err!;
return err?;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
@@ -213,7 +213,7 @@ fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignm
case ALIGNED_CALLOC:
assert(!old_pointer, "Unexpected no old pointer for calloc.");
if (!size) return null;
void* mem = allocator._alloc(size, alignment, offset)?;
void* mem = allocator._alloc(size, alignment, offset)!;
mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
case ALLOC:
@@ -226,16 +226,16 @@ fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignm
if (!size)
{
if (!old_pointer) return null;
allocator.free(old_pointer);
allocator.free_ptr(old_pointer);
return null;
}
if (!old_pointer) return allocator._alloc(size, alignment, offset);
void* mem = allocator._realloc(old_pointer, size, alignment, offset)?;
void* mem = allocator._realloc(old_pointer, size, alignment, offset)!;
return mem;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
allocator.free(old_pointer);
allocator.free_ptr(old_pointer);
return null;
case MARK:
unreachable("Tried to mark a dynamic arena");

View File

@@ -4,7 +4,7 @@
module std::core::mem::allocator;
typedef MemoryAllocFn = fn char[]!(usz);
def MemoryAllocFn = fn char[]!(usz);
struct SimpleHeapAllocator
{
@@ -51,9 +51,9 @@ fn void*! simple_heap_allocator_function(Allocator* this, usz size, usz alignmen
if (!old_pointer) nextcase CALLOC;
return heap._realloc(old_pointer, size);
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION!;
return AllocationFailure.UNSUPPORTED_OPERATION?;
case ALIGNED_FREE:
@aligned_free(heap._free, old_pointer)?;
@aligned_free(heap._free, old_pointer)!;
return null;
case FREE:
heap._free(old_pointer);
@@ -71,7 +71,7 @@ fn void*! SimpleHeapAllocator._realloc(SimpleHeapAllocator* this, void* old_poin
// Find the block header.
Header* block = (Header*)old_pointer - 1;
if (block.size >= bytes) return old_pointer;
void* new = this._alloc(bytes)?;
void* new = this._alloc(bytes)!;
usz max_to_copy = math::min(block.size, bytes);
mem::copy(new, old_pointer, max_to_copy);
this._free(old_pointer);
@@ -80,7 +80,7 @@ fn void*! SimpleHeapAllocator._realloc(SimpleHeapAllocator* this, void* old_poin
fn void*! SimpleHeapAllocator._calloc(SimpleHeapAllocator* this, usz bytes) @local
{
void* data = this._alloc(bytes)?;
void* data = this._alloc(bytes)!;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
@@ -90,7 +90,7 @@ fn void*! SimpleHeapAllocator._alloc(SimpleHeapAllocator* this, usz bytes) @loca
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
if (!this.free_list)
{
this.add_block(aligned_bytes)?;
this.add_block(aligned_bytes)!;
}
Header* current = this.free_list;
@@ -130,14 +130,14 @@ fn void*! SimpleHeapAllocator._alloc(SimpleHeapAllocator* this, usz bytes) @loca
current = current.next;
}
}
this.add_block(aligned_bytes)?;
this.add_block(aligned_bytes)!;
return this.alloc(aligned_bytes);
}
fn void! SimpleHeapAllocator.add_block(SimpleHeapAllocator* this, usz aligned_bytes) @local
{
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
char[] result = this.alloc_fn(aligned_bytes + Header.sizeof)?;
char[] result = this.alloc_fn(aligned_bytes + Header.sizeof)!;
Header* new_block = (Header*)result.ptr;
new_block.size = result.len - Header.sizeof;
new_block.next = null;

View File

@@ -18,7 +18,7 @@ fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offse
case ALIGNED_ALLOC:
case ALIGNED_REALLOC:
case ALIGNED_CALLOC:
return AllocationFailure.OUT_OF_MEMORY!;
return AllocationFailure.OUT_OF_MEMORY?;
default:
return null;
}
@@ -37,8 +37,8 @@ struct AlignedBlock
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
$if ($checks(#alloc_fn(bytes)?))
void* data = #alloc_fn(header + bytes)?;
$if $checks(#alloc_fn(bytes)!):
void* data = #alloc_fn(header + bytes)!;
$else
void* data = #alloc_fn(header + bytes);
$endif
@@ -56,8 +56,8 @@ macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
macro void*! @aligned_calloc(#calloc_fn, usz bytes, usz alignment, usz offset)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
$if ($checks(#calloc_fn(bytes)?))
void* data = #calloc_fn(header + bytes)?;
$if $checks(#calloc_fn(bytes)!):
void* data = #calloc_fn(header + bytes)!;
$else
void* data = #calloc_fn(header + bytes);
$endif
@@ -76,10 +76,10 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)?;
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)!;
mem::copy(new_data, old_pointer, desc.len > bytes ? desc.len : bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
$if ($checks(#free_fn(data_start)?))
#free_fn(data_start)?;
$if $checks(#free_fn(data_start)!):
#free_fn(data_start)!;
$else
#free_fn(data_start);
$endif
@@ -89,8 +89,8 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
macro void! @aligned_free(#free_fn, void* old_pointer)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
$if ($checks(#free_fn(desc.start)?))
#free_fn(desc.start)?;
$if $checks(#free_fn(desc.start)!):
#free_fn(desc.start)!;
$else
#free_fn(desc.start);
$endif
@@ -121,7 +121,7 @@ fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz off
if (!old_pointer) nextcase CALLOC;
data = libc::realloc(old_pointer, bytes);
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION!;
return AllocationFailure.UNSUPPORTED_OPERATION?;
case ALIGNED_FREE:
@aligned_free(libc::free, old_pointer)!!;
return null;
@@ -131,6 +131,6 @@ fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz off
default:
unreachable();
}
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
return data;
}

View File

@@ -111,16 +111,16 @@ fn void*! on_stack_allocator_function(Allocator* data, usz size, usz alignment,
on_stack_allocator_remove_chunk(allocator, old_pointer);
if (kind == AllocationKind.ALIGNED_FREE)
{
allocator.backing_allocator.free_aligned(old_pointer)?;
allocator.backing_allocator.free_aligned(old_pointer)!;
}
else
{
allocator.backing_allocator.free(old_pointer)?;
allocator.backing_allocator.free(old_pointer)!;
}
return null;
case MARK:
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION!;
return AllocationFailure.UNSUPPORTED_OPERATION?;
}
unreachable();
}
@@ -175,14 +175,14 @@ fn void*! on_stack_allocator_realloc(OnStackAllocator* a, void* old_pointer, usz
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
if (aligned)
{
return chunk.data = a.backing_allocator.realloc_aligned(old_pointer, size, alignment, offset)?;
return chunk.data = a.backing_allocator.realloc_aligned(old_pointer, size, alignment, offset)!;
}
return chunk.data = a.backing_allocator.realloc(old_pointer, size)?;
return chunk.data = a.backing_allocator.realloc(old_pointer, size)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = on_stack_allocator_alloc(a, size, alignment, offset, true, aligned)?;
void* mem = on_stack_allocator_alloc(a, size, alignment, offset, true, aligned)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
@@ -209,7 +209,7 @@ fn void*! on_stack_allocator_alloc(OnStackAllocator* a, usz size, usz alignment,
if (end > total_len)
{
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc(OnStackAllocatorExtraChunk.sizeof)?;
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc(OnStackAllocatorExtraChunk.sizeof)!;
defer catch backing_allocator.free(chunk)!!;
defer try a.chunk = chunk;
*chunk = { .prev = a.chunk, .is_aligned = aligned };
@@ -217,13 +217,13 @@ fn void*! on_stack_allocator_alloc(OnStackAllocator* a, usz size, usz alignment,
switch
{
case !aligned && !clear:
data = backing_allocator.alloc(size)?;
data = backing_allocator.alloc(size)!;
case aligned && !clear:
data = backing_allocator.alloc_aligned(size, alignment, offset)?;
data = backing_allocator.alloc_aligned(size, alignment, offset)!;
case !aligned && clear:
data = backing_allocator.calloc(size)?;
data = backing_allocator.calloc(size)!;
case aligned && clear:
data = backing_allocator.calloc_aligned(size, alignment, offset)?;
data = backing_allocator.calloc_aligned(size, alignment, offset)!;
}
return chunk.data = data;
}

View File

@@ -37,12 +37,12 @@ macro bool TempAllocatorPage.is_aligned(TempAllocatorPage* page) => page.size &
/**
* @require size >= 16
**/
fn TempAllocator*! new_temp(usz size, Allocator* backing_allocator)
fn TempAllocator*! new_temp(usz size, Allocator* using)
{
TempAllocator* allocator = malloc_checked(TempAllocator, .using = backing_allocator, .end_padding = size)?;
TempAllocator* allocator = malloc_checked(TempAllocator, .using = using, .end_padding = size)!;
allocator.last_page = null;
allocator.function = &temp_allocator_function;
allocator.backing_allocator = backing_allocator;
allocator.backing_allocator = using;
allocator.used = 0;
allocator.capacity = size;
return allocator;
@@ -75,12 +75,12 @@ fn void*! temp_allocator_function(Allocator* data, usz size, usz alignment, usz
case FREE:
case ALIGNED_FREE:
if (!old_pointer) return null;
arena._free(old_pointer)?;
arena._free(old_pointer)!;
return null;
case MARK:
return (void*)(uptr)arena.used;
case RESET:
arena._reset(size)?;
arena._reset(size)!;
return null;
}
unreachable();
@@ -103,7 +103,7 @@ fn void! TempAllocator._reset(TempAllocator* this, usz mark) @local
{
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
this._free_page(to_free)?;
this._free_page(to_free)!;
}
this.last_page = last_page;
this.used = mark;
@@ -131,15 +131,15 @@ fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocatorPage* pa
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = this._alloc(size, alignment, offset, false)?;
void* data = this._alloc(size, alignment, offset, false)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
if (page.is_aligned())
{
this.backing_allocator.free_aligned(real_pointer)?;
this.backing_allocator.free_aligned(real_pointer)!;
}
else
{
this.backing_allocator.free(real_pointer)?;
this.backing_allocator.free(real_pointer)!;
}
return data;
}
@@ -156,7 +156,7 @@ fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz size, u
}
// TODO optimize last allocation
TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)?;
TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
@@ -200,11 +200,11 @@ fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz
usz total_alloc_size = TempAllocatorPage.sizeof + size;
if (clear)
{
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)?;
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
}
else
{
page = this.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)?;
page = this.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
}
page.start = page;
page.size = size | PAGE_IS_ALIGNED;
@@ -214,7 +214,7 @@ fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz
// Here we might need to pad
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
usz total_alloc_size = padded_header_size + size;
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))?;
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))!;
// Find the page.
page = alloc + padded_header_size - TempAllocatorPage.sizeof;

View File

@@ -5,7 +5,7 @@
module std::core::mem::allocator;
import std::collections::map;
typedef PtrMap = HashMap<uptr, usz>;
def PtrMap = HashMap<uptr, usz>;
// A simple tracking allocator.
// It tracks allocations using a hash map but
@@ -24,10 +24,10 @@ struct TrackingAllocator
*
* @require this != null
**/
fn void TrackingAllocator.init(TrackingAllocator* this, Allocator* allocator)
fn void TrackingAllocator.init(TrackingAllocator* this, Allocator* using)
{
*this = { .inner_allocator = allocator, .allocator.function = &tracking_allocator_fn };
this.map.init(.using = allocator);
*this = { .inner_allocator = using, .allocator.function = &tracking_allocator_fn };
this.map.init(.using = using);
}
fn void TrackingAllocator.free(TrackingAllocator* this)
@@ -43,7 +43,7 @@ fn void TrackingAllocator.free(TrackingAllocator* this)
fn void*! tracking_allocator_fn(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
TrackingAllocator* this = (TrackingAllocator*)data;
void* result = this.inner_allocator.function(this.inner_allocator, size, alignment, offset, old_pointer, kind)?;
void* result = this.inner_allocator.function(this.inner_allocator, size, alignment, offset, old_pointer, kind)!;
switch (kind)
{
case CALLOC:

View File

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

View File

@@ -1,5 +1,12 @@
// Copyright (c) 2023 Christoffer Lerno and contributors. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::bitorder;
// This module contains types of different endianness.
// *BE types represent big-endian types
// *LE types represent little-endian types.
bitstruct ShortBE : short @bigendian
{
short val : 0..15;

View File

@@ -5,17 +5,26 @@ module std::core::builtin;
import libc;
import std::hash;
/**
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
**/
fault IteratorResult
{
NO_MORE_ELEMENT
}
/**
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
**/
fault SearchResult
{
MISSING
}
fault VarCastResult
/**
* Use `CastResult` when an attempt at conversion fails.
**/
fault CastResult
{
TYPE_MISMATCH
}
@@ -33,6 +42,9 @@ macro void @scope(&variable; @body) @builtin
@body();
}
/**
* Swap two variables
**/
macro void @swap(&a, &b) @builtin
{
var temp = a;
@@ -41,15 +53,17 @@ macro void @swap(&a, &b) @builtin
}
/**
* Convert a variant type to a type, returning an failure if there is a type mismatch.
* Convert an `any` type to a type, returning an failure if there is a type mismatch.
*
* @param v `the variant to convert to the given type.`
* @param v `the any to convert to the given type.`
* @param $Type `the type to convert to`
* @return `The variant.ptr converted to its type.`
* @return `The any.ptr converted to its type.`
* @ensure @typeis(return, $Type*)
* @return! CastResult.TYPE_MISMATCH
**/
macro varcast(variant v, $Type) @builtin
macro anycast(any v, $Type) @builtin
{
if (v.type != $Type.typeid) return VarCastResult.TYPE_MISMATCH!;
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
return ($Type*)v.ptr;
}
@@ -64,22 +78,24 @@ struct CallstackElement
fn void default_panic(String message, String file, String function, uint line)
{
CallstackElement* stack = $$stacktrace();
$if ($defined(libc::stderr) && $defined(libc::fprintf))
$if $defined(io::stderr) && $defined(File.printf):
if (stack) stack = stack.prev;
if (stack)
{
libc::fprintf(libc::stderr(), "\nERROR: '%.*s'\n", (int)message.len, message.ptr);
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
(void)io::stderr().printn("'");
}
else
{
libc::fprintf(libc::stderr(), "\nERROR: '%.*s', function %.*s (%.*s:%d)\n",
(int)message.len, message.ptr, (int)function.len, function.ptr, (int)file.len, file.ptr, line);
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
(void)io::stderr().printfn("', in function %s (%s:%d)", function, file, line);
}
while (stack)
{
libc::fprintf(libc::stderr(), " at function %.*s (%.*s:%u)\n", (int)stack.function.len, stack.function.ptr,
(int)stack.file.len, stack.file.ptr, stack.line);
(void)io::stderr().printfn(" in function %s (%s:%d)", stack.function, stack.file, stack.line);
if (stack == stack.prev) break;
stack = stack.prev;
}
@@ -88,66 +104,117 @@ fn void default_panic(String message, String file, String function, uint line)
$$trap();
}
typedef PanicFn = fn void(String message, String file, String function, uint line);
def PanicFn = fn void(String message, String file, String function, uint line);
PanicFn panic = &default_panic;
macro void unreachable($string = "Unreachable statement reached.") @builtin @noreturn
fn void panicf(String fmt, String file, String function, uint line, args...)
{
panic($string, $$FILE, $$FUNC, $$LINE);
@stack_mem(512; Allocator* mem)
{
DString s;
s.init(.using = mem);
s.printf(fmt, ...args);
panic(s.str(), file, function, line);
};
}
/**
* Marks the path as unreachable. This will panic in safe mode, and in fast will simply be assumed
* never happens.
* @param [in] string "The panic message or format string"
**/
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
$$unreachable();
}
/**
* Marks the path as unsupported, this is similar to unreachable.
* @param [in] string "The error message"
**/
macro void unsupported(String string = "Unsupported function invoked") @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
$$unreachable();
}
/**
* @param expr "the expression to cast"
* @param $Type "the type to cast to"
*
* @require $sizeof(expr) == $Type.sizeof "Cannot bitcast between types of different size."
* @ensure @typeis(result, $Type)
**/
macro bitcast(expr, $Type) @builtin
{
var $size = (usz)($sizeof(expr));
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
usz $size = $sizeof(expr);
$Type x @noinit;
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
return x;
}
/**
* @require $Type.kindof == TypeKind.ENUM `Only enums may be used`
* @param $Type `The type of the enum`
* @param [in] enum_name `The name of the enum to search for`
* @require $Type.kindof == ENUM `Only enums may be used`
* @ensure @typeis(return, $Type)
* @return! SearchResult.MISSING
**/
macro enum_by_name($Type, String enum_name) @builtin
{
typeid x = $Type.typeid;
foreach (i, name : x.names)
{
if (str::compare(name, enum_name)) return ($Type)i;
if (name == enum_name) return ($Type)i;
}
return SearchResult.MISSING!;
}
macro bool @likely(bool value, $probability = 1.0) @builtin
{
$if ($probability == 1.0)
return $$expect(value, true);
$else
return $$expect_with_probability(value, true, $probability);
$endif
}
macro bool @unlikely(bool value, $probability = 1.0) @builtin
{
$if ($probability == 1.0)
return $$expect(value, false);
$else
return $$expect_with_probability(value, false, $probability);
$endif
return SearchResult.MISSING?;
}
/**
* @require values::@is_int(value) || values::@is_bool(value)
* @checked $typeof(value) a = expected
* Mark an expression as likely to be true
*
* @param #value "expression to be marked likely"
* @param $probability "in the range 0 - 1"
* @require $probability >= 0 && $probability <= 1.0
**/
macro @expect(value, expected, $probability = 1.0) @builtin
macro bool @likely(bool #value, $probability = 1.0) @builtin
{
$if ($probability == 1.0)
return $$expect(value, ($typeof(value))expected);
$if $probability == 1.0:
return $$expect(#value, true);
$else
return $$expect_with_probability(#value, true, $probability);
$endif
}
/**
* Mark an expression as unlikely to be true
*
* @param #value "expression to be marked unlikely"
* @param $probability "in the range 0 - 1"
* @require $probability >= 0 && $probability <= 1.0
**/
macro bool @unlikely(bool #value, $probability = 1.0) @builtin
{
$if $probability == 1.0:
return $$expect(#value, false);
$else
return $$expect_with_probability(#value, false, $probability);
$endif
}
/**
* @require values::@is_int(#value) || values::@is_bool(#value)
* @checked $typeof(#value) a = expected
* @require $probability >= 0 && $probability <= 1.0
**/
macro @expect(#value, expected, $probability = 1.0) @builtin
{
$if $probability == 1.0:
return $$expect(#value, ($typeof(#value))expected);
$else
return $$expect_with_probability(value, expected, $probability);
return $$expect_with_probability(#value, expected, $probability);
$endif
}
@@ -195,6 +262,18 @@ macro bool @convertible(#expr, $To) @builtin
return $checks($To x = #expr);
}
macro anyfault @catchof(#expr) @builtin
{
if (catch f = #expr) return f;
return anyfault {};
}
macro bool @ok(#expr) @builtin
{
if (catch #expr) return false;
return true;
}
macro uint int.hash(int i) => i;
macro uint uint.hash(uint i) => i;
macro uint short.hash(short s) => s;
@@ -203,7 +282,9 @@ macro uint char.hash(char c) => c;
macro uint ichar.hash(ichar c) => c;
macro uint long.hash(long i) => (uint)((i >> 32) ^ i);
macro uint ulong.hash(ulong i) => (uint)((i >> 32) ^ i);
macro uint int128.hash(int128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
macro uint uint128.hash(uint128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
macro uint bool.hash(bool b) => (uint)b;
macro uint typeid.hash(typeid t) => (uint)(((uptr)t >> 32) ^ (uptr)t);
macro uint typeid.hash(typeid t) => ((ulong)(uptr)t).hash();
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);

View File

@@ -82,7 +82,7 @@ $endswitch
macro min(x, ...) @builtin
{
$if ($vacount == 1)
$if $vacount == 1:
return less(x, $vaarg(0)) ? x : $vaarg(0);
$else
var result = x;
@@ -98,7 +98,7 @@ macro min(x, ...) @builtin
macro max(x, ...) @builtin
{
$if ($vacount == 1)
$if $vacount == 1:
return greater(x, $vaarg(0)) ? x : $vaarg(0);
$else
var result = x;

View File

@@ -8,80 +8,80 @@ const C_LONG_SIZE = $$C_LONG_SIZE;
const C_SHORT_SIZE = $$C_SHORT_SIZE;
const C_LONG_LONG_SIZE = $$C_LONG_LONG_SIZE;
$assert (C_SHORT_SIZE < 32);
$assert (C_INT_SIZE < 128);
$assert (C_LONG_SIZE < 128);
$assert (C_LONG_LONG_SIZE <= 128);
$assert (C_SHORT_SIZE <= C_INT_SIZE);
$assert (C_INT_SIZE <= C_LONG_SIZE);
$assert (C_LONG_SIZE <= C_LONG_LONG_SIZE);
$assert C_SHORT_SIZE < 32;
$assert C_INT_SIZE < 128;
$assert C_LONG_SIZE < 128;
$assert C_LONG_LONG_SIZE <= 128;
$assert C_SHORT_SIZE <= C_INT_SIZE;
$assert C_INT_SIZE <= C_LONG_SIZE;
$assert C_LONG_SIZE <= C_LONG_LONG_SIZE;
$switch ($$C_INT_SIZE)
$case 64:
typedef CInt = long;
typedef CUInt = ulong;
def CInt = long;
def CUInt = ulong;
$case 32:
typedef CInt = int;
typedef CUInt = uint;
def CInt = int;
def CUInt = uint;
$case 16:
typedef CInt = short;
typedef CUInt = ushort;
def CInt = short;
def CUInt = ushort;
$default:
$assert(false, "Invalid C int size");
$error "Invalid C int size";
$endswitch
$switch ($$C_LONG_SIZE)
$case 64:
typedef CLong = long;
typedef CULong = ulong;
def CLong = long;
def CULong = ulong;
$case 32:
typedef CLong = int;
typedef CULong = uint;
def CLong = int;
def CULong = uint;
$case 16:
typedef CLong = short;
typedef CULong = ushort;
def CLong = short;
def CULong = ushort;
$default:
$assert(false, "Invalid C long size");
$error "Invalid C long size";
$endswitch
$switch ($$C_SHORT_SIZE)
$case 32:
typedef CShort = int;
typedef CUShort = uint;
def CShort = int;
def CUShort = uint;
$case 16:
typedef CShort = short;
typedef CUShort = ushort;
def CShort = short;
def CUShort = ushort;
$case 8:
typedef CShort = ichar;
typedef CUShort = char;
def CShort = ichar;
def CUShort = char;
$default:
$assert(false, "Invalid C short size");
$error "Invalid C short size";
$endswitch
$switch ($$C_LONG_LONG_SIZE)
$case 128:
typedef CLongLong = int128;
typedef CULongLong = uint128;
def CLongLong = int128;
def CULongLong = uint128;
$case 64:
typedef CLongLong = long;
typedef CULongLong = ulong;
def CLongLong = long;
def CULongLong = ulong;
$case 32:
typedef CLongLong = int;
typedef CULongLong = uint;
def CLongLong = int;
def CULongLong = uint;
$case 16:
typedef CLongLong = short;
typedef CULongLong = ushort;
def CLongLong = short;
def CULongLong = ushort;
$default:
$assert(false, "Invalid C long long size");
$error "Invalid C long long size";
$endswitch
typedef CSChar = ichar;
typedef CUChar = char;
def CSChar = ichar;
def CUChar = char;
$if ($$C_CHAR_IS_SIGNED)
typedef CChar = ichar;
$if $$C_CHAR_IS_SIGNED:
def CChar = ichar;
$else
typedef CChar = char;
def CChar = char;
$endif

View File

@@ -16,25 +16,25 @@ const uint UTF16_SURROGATE_HIGH_VALUE @private = 0xD800;
**/
fn usz! char32_to_utf8(Char32 c, char* output, usz available)
{
if (!available) return UnicodeResult.CONVERSION_FAILED!;
if (!available) return UnicodeResult.CONVERSION_FAILED?;
switch (true)
{
case c <= 0x7f:
output[0] = (char)c;
return 1;
case c <= 0x7ff:
if (available < 2) return UnicodeResult.CONVERSION_FAILED!;
if (available < 2) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xC0 | c >> 6);
output[1] = (char)(0x80 | (c & 0x3F));
return 2;
case c <= 0xffff:
if (available < 3) return UnicodeResult.CONVERSION_FAILED!;
if (available < 3) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xE0 | c >> 12);
output[1] = (char)(0x80 | (c >> 6 & 0x3F));
output[2] = (char)(0x80 | (c & 0x3F));
return 3;
case c <= 0x10ffff:
if (available < 4) return UnicodeResult.CONVERSION_FAILED!;
if (available < 4) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xF0 | c >> 18);
output[1] = (char)(0x80 | (c >> 12 & 0x3F));
output[2] = (char)(0x80 | (c >> 6 & 0x3F));
@@ -42,7 +42,7 @@ fn usz! char32_to_utf8(Char32 c, char* output, usz available)
return 4;
default:
// 0x10FFFF and above is not defined.
return UnicodeResult.CONVERSION_FAILED!;
return UnicodeResult.CONVERSION_FAILED?;
}
}
@@ -84,15 +84,15 @@ fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
return;
}
// Low surrogate first is an error
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16!;
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16?;
// Unmatched high surrogate is an error
if (*available == 1) return UnicodeResult.INVALID_UTF16!;
if (*available == 1) return UnicodeResult.INVALID_UTF16?;
Char16 low = ptr[1];
// Unmatched high surrogate, invalid
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16!;
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16?;
// The high bits of the codepoint are the value bits of the high surrogate
// The low bits of the codepoint are the value bits of the low surrogate
@@ -134,7 +134,7 @@ fn void char32_to_utf8_unsafe(Char32 c, char** output)
fn Char32! utf8_to_char32(char* ptr, usz* size)
{
usz max_size = *size;
if (max_size < 1) return UnicodeResult.INVALID_UTF8!;
if (max_size < 1) return UnicodeResult.INVALID_UTF8?;
char c = (ptr++)[0];
if ((c & 0x80) == 0)
@@ -144,40 +144,40 @@ fn Char32! utf8_to_char32(char* ptr, usz* size)
}
if ((c & 0xE0) == 0xC0)
{
if (max_size < 2) return UnicodeResult.INVALID_UTF8!;
if (max_size < 2) return UnicodeResult.INVALID_UTF8?;
*size = 2;
Char32 uc = (c & 0x1F) << 6;
c = *ptr;
// Overlong sequence or invalid second.
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
return uc + c & 0x3F;
}
if ((c & 0xF0) == 0xE0)
{
if (max_size < 3) return UnicodeResult.INVALID_UTF8!;
if (max_size < 3) return UnicodeResult.INVALID_UTF8?;
*size = 3;
Char32 uc = (c & 0x0F) << 12;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
uc += (c & 0x3F) << 6;
c = ptr++[0];
// Overlong sequence or invalid last
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
return uc + c & 0x3F;
}
if (max_size < 4) return UnicodeResult.INVALID_UTF8!;
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8!;
if (max_size < 4) return UnicodeResult.INVALID_UTF8?;
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8?;
*size = 4;
Char32 uc = (c & 0x07) << 18;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
uc += (c & 0x3F) << 12;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
uc += (c & 0x3F) << 6;
c = ptr++[0];
// Overlong sequence or invalid last
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
return uc + c & 0x3F;
}
@@ -303,7 +303,7 @@ fn usz! utf32to8(Char32[] utf32, String utf8_buffer)
char* ptr = utf8_buffer.ptr;
foreach (Char32 uc : utf32)
{
usz used = char32_to_utf8(uc, ptr, len) @inline?;
usz used = char32_to_utf8(uc, ptr, len) @inline!;
len -= used;
ptr += used;
}
@@ -327,9 +327,9 @@ fn usz! utf8to32(String utf8, Char32[] utf32_buffer)
usz buf_len = utf32_buffer.len;
for (usz i = 0; i < len;)
{
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED!;
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED?;
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
i += width;
ptr[len32++] = uc;
}
@@ -352,7 +352,7 @@ fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
for (usz i = 0; i < len16;)
{
usz available = len16 - i;
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline?;
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline!;
i += available;
}
}
@@ -371,7 +371,7 @@ fn void! utf8to32_unsafe(String utf8, Char32* utf32_buffer)
for (usz i = 0; i < len;)
{
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
i += width;
(utf32_buffer++)[0] = uc;
}
@@ -391,7 +391,7 @@ fn void! utf8to16_unsafe(String utf8, Char16* utf16_buffer)
for (usz i = 0; i < len;)
{
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
char32_to_utf16_unsafe(uc, &utf16_buffer) @inline;
i += width;
}

View File

@@ -1,6 +1,6 @@
module std::core::dstring;
typedef DString = distinct void*;
def DString = distinct void*;
const usz MIN_CAPACITY @private = 16;
@@ -261,7 +261,7 @@ fn void DString.append_chars(DString* this, String str)
fn Char32[] DString.copy_utf32(DString* this, Allocator* using = mem::heap())
{
return str::utf8to32(this.str(), using) @inline!!;
return this.str().to_utf32(using) @inline!!;
}
fn void DString.append_string(DString* this, DString str)
@@ -308,7 +308,7 @@ macro void DString.append(DString* str, value)
$case @convertible(value, String):
str.append_chars(value);
$default:
$assert(false, "Unsupported type for append use printf instead.");
$error "Unsupported type for append use printf instead.";
$endswitch
$endswitch
}
@@ -325,7 +325,7 @@ fn usz! DString.printfn(DString* str, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
usz len = formatter.vprintf(format, args)?;
usz len = formatter.vprintf(format, args)!;
str.append('\n');
return len + 1;
}
@@ -350,7 +350,7 @@ fn DString new_join(String[] s, String joiner, Allocator* using = mem::heap())
fn void! out_string_append_fn(char c, void* data) @private
{
DynString* s = data;
DString* s = data;
s.append_char(c);
}
@@ -380,11 +380,11 @@ fn usz! DString.read_from_stream(DString* string, Stream* reader)
if (reader.supports_available())
{
usz total_read = 0;
while (usz available = reader.available()?)
while (usz available = reader.available()!)
{
string.reserve(available);
StringData* data = string.data();
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])?;
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!;
total_read += len;
data.len += len;
}
@@ -397,7 +397,7 @@ fn usz! DString.read_from_stream(DString* string, Stream* reader)
string.reserve(16);
StringData* data = string.data();
// Read into the rest of the buffer
usz read = reader.read(data.chars[data.len..(data.capacity - 1)])?;
usz read = reader.read(data.chars[data.len..(data.capacity - 1)])!;
data.len += read;
// Ok, we reached the end.
if (read < 16) return total_read;

View File

@@ -32,7 +32,7 @@ enum OsType
KFREEBSD,
LINUX,
PS3,
MACOSX,
MACOS,
NETBSD,
OPENBSD,
SOLARIS,
@@ -130,14 +130,14 @@ const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT;
macro bool os_is_win32()
{
return OS_TYPE == OsType.WIN32;
return OS_TYPE == WIN32;
}
macro bool os_is_darwin()
{
$switch (OS_TYPE)
$case IOS:
$case MACOSX:
$case MACOS:
$case TVOS:
$case WATCHOS:
return true;
@@ -150,7 +150,7 @@ macro bool os_is_posix()
{
$switch (OS_TYPE)
$case IOS:
$case MACOSX:
$case MACOS:
$case NETBSD:
$case LINUX:
$case KFREEBSD:
@@ -177,11 +177,11 @@ macro bool os_is_posix()
**/
fn String! get_var(String name)
{
$if (COMPILER_LIBC_AVAILABLE && OS_TYPE != OsType.WIN32)
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
@pool()
{
ZString val = libc::getenv(name.zstr_tcopy());
return val ? val.as_str() : SearchResult.MISSING!;
return val ? val.as_str() : SearchResult.MISSING?;
};
$else
return "";
@@ -196,7 +196,7 @@ $endif
**/
fn void set_var(String name, String value, bool overwrite = true)
{
$if (COMPILER_LIBC_AVAILABLE && OS_TYPE != OsType.WIN32)
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
@pool()
{
if (libc::setenv(name.zstr_tcopy(), value.zstr_copy(), (int)overwrite))
@@ -213,7 +213,7 @@ $endif
**/
fn void clear_var(String name)
{
$if (COMPILER_LIBC_AVAILABLE && OS_TYPE != OsType.WIN32)
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
@pool()
{
if (libc::unsetenv(name.zstr_tcopy()))

View File

@@ -91,7 +91,7 @@ fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if ($inlined)
$if $inlined:
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
$else
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
@@ -100,7 +100,7 @@ macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = fal
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if ($inlined)
$if $inlined:
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
$else
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
@@ -114,7 +114,7 @@ macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if ($inlined)
$if $inlined:
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
$else
$$memset(dst, val, len, $is_volatile, $dst_align);
@@ -130,12 +130,12 @@ macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volati
**/
macro bool equals(a, b, isz len = -1, usz $align = 0)
{
$if (!$align)
$if !$align:
$align = $typeof(a[0]).alignof;
$endif
void* x @noinit;
void* y @noinit;
$if (values::@inner_kind(a) == TypeKind.SUBARRAY)
$if values::@inner_kind(a) == TypeKind.SUBARRAY:
len = a.len;
if (len != b.len) return false;
x = a.ptr;
@@ -206,10 +206,10 @@ macro malloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
**/
macro malloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
$if ($checks($vatype(0).sizeof))
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$assert(!type_alloc_must_be_aligned($vatype(0)), "Type must be allocated with malloc_aligned");
$if ($vacount == 2)
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with malloc_aligned";
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.alloc($Type.sizeof * size + end_padding))[:size];
$else
@@ -228,9 +228,9 @@ macro malloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @
**/
macro malloc_aligned(..., usz alignment = 0, usz end_padding = 0, Allocator* using = mem::heap()) @builtin
{
$if ($checks($vatype(0).sizeof))
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if ($vacount == 2)
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size];
$else
@@ -241,10 +241,6 @@ macro malloc_aligned(..., usz alignment = 0, usz end_padding = 0, Allocator* usi
$endif
}
macro alloc($Type) @deprecated => malloc($Type);
macro char[] alloc_bytes(usz bytes) @deprecated => malloc(char, bytes);
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
@@ -260,10 +256,10 @@ macro calloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
**/
macro calloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
$if ($checks($vatype(0).sizeof))
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$assert(!type_alloc_must_be_aligned($vatype(0)), "Type must be allocated with calloc_aligned");
$if ($vacount == 2)
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with calloc_aligned";
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.calloc($Type.sizeof * size + end_padding))[:size];
$else
@@ -282,9 +278,9 @@ macro calloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @
**/
macro calloc_aligned(..., usz alignment = 0, Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
$if ($checks($vatype(0).sizeof))
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if ($vacount == 2)
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size];
$else
@@ -329,17 +325,15 @@ macro void @scoped(Allocator* using; @body())
@body();
}
macro talloc($Type) @builtin @deprecated => tmalloc($Type);
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @builtin
{
$if ($checks($vatype(0).sizeof))
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if ($vacount == 2)
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)temp().alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
$else
@@ -356,9 +350,9 @@ macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @
**/
macro tcalloc(..., usz end_padding = 0, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin
{
$if ($checks($vatype(0).sizeof))
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if ($vacount == 2)
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)temp().calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
$else
@@ -417,7 +411,7 @@ macro TempAllocator* temp()
macro Allocator* current_allocator() => thread_allocator;
macro Allocator* heap() => thread_allocator;
$if (!env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64)
$if !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64:
SimpleHeapAllocator wasm_allocator @private;

View File

@@ -6,7 +6,7 @@ const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR;
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR;
typedef AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
def AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
struct Allocator
{
@@ -83,12 +83,12 @@ fn void*! Allocator.calloc_aligned(Allocator* allocator, usz size, usz alignment
fn void! Allocator.free(Allocator* allocator, void* old_pointer) @inline
{
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)?;
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)!;
}
fn void! Allocator.free_aligned(Allocator* allocator, void* old_pointer) @inline
{
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)?;
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)!;
}
fn void Allocator.reset(Allocator* allocator, usz mark = 0)

View File

@@ -1,31 +0,0 @@
// Copyright (c) 2021-2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::array;
/**
* @require usz.max / elements > $Type.sizeof
**/
macro alloc($Type, usz elements) @deprecated => malloc($Type, elements);
/**
* @require usz.max / elements > $Type.sizeof
**/
macro talloc($Type, usz elements) @deprecated => tmalloc($Type, elements);
/**
* @require (usz.max / elements > $Type.sizeof)
**/
macro make($Type, usz elements, Allocator* using = mem::heap()) @deprecated
{
return calloc($Type, elements, .using = using);
}
/**
* @require (usz.max / elements > $Type.sizeof)
**/
macro tmake($Type, usz elements) @deprecated
{
return tcalloc($Type, elements);
}

View File

@@ -25,7 +25,7 @@ fn char[]! WasmMemory.allocate_block(WasmMemory* this, usz bytes)
}
usz blocks_required = (bytes_required + WASM_BLOCK_SIZE + 1) / WASM_BLOCK_SIZE;
if ($$wasm_memory_grow(0, blocks_required) == -1) return AllocationFailure.OUT_OF_MEMORY!;
if ($$wasm_memory_grow(0, blocks_required) == -1) return AllocationFailure.OUT_OF_MEMORY?;
this.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
defer this.use += bytes;
return ((char*)this.use)[:bytes];

View File

@@ -7,7 +7,11 @@ macro usz _strlen(ptr) @private
return len;
}
macro int @main_to_err_main(#m, int, char**) => catch? #m() ? 1 : 0;
macro int @main_to_err_main(#m, int, char**)
{
if (catch #m()) return 1;
return 0;
}
macro int @main_to_int_main(#m, int, char**) => #m();
macro int @main_to_void_main(#m, int, char**)
{
@@ -32,7 +36,8 @@ macro int @main_to_err_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
return catch? #m(list) ? 1 : 0;
if (catch #m(list)) return 1;
return 0;
}
macro int @main_to_int_main_args(#m, int argc, char** argv)
@@ -50,7 +55,7 @@ macro int @main_to_void_main_args(#m, int argc, char** argv)
return 0;
}
$if (env::OS_TYPE == OsType.WIN32)
$if env::os_is_win32():
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @extern("CommandLineToArgvW");
@@ -79,7 +84,11 @@ macro void release_wargs(String[] list) @private
free(list.ptr);
}
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => catch? #m() ? 1 : 0;
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
{
if (catch #m()) return 1;
return 0;
}
macro int @win_to_int_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => #m();
macro int @win_to_void_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
{
@@ -91,7 +100,8 @@ macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return catch? #m(args) ? 1 : 0;
if (catch #m(args)) return 1;
return 0;
}
macro int @win_to_int_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
@@ -113,7 +123,8 @@ macro int @win_to_err_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return catch? #m(handle, args, show_cmd) ? 1 : 0;
if (catch #m(handle, args, show_cmd)) return 1;
return 0;
}
macro int @win_to_int_main(#m, void* handle, Char16* cmd_line, int show_cmd)
@@ -135,7 +146,8 @@ macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
return catch? #m(args) ? 1 : 0;
if (catch #m(args)) return 1;
return 1;
}
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)

View File

@@ -9,26 +9,13 @@ struct VirtualAny
typeid type_id;
}
struct VirtualContainer
{
void* ptr;
void* impl_ptr;
}
struct SubArrayContainer
{
void* ptr;
usz len;
}
struct VarArrayHeader
{
usz size;
usz capacity;
void *allocator;
}
typedef TestFn = fn void!();
def TestFn = fn void!();
struct TestRunner
{
@@ -48,10 +35,13 @@ fn TestRunner test_runner_create()
import libc;
TestRunner* current_runner @private;
fn void test_panic(String message, String file, String function, uint line)
{
io::printn("[error]");
io::printfn("\n Error: %s", message);
io::print("\n Error: ");
io::print(message);
io::printn();
io::printfn(" - in %s %s:%s.\n", function, file, line);
libc::longjmp(&current_runner.buf, 1);
}
@@ -98,7 +88,7 @@ fn bool __run_default_test_runner()
return test_runner_create().run();
}
$if (!env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64)
$if !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64:
extern fn void __wasm_call_ctors();
fn void wasm_initialize() @extern("_initialize") @wasm

View File

@@ -1,259 +0,0 @@
module std::core::str;
fn VarString join(String[] s, String joiner)
{
if (!s.len) return (VarString)null;
usz total_size = joiner.len * s.len;
foreach (String* &str : s)
{
total_size += str.len;
}
VarString res = string::new_with_capacity(total_size);
res.append(s[0]);
foreach (String* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res;
}
macro bool char_in_set(char c, String set)
{
foreach (ch : set)
{
if (ch == c) return true;
}
return false;
}
macro char_is_space_tab(char c) @private
{
return c == ' ' || c == '\t';
}
macro to_integer($Type, String string) @private
{
usz len = string.len;
usz index = 0;
char* ptr = string.ptr;
while (index < len && char_is_space_tab(ptr[index])) index++;
if (len == index) return NumberConversion.EMPTY_STRING!;
bool is_negative;
switch (string[index])
{
case '-':
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE!;
is_negative = true;
index++;
case '+':
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
$Type base = 10;
if (string[index] == '0')
{
index++;
if (index == len) return ($Type)0;
switch (string[index])
{
case 'x':
case 'X':
base = 16;
index++;
case 'b':
case 'B':
base = 2;
index++;
case 'o':
case 'O':
base = 8;
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
}
$Type value = 0;
while (index != len)
{
char c = {|
char ch = string[index++];
if (base != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A');
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER!;
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER!;
return (char)(ch - 'a');
|}?;
if (c >= base) return NumberConversion.MALFORMED_INTEGER!;
value = {|
if (is_negative)
{
$Type new_value = value * base - c;
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW!;
return new_value;
}
$Type new_value = value * base + c;
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW!;
return new_value;
|}?;
}
return value;
}
fn float! to_float(String string) => floatparse(string, float);
fn double! to_double(String string) => floatparse(string, double);
fn int128! to_int128(String string) => to_integer(int128, string);
fn long! to_long(String string) => to_integer(long, string);
fn int! to_int(String string) => to_integer(int, string);
fn short! to_short(String string) => to_integer(short, string);
fn ichar! to_ichar(String string) => to_integer(ichar, string);
fn uint128! to_uint128(String str) => to_integer(uint128, str);
fn ulong! to_ulong(String str) => to_integer(ulong, str);
fn uint! to_uint(String str) => to_integer(uint, str);
fn ushort! to_ushort(String str) => to_integer(ushort, str);
fn char! to_uchar(String str) => to_integer(char, str);
fn String trim(String string, String to_trim = "\t\n\r ") @deprecated => string.trim(to_trim);
fn bool starts_with(String s, String needle) @deprecated => s.starts_with(needle);
fn String[] tsplit(String s, String needle) @deprecated => s.split(needle, .using = mem::temp()) @inline;
fn String[] split(String s, String needle, Allocator* using = mem::heap()) @deprecated => s.split(needle, .using = using);
fn usz! rindex_of(String s, String needle) @deprecated => s.rindex_of(needle);
fn usz! index_of(String s, String needle) @deprecated => s.index_of(needle);
fn ZString String.zstrcopy(String s, Allocator* using = mem::heap()) @deprecated => s.zstr_copy(using);
fn ZString String.zstrtcopy(String s) @deprecated => s.zstr_tcopy();
fn ZString copy_zstring(String s, Allocator* using = mem::heap()) @deprecated => s.zstr_copy(using);
fn String copyz(String s, Allocator* using = mem::heap()) @deprecated => s.copy(using);
fn ZString tcopy_zstring(String s) @deprecated => s.zstr_tcopy();
fn bool compare(String a, String b)
{
if (a.len != b.len) return false;
foreach (i, c : a)
{
if (c != b[i]) return false;
}
return true;
}
fault UnicodeResult
{
INVALID_UTF8,
INVALID_UTF16,
CONVERSION_FAILED,
}
fn usz utf8_codepoints(String utf8)
{
usz len = 0;
foreach (char c : utf8)
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
fn Char32[]! utf8to32(String utf8, Allocator* using = mem::heap())
{
usz codepoints = conv::utf8_codepoints(utf8);
Char32* data = malloc_checked(Char32, codepoints + 1, .using = using)?;
conv::utf8to32_unsafe(utf8, data)?;
data[codepoints] = 0;
return data[:codepoints];
}
fn String utf32to8(Char32[] utf32, Allocator* using = mem::heap())
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = malloc_checked(len + 1, .using = using)!!;
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return (String)data[:len];
}
fn Char16[]! utf8to16(String utf8, Allocator* using = mem::heap())
{
usz len16 = conv::utf16len_for_utf8(utf8);
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)?;
conv::utf8to16_unsafe(utf8, data)?;
data[len16] = 0;
return data[:len16];
}
fn String! utf16to8(Char16[] utf16, Allocator* using = mem::heap())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = malloc_checked(len + 1, .using = using)?;
conv::utf16to8_unsafe(utf16, data)?;
data[len] = 0;
return (String)data[:len];
}
fn String copy(String s, Allocator* using = mem::heap()) @deprecated
{
usz len = s.len;
ZString str_copy = s.zstr_copy(using) @inline;
return (String)str_copy[:len];
}
fn String tcopy(String s) @deprecated
{
usz len = s.len;
ZString str_copy = s.zstr_tcopy() @inline;
return (String)str_copy[:len];
}
fn String tconcat(String s1, String s2)
{
usz full_len = s1.len + s2.len;
char* str = tmalloc(full_len + 1);
usz s1_len = s1.len;
mem::copy(str, s1.ptr, s1_len);
mem::copy(str + s1_len, s2.ptr, s2.len);
str[full_len] = 0;
return (String)str[:full_len];
}
fn String concat(String s1, String s2)
{
usz full_len = s1.len + s2.len;
char* str = malloc(full_len + 1);
usz s1_len = s1.len;
mem::copy(str, s1.ptr, s1_len);
mem::copy(str + s1_len, s2.ptr, s2.len);
str[full_len] = 0;
return (String)str[:full_len];
}
fn String ZString.as_str(ZString str)
{
return (String)((char*)str)[:str.len()];
}
fn usz ZString.char_len(ZString str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0])
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
fn usz ZString.len(ZString str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0]) len++;
return len;
}

View File

@@ -1,8 +1,16 @@
module std::core::string;
import std::ascii;
typedef ZString = distinct inline char*;
typedef Char32 = uint;
typedef Char16 = ushort;
def ZString = distinct inline char*;
def Char32 = uint;
def Char16 = ushort;
fault UnicodeResult
{
INVALID_UTF8,
INVALID_UTF16,
CONVERSION_FAILED,
}
const uint SURROGATE_OFFSET @private = 0x10000;
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
@@ -47,6 +55,31 @@ macro bool char_in_set(char c, String set)
return false;
}
fn String join(String[] s, String joiner, Allocator* using = mem::heap())
{
if (!s)
{
return (String)(calloc(char, 2, .using = using)[:0]);
}
usz total_size = joiner.len * s.len;
foreach (String* &str : s)
{
total_size += str.len;
}
@stack_mem(256; Allocator* mem)
{
DString res = dstring::new_with_capacity(total_size, .using = mem);
res.append(s[0]);
foreach (String* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res.copy_str(using);
};
}
/**
* @param [in] string
* @param [in] to_trim
@@ -128,7 +161,7 @@ fn String[] String.split(String s, String needle, usz max = 0, Allocator* using
bool no_more = false;
while (!no_more)
{
usz! index = i == max - 1 ? SearchResult.MISSING! : s.index_of(needle);
usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle);
String res @noinit;
if (try index)
{
@@ -163,6 +196,11 @@ fn String[] String.tsplit(String s, String needle, usz max = 0)
return s.split(needle, max, mem::temp()) @inline;
}
fn bool String.contains(String s, String needle)
{
return @ok(s.index_of(needle));
}
/**
* Find the index of the first incidence of a string.
*
@@ -170,7 +208,9 @@ fn String[] String.tsplit(String s, String needle, usz max = 0)
* @param [in] needle
* @pure
* @ensure return < s.len
* @require needle.len > 0 "The needle must be len 1 or more"
* @require needle.len > 0 : "The needle must be len 1 or more"
* @return "the index of the needle"
* @return! SearchResult.MISSING "if the needle cannot be found"
**/
fn usz! String.index_of(String s, String needle)
{
@@ -194,7 +234,7 @@ fn usz! String.index_of(String s, String needle)
search = needle[0];
}
}
return SearchResult.MISSING!;
return SearchResult.MISSING?;
}
/**
@@ -205,6 +245,8 @@ fn usz! String.index_of(String s, String needle)
* @pure
* @ensure return < s.len
* @require needle.len > 0 "The needle must be len 1 or more"
* @return "the index of the needle"
* @return! SearchResult.MISSING "if the needle cannot be found"
**/
fn usz! String.rindex_of(String s, String needle)
{
@@ -228,9 +270,34 @@ fn usz! String.rindex_of(String s, String needle)
search = needle[^1];
}
}
return SearchResult.MISSING!;
return SearchResult.MISSING?;
}
fn String ZString.as_str(ZString str)
{
return (String)((char*)str)[:str.len()];
}
fn usz ZString.char_len(ZString str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0])
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
fn usz ZString.len(ZString str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0]) len++;
return len;
}
fn ZString String.zstr_copy(String s, Allocator* using = mem::heap())
{
usz len = s.len;
@@ -270,23 +337,178 @@ fn String String.tcopy(String s) => s.copy(mem::temp()) @inline;
fn String ZString.copy(ZString z, Allocator* using = mem::heap()) => z.as_str().copy(using) @inline;
fn String ZString.tcopy(ZString z) => z.as_str().copy(mem::temp()) @inline;
/**
* Convert an UTF-8 string to UTF-16
* @return "The UTF-16 string as a slice, allocated using the given allocator"
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
* @return! AllocationFailure "If allocation of the string fails"
**/
fn Char16[]! String.to_utf16(String s, Allocator* using = mem::heap())
{
usz len16 = conv::utf16len_for_utf8(s);
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)?;
conv::utf8to16_unsafe(s, data)?;
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)!;
conv::utf8to16_unsafe(s, data)!;
data[len16] = 0;
return data[:len16];
}
fn Char32[]! String.to_utf32(String s, Allocator* using = mem::heap())
{
usz codepoints = conv::utf8_codepoints(s);
Char32* data = malloc_checked(Char32, codepoints + 1, .using = using)!;
conv::utf8to32_unsafe(s, data)!;
data[codepoints] = 0;
return data[:codepoints];
}
fn void String.convert_ascii_to_lower(String s)
{
foreach (&c : s) if (*c >= 'A' && *c <= 'Z') *c += 'a' - 'A';
}
fn String String.ascii_to_lower(String s, Allocator* using = mem::heap())
{
String copy = s.copy(using);
copy.convert_ascii_to_lower();
return copy;
}
fn void String.convert_ascii_to_upper(String s)
{
foreach (&c : s) if (*c >= 'a' && *c <= 'z') *c -= 'a' - 'A';
}
fn String String.ascii_to_upper(String s, Allocator* using = mem::heap())
{
String copy = s.copy(using);
copy.convert_ascii_to_upper();
return copy;
}
fn String! from_utf32(Char32[] utf32, Allocator* using = mem::heap())
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = malloc_checked(len + 1, .using = using)!;
defer catch free(data, .using = using);
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return (String)data[:len];
}
fn String! from_utf16(Char16[] utf16, Allocator* using = mem::heap())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = malloc_checked(len + 1, .using = using)?;
conv::utf16to8_unsafe(utf16, data)?;
char* data = malloc_checked(len + 1, .using = using)!;
defer catch free(data, .using = using);
conv::utf16to8_unsafe(utf16, data)!;
data[len] = 0;
return (String)data[:len];
}
fn String! from_zutf16(Char16* utf16_pointer, Allocator* using = mem::heap())
{
usz utf16_len;
while (utf16_pointer[utf16_len] != 0) utf16_len++;
Char16[] utf16 = utf16_pointer[:utf16_len];
return from_utf16(utf16, using);
}
fn usz String.utf8_codepoints(String s)
{
usz len = 0;
foreach (char c : s)
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
macro String.to_integer(String string, $Type)
{
usz len = string.len;
usz index = 0;
char* ptr = string.ptr;
while (index < len && ascii::is_blank_m(ptr[index])) index++;
if (len == index) return NumberConversion.EMPTY_STRING?;
bool is_negative;
switch (string[index])
{
case '-':
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE?;
is_negative = true;
index++;
case '+':
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
$Type base = 10;
if (string[index] == '0')
{
index++;
if (index == len) return ($Type)0;
switch (string[index])
{
case 'x':
case 'X':
base = 16;
index++;
case 'b':
case 'B':
base = 2;
index++;
case 'o':
case 'O':
base = 8;
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
}
$Type value = 0;
while (index != len)
{
char c = {|
char ch = string[index++];
if (base != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A');
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?;
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?;
return (char)(ch - 'a');
|}!;
if (c >= base) return NumberConversion.MALFORMED_INTEGER?;
value = {|
if (is_negative)
{
$Type new_value = value * base - c;
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
}
$Type new_value = value * base + c;
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
|}!;
}
return value;
}
fn Char16[]! String.to_temp_utf16(String s) => s.to_utf16(mem::temp());
fn int128! String.to_int128(String s) => s.to_integer(int128);
fn long! String.to_long(String s) => s.to_integer(long);
fn int! String.to_int(String s) => s.to_integer(int);
fn short! String.to_short(String s) => s.to_integer(short);
fn ichar! String.to_ichar(String s) => s.to_integer(ichar);
fn uint128! String.to_uint128(String s) => s.to_integer(uint128);
fn ulong! String.to_ulong(String s) => s.to_integer(ulong);
fn uint! String.to_uint(String s) => s.to_integer(uint);
fn ushort! String.to_ushort(String s) => s.to_integer(ushort);
fn char! String.to_uchar(String s) => s.to_integer(char);
fn double! String.to_double(String s) => s.to_real(double);
fn float! String.to_float(String s) => s.to_real(float);

View File

@@ -15,9 +15,9 @@ fn Char32! StringIterator.next(StringIterator* this)
{
usz len = this.utf8.len;
usz current = this.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT!;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
usz read = (len - current < 4 ? len - current : 4);
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)?;
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)!;
this.current += read;
return res;
}

View File

@@ -1,4 +1,4 @@
module std::core::str;
module std::core::string;
import std::math;
// Float parsing based on code in Musl floatscan.c by Rich Felker.
@@ -64,7 +64,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT!;
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
@@ -83,7 +83,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
switch
{
case c == '.':
if (got_rad) return NumberConversion.MALFORMED_FLOAT!;
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
got_rad = true;
lrp = dc;
case k < KMAX - 3:
@@ -113,24 +113,24 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
c = chars[++index];
}
if (!got_rad) lrp = dc;
if (!got_digit) return NumberConversion.MALFORMED_FLOAT!;
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if ((c | 32) == 'e')
{
if (last_char == index) return NumberConversion.MALFORMED_FLOAT!;
long e10 = str::to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT!?;
if (last_char == index) return NumberConversion.MALFORMED_FLOAT?;
long e10 = String.to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT?!;
lrp += e10;
}
else if (index != last_char)
{
return NumberConversion.MALFORMED_FLOAT!;
return NumberConversion.MALFORMED_FLOAT?;
}
// Handle zero specially to avoid nasty special cases later
if (!x[0]) return sign * 0.0;
// Optimize small integers (w/no exponent) and over/under-flow
if (lrp == dc && dc < 10 && ($bits > 30 || (ulong)x[0] >> $bits == 0)) return sign * (double)x[0];
if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE!;
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE!;
if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
// Align incomplete final B1B digit
if (j)
@@ -320,7 +320,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
y *= 0.5;
e2++;
}
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT!;
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT?;
}
return math::scalbn(y, e2);
}
@@ -351,7 +351,7 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT!;
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
@@ -369,7 +369,7 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
{
if (c == '.')
{
if (got_rad) return NumberConversion.MALFORMED_FLOAT!;
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
got_rad = true;
rp = dc;
}
@@ -396,20 +396,20 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
if (index == last_char) break;
c = chars[++index];
}
if (!got_digit) return NumberConversion.MALFORMED_FLOAT!;
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if (!got_rad) rp = dc;
for (; dc < 8; dc++) x *= 16;
long e2;
if ((c | 32) == 'p')
{
long e2val = str::to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT!?;
long e2val = String.to_long((String)chars[index + 1..]) ?? (NumberConversion.MALFORMED_FLOAT?)!;
e2 = e2val;
}
e2 += 4 * rp - 32;
if (!x) return sign * 0.0;
if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE!;
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE!;
if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
while (x < 0x80000000)
{
@@ -444,12 +444,12 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
}
y = bias + sign * (double)x + sign * y;
y -= bias;
if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE!;
if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE?;
return math::scalbn(y, (int)e2);
}
macro floatparse(String chars, $Type) @private
macro String.to_real(String chars, $Type) @private
{
int sign = 1;
$switch ($Type)
@@ -460,13 +460,13 @@ macro floatparse(String chars, $Type) @private
const int BITS = math::DOUBLE_MANT_DIG;
const int EMIN = math::DOUBLE_MIN_EXP - BITS;
$case float128:
$assert(false, "Not yet supported");
$error "Not yet supported";
$default:
$assert(false, "Unexpected type");
$error "Unexpected type";
$endswitch
while (chars.len && chars[0] == ' ') chars = chars[1..];
if (!chars.len) return NumberConversion.MALFORMED_FLOAT!;
if (!chars.len) return NumberConversion.MALFORMED_FLOAT?;
switch (chars[0])
{
case '-':

View File

@@ -1,3 +1,4 @@
module std::core::types;
import libc;
@@ -10,63 +11,63 @@ fault ConversionResult
/**
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
**/
macro variant_to_int(variant v, $Type)
macro any_to_int(any v, $Type)
{
typeid variant_type = v.type;
TypeKind kind = variant_type.kindof;
typeid any_type = v.type;
TypeKind kind = any_type.kindof;
if (kind == TypeKind.ENUM)
{
variant_type = variant_type.inner;
kind = variant_type.kindof;
any_type = any_type.inner;
kind = any_type.kindof;
}
bool is_mixed_signed = $Type.kindof != variant_type.kindof;
bool is_mixed_signed = $Type.kindof != any_type.kindof;
$Type max = $Type.max;
$Type min = $Type.min;
switch (variant_type)
switch (any_type)
{
case ichar:
ichar c = *(char*)v.ptr;
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
return ($Type)c;
case short:
short s = *(short*)v.ptr;
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)s;
case int:
int i = *(int*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case long:
long l = *(long*)v.ptr;
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)l;
case int128:
int128 i = *(int128*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case char:
char c = *(char*)v.ptr;
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)c;
case ushort:
ushort s = *(ushort*)v.ptr;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)s;
case uint:
uint i = *(uint*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case ulong:
ulong l = *(ulong*)v.ptr;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)l;
case uint128:
uint128 i = *(uint128*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
default:
unreachable();
@@ -76,7 +77,7 @@ macro variant_to_int(variant v, $Type)
macro bool is_numerical($Type)
{
var $kind = $Type.kindof;
$if ($kind == TypeKind.DISTINCT)
$if $kind == TypeKind.DISTINCT:
return is_numerical($Type.inner);
$else
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
@@ -97,7 +98,7 @@ macro bool is_indexable($Type)
macro bool is_comparable($Type)
{
var $kind = $Type.kindof;
$if ($kind == TypeKind.DISTINCT)
$if $kind == TypeKind.DISTINCT:
return is_comparable($Type.inner);
$else
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
@@ -161,7 +162,7 @@ macro bool is_vector($Type)
macro TypeKind inner_kind($Type)
{
$if ($Type.kindof == TypeKind.DISTINCT)
$if $Type.kindof == TypeKind.DISTINCT:
return inner_kind($typefrom($Type.inner));
$else
return $Type.kindof;
@@ -181,11 +182,11 @@ macro bool is_same($TypeA, $TypeB)
macro bool @has_same(#a, #b, ...)
{
var $type_a = $typeof(#a).typeid;
$if ($type_a != $typeof(#b).typeid)
$if $type_a != $typeof(#b).typeid:
return false;
$endif
$for (var $i = 0; $i < $vacount; $i++)
$if ($typeof($vaexpr($i)).typeid != $type_a)
$if $typeof($vaexpr($i)).typeid != $type_a:
return false;
$endif
$endfor
@@ -212,25 +213,30 @@ macro bool is_promotable_to_float($Type) => types::is_float($Type) || types::is_
macro bool is_same_vector_type($Type1, $Type2)
{
$if ($Type1.kindof != TypeKind.VECTOR)
$if $Type1.kindof != TypeKind.VECTOR:
return $Type2.kindof != TypeKind.VECTOR;
$else
return $Type1.inner == $Type2.inner && $Type1.len == $Type2.len;
$endif
}
macro bool is_equatable_value(value)
macro bool is_equatable_type($Type)
{
$if ($defined(value.less) || $defined(value.compare_to) || $defined(value.equals))
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
return true;
$else
return is_equatable($typeof(value));
return is_equatable($Type);
$endif
}
macro bool is_equatable_value(value)
{
return is_equatable_type($typeof(value));
}
macro bool is_comparable_value(value)
{
$if ($defined(value.less) || $defined(value.compare_to))
$if $defined(value.less) || $defined(value.compare_to):
return true;
$else
return is_comparable($typeof(value));
@@ -245,8 +251,8 @@ enum TypeKind : char
UNSIGNED_INT,
FLOAT,
TYPEID,
ANYERR,
VARIANT,
ANYFAULT,
ANY,
ENUM,
FAULT,
STRUCT,

View File

@@ -12,7 +12,7 @@ macro bool @is_promotable_to_float(#value) => types::is_promotable_to_float($typ
macro bool @is_same_vector_type(#value1, #value2) => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
macro promote_int(x)
{
$if (@is_int(x))
$if @is_int(x):
return (double)x;
$else
return x;
@@ -20,3 +20,4 @@ macro promote_int(x)
}
macro TypeKind @inner_kind(#value) => types::inner_kind($typeof(#value));

View File

@@ -1,327 +0,0 @@
module std::core::string;
import libc;
typedef VarString = distinct void*;
typedef DynStr = VarString;
typedef DynString = VarString;
typedef VString = VarString;
typedef Text = VarString;
const usz MIN_CAPACITY = 16;
fn VarString new_with_capacity(usz capacity, Allocator* allocator = mem::heap())
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = malloc(StringData, 1, .using = allocator, .end_padding = capacity);
data.allocator = allocator;
data.len = 0;
data.capacity = capacity;
return (VarString)data;
}
fn VarString new(String c)
{
usz len = c.len;
VarString str = new_with_capacity(len);
StringData* data = str.data();
if (len)
{
data.len = len;
mem::copy(&data.chars, c.ptr, len);
}
return (VarString)data;
}
fn ZString VarString.zstr(VarString str)
{
StringData* data = str.data();
if (!data) return (ZString)"";
if (data.capacity == data.len)
{
str.reserve(1);
data.chars[data.len] = 0;
}
else if (data.chars[data.len] != 0)
{
data.chars[data.len] = 0;
}
return (ZString)&data.chars[0];
}
fn usz VarString.capacity(VarString this)
{
if (!this) return 0;
return this.data().capacity;
}
fn usz VarString.len(VarString this)
{
if (!this) return 0;
return this.data().len;
}
/**
* @require new_size <= this.len()
*/
fn void VarString.chop(VarString this, usz new_size)
{
if (!this) return;
this.data().len = new_size;
}
fn String VarString.str(VarString str)
{
StringData* data = (StringData*)str;
if (!data) return "";
return (String)data.chars[:data.len];
}
fn void VarString.append_utf32(VarString* str, Char32[] chars)
{
str.reserve(chars.len);
foreach (Char32 c : chars)
{
str.append_char32(c);
}
}
/**
* @require index < str.len()
**/
fn void VarString.set(VarString str, usz index, char c)
{
str.data().chars[index] = c;
}
fn void VarString.append_repeat(VarString* str, char c, usz times)
{
if (times == 0) return;
str.reserve(times);
StringData* data = str.data();
for (usz i = 0; i < times; i++)
{
data.chars[data.len++] = c;
}
}
/**
* @require c <= 0x10ffff
*/
fn void VarString.append_char32(VarString* str, Char32 c)
{
if (c < 0x7f)
{
str.reserve(1);
StringData* data = str.data();
data.chars[data.len++] = (char)c;
return;
}
if (c < 0x7ff)
{
str.reserve(2);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xC0 | c >> 6);
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
return;
}
if (c < 0xffff)
{
str.reserve(3);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xE0 | c >> 12);
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
return;
}
str.reserve(4);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xF0 | c >> 18);
data.chars[data.len++] = (char)(0x80 | (c >> 12 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
}
fn VarString VarString.tcopy(VarString* str) => str.copy(mem::temp());
fn VarString VarString.copy(VarString* str, Allocator* allocator = null)
{
if (!str)
{
if (allocator) return new_with_capacity(0, allocator);
return (VarString)null;
}
if (!allocator) allocator = mem::heap();
StringData* data = str.data();
VarString new_string = new_with_capacity(data.capacity, allocator);
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
return new_string;
}
fn ZString VarString.copy_zstr(VarString* str, Allocator* allocator = mem::heap())
{
usz str_len = str.len();
if (!str_len)
{
return (ZString)calloc(1, .using = allocator);
}
char* zstr = malloc(str_len + 1, .using = allocator);
StringData* data = str.data();
mem::copy(zstr, &data.chars, str_len);
zstr[str_len] = 0;
return (ZString)zstr;
}
fn String VarString.copy_str(VarString* str, Allocator* allocator = mem::heap())
{
return (String)str.copy_zstr(allocator)[:str.len()];
}
fn String VarString.tcopy_str(VarString* str) => str.copy_str(mem::temp()) @inline;
fn bool VarString.equals(VarString str, VarString other_string)
{
StringData *str1 = str.data();
StringData *str2 = other_string.data();
if (str1 == str2) return true;
if (!str1) return str2.len == 0;
if (!str2) return str1.len == 0;
usz str1_len = str1.len;
if (str1_len != str2.len) return false;
for (int i = 0; i < str1_len; i++)
{
if (str1.chars[i] != str2.chars[i]) return false;
}
return true;
}
fn void VarString.destroy(VarString* str)
{
if (!*str) return;
StringData* data = str.data();
if (!data) return;
free(data, .using = data.allocator);
*str = (VarString)null;
}
fn bool VarString.less(VarString str, VarString other_string)
{
StringData* str1 = str.data();
StringData* str2 = other_string.data();
if (str1 == str2) return false;
if (!str1) return str2.len != 0;
if (!str2) return str1.len == 0;
usz str1_len = str1.len;
usz str2_len = str2.len;
if (str1_len != str2_len) return str1_len < str2_len;
for (int i = 0; i < str1_len; i++)
{
if (str1.chars[i] >= str2.chars[i]) return false;
}
return true;
}
fn void VarString.append_chars(VarString* this, String str)
{
usz other_len = str.len;
if (!other_len) return;
if (!*this)
{
*this = new(str);
return;
}
this.reserve(other_len);
StringData* data = (StringData*)*this;
mem::copy(&data.chars[data.len], str.ptr, other_len);
data.len += other_len;
}
fn Char32[] VarString.copy_utf32(VarString* this, Allocator* allocator = mem::heap())
{
return str::utf8to32(this.str(), allocator) @inline!!;
}
fn void VarString.append_string(VarString* this, VarString str)
{
StringData* other = (StringData*)str;
if (!other) return;
this.append(str.str());
}
fn void VarString.clear(VarString* str)
{
str.data().len = 0;
}
fn void VarString.append_char(VarString* str, char c)
{
if (!*str)
{
*str = new_with_capacity(MIN_CAPACITY);
}
str.reserve(1);
StringData* data = (StringData*)*str;
data.chars[data.len++] = c;
}
macro void VarString.append(VarString* str, value)
{
var $Type = $typeof(value);
$switch ($Type)
$case char:
$case ichar:
str.append_char(value);
$case VarString:
str.append_string(value);
$case String:
str.append_chars(value);
$case Char32:
str.append_char32(value);
$default:
$switch
$case @convertible(value, Char32):
str.append_char32(value);
$case @convertible(value, String):
str.append_chars(value);
$default:
$assert(false, "Unsupported type for append use printf instead.");
$endswitch
$endswitch
}
fn StringData* VarString.data(VarString str) @inline @private
{
return (StringData*)str;
}
fn void VarString.reserve(VarString* str, usz addition) @private
{
StringData* data = str.data();
if (!data)
{
*str = string::new_with_capacity(addition);
return;
}
usz len = data.len + addition;
if (data.capacity >= len) return;
usz new_capacity = data.capacity *= 2;
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
*str = (VarString)realloc(data, StringData.sizeof + new_capacity, .using = data.allocator);
}
fn VarString VarString.new_concat(VarString a, VarString b, Allocator* allocator = mem::heap())
{
VarString string = new_with_capacity(a.len() + b.len(), allocator);
string.append(a);
string.append(b);
return string;
}
struct StringData @private
{
Allocator* allocator;
usz len;
usz capacity;
char[*] chars;
}

View File

@@ -32,7 +32,7 @@ struct JsonParser
DString last_string;
double last_number;
char current;
anyerr current_err;
anyfault current_err;
bool skip_comments;
bool reached_end;
}
@@ -61,13 +61,13 @@ fn Object*! JsonParser.parse_from_token(JsonParser* this, JsonTokenType token)
case COMMA:
case RBRACE:
case RBRACKET:
case COLON: return JsonParsingError.UNEXPECTED_CHARACTER!;
case COLON: return JsonParsingError.UNEXPECTED_CHARACTER?;
case STRING: return object::new_string(this.last_string.str(), this.allocator);
case NUMBER: return object::new_float(this.last_number, this.allocator);
case TRUE: return object::new_bool(true);
case FALSE: return object::new_bool(false);
case NULL: return object::new_null();
case EOF: return JsonParsingError.EOF!;
case EOF: return JsonParsingError.EOF?;
}
unreachable();
}
@@ -85,17 +85,17 @@ fn JsonTokenType! JsonParser.lex_number(JsonParser* this, char c)
if (negate)
{
t.append(c);
c = this.read_next()?;
c = this.read_next()!;
}
while (c >= '0' && c <= '9')
{
t.append(c);
c = this.read_next()?;
c = this.read_next()!;
}
if (c == '.')
{
t.append(c);
while (c = this.read_next()?, c >= '0' && c <= '9')
while (c = this.read_next()!, c >= '0' && c <= '9')
{
t.append(c);
}
@@ -103,24 +103,24 @@ fn JsonTokenType! JsonParser.lex_number(JsonParser* this, char c)
if ((c | 32) == 'e')
{
t.append(c);
c = this.read_next()?;
c = this.read_next()!;
switch (c)
{
case '-':
case '+':
t.append(c);
c = this.read_next()?;
c = this.read_next()!;
}
if (c < '0' || c > '9') return JsonParsingError.INVALID_NUMBER!;
if (c < '0' || c > '9') return JsonParsingError.INVALID_NUMBER?;
while (c >= '0' && c <= '9')
{
t.append(c);
c = this.read_next()?;
c = this.read_next()!;
}
}
this.pushback();
double! d = str::to_double(t.str()) ?? JsonParsingError.INVALID_NUMBER!;
this.last_number = d?;
double! d = t.str().to_double() ?? JsonParsingError.INVALID_NUMBER?;
this.last_number = d!;
return NUMBER;
};
}
@@ -128,30 +128,30 @@ fn JsonTokenType! JsonParser.lex_number(JsonParser* this, char c)
fn Object*! JsonParser.parse_map(JsonParser* this)
{
Object* map = object::new_obj(this.allocator);
JsonTokenType token = this.advance()?;
JsonTokenType token = this.advance()!;
defer catch map.free();
DString temp_key = dstring::new_with_capacity(32, this.allocator);
defer temp_key.free();
while (token != JsonTokenType.RBRACE)
{
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER!;
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
DString string = this.last_string;
if (map.has_key(string.str())) return JsonParsingError.DUPLICATE_MEMBERS!;
if (map.has_key(string.str())) return JsonParsingError.DUPLICATE_MEMBERS?;
// Copy the key to our temp holder. We do this to work around the issue
// if the temp allocator should be used as the default allocator.
temp_key.clear();
temp_key.append(string);
this.parse_expected(COLON)?;
Object* element = this.parse_any()?;
this.parse_expected(COLON)!;
Object* element = this.parse_any()!;
map.set(temp_key.str(), element);
token = this.advance()?;
token = this.advance()!;
if (token == JsonTokenType.COMMA)
{
token = this.advance()?;
token = this.advance()!;
continue;
}
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER!;
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
return map;
}
@@ -160,18 +160,18 @@ fn Object*! JsonParser.parse_array(JsonParser* this)
{
Object* list = object::new_obj(this.allocator);
defer catch list.free();
JsonTokenType token = this.advance()?;
JsonTokenType token = this.advance()!;
while (token != JsonTokenType.RBRACKET)
{
Object* element = this.parse_from_token(token)?;
Object* element = this.parse_from_token(token)!;
list.append(element);
token = this.advance()?;
token = this.advance()!;
if (token == JsonTokenType.COMMA)
{
token = this.advance()?;
token = this.advance()!;
continue;
}
if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER!;
if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
return list;
}
@@ -191,7 +191,7 @@ fn char! JsonParser.read_next(JsonParser* this)
this.reached_end = true;
return '\0';
default:
return err!;
return err?;
}
if (c == 0)
{
@@ -204,7 +204,7 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
{
char c;
// Skip whitespace
while WS: (c = this.read_next()?)
while WS: (c = this.read_next()!)
{
switch (c)
{
@@ -218,7 +218,7 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
continue;
case '/':
if (!this.skip_comments) break;
c = this.read_next()?;
c = this.read_next()!;
if (c != '*')
{
this.pushback();
@@ -227,12 +227,12 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
while COMMENT: (1)
{
// Skip to */
while (c = this.read_next()?)
while (c = this.read_next()!)
{
if (c == '\n') this.line++;
if (c != '*') continue;
// Skip through all the '*'
while (c = this.read_next()?)
while (c = this.read_next()!)
{
if (c == '\n') this.line++;
if (c != '*') break;
@@ -248,7 +248,7 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
switch (c)
{
case '\0':
return IoError.EOF!;
return IoError.EOF?;
case '{':
return LBRACE;
case '}':
@@ -267,16 +267,16 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
case '0'..'9':
return this.lex_number(c);
case 't':
this.match("rue")?;
this.match("rue")!;
return TRUE;
case 'f':
this.match("alse")?;
this.match("alse")!;
return FALSE;
case 'n':
this.match("ull")?;
this.match("ull")!;
return NULL;
default:
return JsonParsingError.UNEXPECTED_CHARACTER!;
return JsonParsingError.UNEXPECTED_CHARACTER?;
}
}
@@ -284,14 +284,14 @@ fn void! JsonParser.match(JsonParser* this, String str)
{
foreach (c : str)
{
char l = this.read_next()?;
if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER!;
char l = this.read_next()!;
if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
}
fn void! JsonParser.parse_expected(JsonParser* this, JsonTokenType token) @local
{
if (this.advance()? != token) return JsonParsingError.UNEXPECTED_CHARACTER!;
if (this.advance()! != token) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
@@ -299,13 +299,13 @@ fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
this.last_string.clear();
while LOOP: (1)
{
char c = this.read_next()?;
char c = this.read_next()!;
switch (c)
{
case '\0':
return JsonParsingError.EOF!;
return JsonParsingError.EOF?;
case 1..31:
return JsonParsingError.UNEXPECTED_CHARACTER!;
return JsonParsingError.UNEXPECTED_CHARACTER?;
case '"':
break LOOP;
case '\\':
@@ -314,13 +314,13 @@ fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
this.last_string.append(c);
continue;
}
c = this.read_next()?;
c = this.read_next()!;
switch (c)
{
case '\0':
return JsonParsingError.EOF!;
return JsonParsingError.EOF?;
case 1..31:
return JsonParsingError.UNEXPECTED_CHARACTER!;
return JsonParsingError.UNEXPECTED_CHARACTER?;
case '"':
case '\\':
case '/':
@@ -339,14 +339,14 @@ fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
uint val;
for (int i = 0; i < 4; i++)
{
c = this.read_next()?;
if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE!;
c = this.read_next()!;
if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
val = val << 4 + (c > '9' ? (c | 32) - 'a' + 10 : c - '0');
}
this.last_string.append_char32(val);
continue;
default:
return JsonParsingError.INVALID_ESCAPE_SEQUENCE!;
return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
}
}
return STRING;

View File

@@ -3,7 +3,7 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::hash::fnv32a;
typedef Fnv32a = distinct uint;
def Fnv32a = distinct uint;
const FNV32A_START @private = 0x811c9dc5;
const FNV32A_MUL @private = 0x01000193;

View File

@@ -96,7 +96,7 @@ macro @blk(&block, i) @local
macro @blk0(&block, i) @local
{
$if (env::BIG_ENDIAN)
$if env::BIG_ENDIAN:
return block.l[i];
$else
return block.l[i] = (block.l[i].rotl(24) & 0xFF00FF00)

View File

@@ -25,6 +25,7 @@ fault IoError
FILE_IS_PIPE,
FILE_EOF,
INCOMPLETE_WRITE,
BUSY,
NO_PERMISSION,
OUT_OF_SPACE,
INVALID_PUSHBACK,
@@ -39,6 +40,7 @@ fault IoError
NOT_SEEKABLE,
NAME_TOO_LONG,
WOULD_BLOCK,
DIR_NOT_EMPTY,
INTERRUPTED,
GENERAL_ERROR,
UNKNOWN_ERROR,
@@ -55,18 +57,16 @@ macro void print(x)
var $Type = $typeof(x);
$switch ($Type)
$case String:
catch? stdout().print(x);
(void)stdout().print(x);
$case ZString:
catch? stdout().print(x.as_str());
(void)stdout().print(x.as_str());
$case DString:
catch? stdout().print(x.str());
$case VarString:
catch? stdout().print(x.str());
(void)stdout().print(x.str());
$default:
$if (@convertible(x, String))
catch? stdout().print((String)x);
$if @convertible(x, String):
(void)stdout().print((String)x);
$else
catch? stdout().printf("%s", x);
(void)stdout().printf("%s", x);
$endif
$endswitch
}
@@ -76,24 +76,20 @@ macro void printn(x = "")
var $Type = $typeof(x);
$switch ($Type)
$case String:
catch? stdout().printn(x);
(void)stdout().printn(x);
$case ZString:
catch? stdout().printn(x.as_str());
(void)stdout().printn(x.as_str());
$case DString:
catch? stdout().printn(x.str());
$case VarString:
catch? stdout().printn(x.str());
(void)stdout().printn(x.str());
$default:
$if (@convertible(x, String))
catch? stdout().printn((String)x);
$if @convertible(x, String):
(void)stdout().printn((String)x);
$else
catch? stdout().printfn("%s", x);
(void)stdout().printfn("%s", x);
$endif
$endswitch
}
macro void println(x = "") @deprecated => printn(x);
fn File stdout()
{
return { libc::stdout() };

View File

@@ -6,9 +6,9 @@ fn File! open(String filename, String mode)
return { .file = os::native_fopen(filename, mode) };
}
fn void! File.open(File* file, String filename, String mode) @deprecated
fn File! open_path(Path path, String mode)
{
file.file = os::native_fopen(filename, mode)?;
return { .file = os::native_fopen(path.as_str(), mode) };
}
/**
@@ -16,7 +16,7 @@ fn void! File.open(File* file, String filename, String mode) @deprecated
**/
fn void! File.reopen(File* file, String filename, String mode)
{
file.file = os::native_freopen(file.file, filename, mode)?;
file.file = os::native_freopen(file.file, filename, mode)!;
}
/**
@@ -24,7 +24,7 @@ fn void! File.reopen(File* file, String filename, String mode)
**/
fn usz! File.seek(File file, isz offset, Seek seek_mode = Seek.SET)
{
os::native_fseek(file.file, offset, seek_mode)?;
os::native_fseek(file.file, offset, seek_mode)!;
return os::native_ftell(file.file);
}
@@ -50,7 +50,7 @@ fn void! File.memopen(File* file, char[] data, String mode)
*/
fn void! File.putc(File *file, char c)
{
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF!;
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF?;
}
/**
@@ -63,8 +63,8 @@ fn void! File.close(File *file) @inline
switch (libc::errno())
{
case errno::ECONNRESET:
case errno::EBADF: return IoError.FILE_NOT_VALID!;
case errno::EINTR: return IoError.INTERRUPTED!;
case errno::EBADF: return IoError.FILE_NOT_VALID?;
case errno::EINTR: return IoError.INTERRUPTED?;
case errno::EDQUOT:
case errno::EFAULT:
case errno::EAGAIN:
@@ -72,8 +72,8 @@ fn void! File.close(File *file) @inline
case errno::ENETDOWN:
case errno::ENETUNREACH:
case errno::ENOSPC:
case errno::EIO: return IoError.INCOMPLETE_WRITE!;
default: return IoError.UNKNOWN_ERROR!;
case errno::EIO: return IoError.INCOMPLETE_WRITE?;
default: return IoError.UNKNOWN_ERROR?;
}
}
file.file = null;
@@ -109,10 +109,10 @@ fn usz! File.write(File file, char[] buffer)
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn usz! File.printn(File file, String string)
fn usz! File.printn(File file, String string = "")
{
usz len = file.print(string)?;
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR!;
usz len = file.print(string)!;
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR?;
return len + 1;
}
@@ -123,12 +123,10 @@ fn usz! File.printn(File file, String string)
fn usz! File.print(File file, String string)
{
usz len = string.len;
if (len != file.write((char[])string)?) return IoError.UNKNOWN_ERROR!;
if (len != file.write((char[])string)!) return IoError.UNKNOWN_ERROR?;
return len;
}
fn usz! File.println(File file, String string) @deprecated => file.printn(string);
/**
* @param [&in] file
* @require file.file `File must be initialized`
@@ -159,7 +157,7 @@ fn String File.tgetline(File* file)
fn char! File.getc(File* file)
{
int c = libc::fgetc(file.file);
if (c == -1) return IoError.FILE_EOF!;
if (c == -1) return IoError.FILE_EOF?;
return (char)c;
}

View File

@@ -6,17 +6,17 @@ const char[16] XDIGITS_L = "0123456789abcdef";
fn void! Formatter.left_adjust(Formatter* this, usz len) @local
{
if (!this.flags.left) return;
for (usz l = len; l < this.width; l++) this.out(' ')?;
for (usz l = len; l < this.width; l++) this.out(' ')!;
}
fn void! Formatter.right_adjust(Formatter* this, usz len) @local
{
if (this.flags.left) return;
for (usz l = len; l < this.width; l++) this.out(' ')?;
for (usz l = len; l < this.width; l++) this.out(' ')!;
}
fn uint128! int_from_variant(variant arg, bool *is_neg) @private
fn uint128! int_from_any(any arg, bool *is_neg) @private
{
*is_neg = false;
if (arg.type.kindof == TypeKind.POINTER)
@@ -25,7 +25,7 @@ fn uint128! int_from_variant(variant arg, bool *is_neg) @private
}
if (arg.type.kindof == TypeKind.DISTINCT)
{
return int_from_variant(variant { arg.ptr, arg.type.inner }, is_neg);
return int_from_any(any { arg.ptr, arg.type.inner }, is_neg);
}
switch (arg)
{
@@ -63,21 +63,21 @@ fn uint128! int_from_variant(variant arg, bool *is_neg) @private
double d = *arg;
return (uint128)((*is_neg = d < 0) ? -d : d);
default:
return PrintFault.INVALID_ARGUMENT_TYPE!;
return PrintFault.INVALID_ARGUMENT_TYPE?;
}
}
fn FloatType! float_from_variant(variant arg) @private
fn FloatType! float_from_any(any arg) @private
{
$if (env::F128_SUPPORT)
$if env::F128_SUPPORT:
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
$endif
$if (env::F16_SUPPORT)
$if env::F16_SUPPORT:
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
$endif
if (arg.type.kindof == TypeKind.DISTINCT)
{
return float_from_variant(variant { arg.ptr, arg.type.inner });
return float_from_any(any { arg.ptr, arg.type.inner });
}
switch (arg)
{
@@ -108,7 +108,7 @@ fn FloatType! float_from_variant(variant arg) @private
case double:
return (FloatType)*arg;
default:
return PrintFault.INVALID_ARGUMENT_TYPE!;
return PrintFault.INVALID_ARGUMENT_TYPE?;
}
}
@@ -141,7 +141,7 @@ fn void! Formatter.out_substr(Formatter *this, String str) @private
usz l = conv::utf8_codepoints(str);
uint prec = this.prec;
if (this.flags.precision && l < prec) l = prec;
this.right_adjust(' ')?;
this.right_adjust(' ')!;
usz index = 0;
usz chars = str.len;
char* ptr = str.ptr;
@@ -150,7 +150,7 @@ fn void! Formatter.out_substr(Formatter *this, String str) @private
char c = ptr[index];
// Break if we have precision set and we ran out...
if (c & 0xC0 != 0x80 && this.flags.precision && !prec--) break;
this.out(c)?;
this.out(c)!;
index++;
}
return this.left_adjust(l);
@@ -158,7 +158,7 @@ fn void! Formatter.out_substr(Formatter *this, String str) @private
fn void! Formatter.pad(Formatter* this, char c, isz width, isz len) @inline
{
for (isz i = len; i < width; i++) this.out(c)?;
for (isz i = len; i < width; i++) this.out(c)!;
}
fn char* fmt_u(uint128 x, char* s)
@@ -170,7 +170,7 @@ fn char* fmt_u(uint128 x, char* s)
fn void! Formatter.out_chars(Formatter* this, char[] s)
{
foreach (c : s) this.out(c)?;
foreach (c : s) this.out(c)!;
}
enum FloatFormatting
@@ -203,12 +203,12 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
if (!math::is_finite(y))
{
// Add padding
if (!this.flags.left) this.pad(' ', this.width, 3 + pl)?;
if (!this.flags.left) this.pad(' ', this.width, 3 + pl)!;
String s = this.flags.uppercase ? "INF" : "inf";
if (y != y) this.flags.uppercase ? "NAN" : "nan";
if (pl) this.out(is_neg ? '-' : '+')?;
this.out_chars(s)?;
if (this.flags.left) this.pad(' ', this.width, 3 + pl)?;
if (pl) this.out(is_neg ? '-' : '+')!;
this.out_chars(s)!;
if (this.flags.left) this.pad(' ', this.width, 3 + pl)!;
return;
}
// Rescale
@@ -260,18 +260,18 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
} while (y);
isz outlen = s - buf;
isz explen = ebuf - estr;
if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
usz l = p && outlen - 2 < p
? p + 2 + explen
: outlen + explen;
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)?;
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')?;
this.out_chars(this.flags.uppercase ? "0X" : "0x")?;
if (this.flags.zeropad) this.pad('0', this.width, pl + l)?;
this.out_chars(buf[:outlen])?;
this.pad('0', l - outlen - explen, 0)?;
this.out_chars(estr[:explen])?;
if (this.flags.left) this.pad(' ', this.width, pl + l)?;
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
this.out_chars(this.flags.uppercase ? "0X" : "0x")!;
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
this.out_chars(buf[:outlen])!;
this.pad('0', l - outlen - explen, 0)!;
this.out_chars(estr[:explen])!;
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
return;
}
if (p < 0) p = 6;
@@ -427,12 +427,12 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
}
}
}
if (p > int.max - 1 - (isz)(p || this.flags.hash)) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (p > int.max - 1 - (isz)(p || this.flags.hash)) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
int l = (int)(1 + p + (isz)(p || this.flags.hash));
char* estr @noinit;
if (formatting == FLOAT)
{
if (e > int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (e > int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (e > 0) l += e;
}
else
@@ -441,13 +441,13 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
while (ebuf - estr < 2) (--estr)[0] = '0';
*--estr = (e < 0 ? '-' : '+');
*--estr = this.flags.uppercase ? 'E' : 'e';
if (ebuf - estr > (isz)int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (ebuf - estr > (isz)int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
l += (int)(ebuf - estr);
}
if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)?;
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')?;
if (this.flags.zeropad) this.pad('0', this.width, pl + l)?;
if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
if (formatting == FLOAT)
{
if (a > r) a = r;
@@ -462,16 +462,16 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
case s == buf + 9:
*--s = '0';
}
this.out_chars(s[:buf + 9 - s])?;
this.out_chars(s[:buf + 9 - s])!;
}
if (p || this.flags.hash) this.out('.')?;
if (p || this.flags.hash) this.out('.')!;
for (; d < z && p > 0; d++, p -= 9)
{
char* s = fmt_u(*d, buf + 9);
while (s > buf) *--s = '0';
this.out_chars(s[:math::min((isz)9, p)])?;
this.out_chars(s[:math::min((isz)9, p)])!;
}
this.pad('0', p + 9, 9)?;
this.pad('0', p + 9, 9)!;
}
else
{
@@ -486,17 +486,17 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
}
else
{
this.out(s++[0])?;
if (p > 0 || this.flags.hash) this.out('.')?;
this.out(s++[0])!;
if (p > 0 || this.flags.hash) this.out('.')!;
}
this.out_chars(s[:math::min(buf + 9 - s, p)])?;
this.out_chars(s[:math::min(buf + 9 - s, p)])!;
p -= buf + 9 - s;
}
this.pad('0', p + 18, 18)?;
this.out_chars(estr[:ebuf - estr])?;
this.pad('0', p + 18, 18)!;
this.out_chars(estr[:ebuf - estr])!;
}
if (this.flags.left) this.pad(' ', this.width, pl + l)?;
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
return;
}
@@ -515,7 +515,7 @@ fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base
char past_10 = (this.flags.uppercase ? 'A' : 'a') - 10;
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
char digit = (char)(value % base);
buf[len++] = digit + (digit < 10 ? '0' : past_10);
value /= base;
@@ -533,12 +533,12 @@ fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negati
if (this.width && this.flags.zeropad && (negative || this.flags.plus || this.flags.space)) this.width--;
while (len < this.prec)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '0';
}
while (this.flags.zeropad && len < this.width)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '0';
}
}
@@ -553,7 +553,7 @@ fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negati
}
if (base != 10)
{
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
switch (base)
{
case 16:
@@ -572,13 +572,13 @@ fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negati
switch (true)
{
case negative:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '-';
case this.flags.plus:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '+';
case this.flags.space:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = ' ';
}
if (!len) return;
@@ -586,36 +586,36 @@ fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negati
}
fn void! Formatter.ntoa_variant(Formatter* this, variant arg, uint base) @private
fn void! Formatter.ntoa_any(Formatter* this, any arg, uint base) @private
{
bool is_neg;
uint128 val = int_from_variant(arg, &is_neg)!!;
uint128 val = int_from_any(arg, &is_neg)!!;
return this.ntoa(val, is_neg, base) @inline;
}
fn void! Formatter.out_char(Formatter* this, variant arg) @private
fn void! Formatter.out_char(Formatter* this, any arg) @private
{
uint l = 1;
// pre padding
this.right_adjust(l)?;
this.right_adjust(l)!;
// char output
Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD;
Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD;
switch (true)
{
case c < 0x7f:
this.out((char)c)?;
this.out((char)c)!;
case c < 0x7ff:
this.out((char)(0xC0 | c >> 6))?;
this.out((char)(0x80 | (c & 0x3F)))?;
this.out((char)(0xC0 | c >> 6))!;
this.out((char)(0x80 | (c & 0x3F)))!;
case c < 0xffff:
this.out((char)(0xE0 | c >> 12))?;
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
this.out((char)(0x80 | (c & 0x3F)))?;
this.out((char)(0xE0 | c >> 12))!;
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
this.out((char)(0x80 | (c & 0x3F)))!;
default:
this.out((char)(0xF0 | c >> 18))?;
this.out((char)(0x80 | (c >> 12 & 0x3F)))?;
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
this.out((char)(0x80 | (c & 0x3F)))?;
this.out((char)(0xF0 | c >> 18))!;
this.out((char)(0x80 | (c >> 12 & 0x3F)))!;
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
this.out((char)(0x80 | (c & 0x3F)))!;
}
return this.left_adjust(l);
}
@@ -630,11 +630,11 @@ fn void! Formatter.out_reverse(Formatter* this, char[] buf) @private
{
for (usz i = len; i < this.width; i++)
{
this.out(' ')?;
this.out(' ')!;
}
}
// reverse string
while (len) this.out(buf[--len])?;
while (len) this.out(buf[--len])!;
// append pad spaces up to given width
return this.left_adjust(this.idx - buffer_start_idx);
@@ -643,25 +643,25 @@ fn void! Formatter.out_reverse(Formatter* this, char[] buf) @private
fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private
{
usz val = ++(*index_ptr);
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!;
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?;
}
fn variant! next_variant(variant* args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
fn any! next_any(any* args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
{
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG!;
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG?;
return args_ptr[(*arg_index_ptr)++];
}
fn int! printf_parse_format_field(
variant* args_ptr, usz args_len, usz* args_index_ptr,
any* args_ptr, usz args_len, usz* args_index_ptr,
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
{
char c = format_ptr[*index_ptr];
if (c >= '0' && c <= '9') return simple_atoi(format_ptr, format_len, index_ptr);
if (c != '*') return 0;
printf_advance_format(format_len, index_ptr)?;
variant val = next_variant(args_ptr, args_len, args_index_ptr)?;
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG!;
uint! intval = types::variant_to_int(val, int);
return intval ?? FormattingFault.INVALID_WIDTH_ARG!;
printf_advance_format(format_len, index_ptr)!;
any val = next_any(args_ptr, args_len, args_index_ptr)!;
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?;
uint! intval = types::any_to_int(val, int);
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;
}

View File

@@ -21,10 +21,11 @@ fault FormattingFault
INVALID_FORMAT_TYPE,
}
typedef OutputFn = fn void!(char c, void* buffer);
typedef ToStringFunction = fn String(void* value, Allocator *using);
typedef ToFormatFunction = fn void!(void* value, Formatter* formatter);
typedef FloatType = double;
def OutputFn = fn void!(char c, void* buffer);
def FloatType = double;
fn String any.to_string(void* value, Allocator *using) @interface;
fn void! any.to_format(void* value, Formatter* formatter) @interface;
fn usz! printf(String format, args...) @maydiscard
{
@@ -33,39 +34,21 @@ fn usz! printf(String format, args...) @maydiscard
return formatter.vprintf(format, args);
}
define printfln = printfn;
fn usz! printfn(String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
usz len = formatter.vprintf(format, args)?;
usz len = formatter.vprintf(format, args)!;
putchar('\n');
return len + 1;
}
fn usz! VarString.printf(VarString* str, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
return formatter.vprintf(format, args);
}
fn usz! VarString.printfn(VarString* str, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
usz len = formatter.vprintf(format, args)?;
str.append('\n');
return len + 1;
}
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
{
Formatter formatter;
BufferData data = { .buffer = buffer };
formatter.init(&out_buffer_fn, &data);
usz size = formatter.vprintf(format, args)?;
usz size = formatter.vprintf(format, args)!;
return buffer[:data.written];
}
@@ -73,15 +56,15 @@ fn usz! File.printf(File file, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_fputchar_fn, &file);
return formatter.vprintf(format, args)?;
return formatter.vprintf(format, args)!;
}
fn usz! File.printfn(File file, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_fputchar_fn, &file);
usz len = formatter.vprintf(format, args)?;
file.putc('\n')?;
usz len = formatter.vprintf(format, args)!;
file.putc('\n')!;
file.flush();
return len + 1;
}
@@ -120,49 +103,14 @@ fn void Formatter.init(Formatter* this, OutputFn out_fn, void* data = null)
*this = { .data = data, .out_fn = out_fn};
}
/**
* @require $checks($Type a, a.to_string()) || $checks($Type a, a.to_format(&&Formatter{})) "Expected a type with 'to_string' or 'to_format' defined"
* @require !$checks($Type a, a.to_string()) || $checks($Type a, a.to_string(&&Allocator{})) "Expected 'to_string' to take an allocator as argument."
* @require !$checks($Type a, a.to_format(&&Formatter{})) || $checks($Type a, Formatter b, a.to_format(&b)) "Expected 'to_format' to take a Formatter as argument."
*/
macro void formatter_register_type($Type)
{
$if ($checks($Type a, a.to_format(&&Formatter {})))
if (!toformat_functions.table.len)
{
toformat_functions.init(64);
}
toformat_functions.set($Type.typeid, (ToFormatFunction)&$Type.to_format);
$else
if (!tostring_functions.table.len)
{
tostring_functions.init(64);
}
tostring_functions.set($Type.typeid, (ToStringFunction)&$Type.to_string);
$endif
}
static initialize @priority(101)
{
if (!toformat_functions.table.len)
{
toformat_functions.init(64);
}
if (!tostring_functions.table.len)
{
tostring_functions.init(64);
}
}
fn void! Formatter.out(Formatter* this, char c) @private
{
this.out_fn(c, this.data)?;
this.out_fn(c, this.data)!;
}
macro bool! Formatter.print_with_function(Formatter* this, variant arg)
macro bool! Formatter.print_with_function(Formatter* this, any arg)
{
if (try to_format = toformat_functions.get(arg.type))
if (&arg.to_format)
{
PrintFlags old = this.flags;
uint old_width = this.width;
@@ -173,10 +121,10 @@ macro bool! Formatter.print_with_function(Formatter* this, variant arg)
this.width = old_width;
this.prec = old_prec;
}
to_format(arg.ptr, this)?;
arg.to_format(this)!;
return true;
}
if (try to_string = tostring_functions.get(arg.type))
}
if (&arg.to_string)
{
PrintFlags old = this.flags;
uint old_width = this.width;
@@ -187,98 +135,94 @@ macro bool! Formatter.print_with_function(Formatter* this, variant arg)
this.width = old_width;
this.prec = old_prec;
}
@pool()
@stack_mem(512; Allocator* mem)
{
this.out_substr(to_string(arg.ptr, mem::temp()))?;
this.out_substr(arg.to_string(mem))!;
return true;
};
}
return false;
}
fn void! Formatter.out_str(Formatter* this, variant arg) @private
fn void! Formatter.out_str(Formatter* this, any arg) @private
{
switch (arg.type.kindof)
{
case TYPEID:
return this.out_substr("<typeid>");
return this.out_substr("typeid");
case VOID:
return this.out_substr("void");
case ANYERR:
case ANYFAULT:
case FAULT:
return this.out_substr((*(anyerr*)arg.ptr).nameof);
case VARIANT:
return this.out_str(*(variant*)arg);
return this.out_substr((*(anyfault*)arg.ptr).nameof);
case ANY:
return this.out_str(*(any*)arg);
case ENUM:
if (this.print_with_function(arg)?) return;
return this.out_substr(arg.type.names[types::variant_to_int(arg, usz)!!]);
if (this.print_with_function(arg)!) return;
return this.out_substr(arg.type.names[types::any_to_int(arg, usz)!!]);
case STRUCT:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
return this.out_substr("<struct>");
case UNION:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
return this.out_substr("<union>");
case BITSTRUCT:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
return this.out_substr("<bitstruct>");
case FUNC:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
return this.out_substr("<function>");
case OPTIONAL:
unreachable();
case DISTINCT:
if (this.print_with_function(arg)?) return;
if (arg.type == VarString.typeid)
{
return this.out_substr(((VarString*)arg).str());
}
if (this.print_with_function(arg)!) return;
if (arg.type == DString.typeid)
{
return this.out_substr(((DString*)arg).str());
}
return this.out_str(variant { arg.ptr, arg.type.inner });
return this.out_str(any { arg.ptr, arg.type.inner });
case POINTER:
if (this.print_with_function(arg)?) return;
return this.ntoa_variant(arg, 16);
if (this.print_with_function(arg)!) return;
return this.ntoa_any(arg, 16);
case SIGNED_INT:
case UNSIGNED_INT:
return this.ntoa_variant(arg, 10);
return this.ntoa_any(arg, 10);
case FLOAT:
return this.ftoa(float_from_variant(arg)!!);
return this.ftoa(float_from_any(arg)!!);
case ARRAY:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz len = arg.type.len;
// Pretend this is a String
void* ptr = (void*)arg.ptr;
this.out('[')?;
this.out('[')!;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")?;
this.out_str(variant { ptr, inner })?;
if (i != 0) this.out_substr(", ")!;
this.out_str(any { ptr, inner })!;
ptr += size;
}
return this.out(']');
case VECTOR:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz len = arg.type.len;
// Pretend this is a String
void* ptr = (void*)arg.ptr;
this.out_substr("[<")?;
this.out_substr("[<")!;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")?;
this.out_str(variant { ptr, inner })?;
if (i != 0) this.out_substr(", ")!;
this.out_str(any { ptr, inner })!;
ptr += size;
}
return this.out_substr(">]");
case SUBARRAY:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
// this is SomeType[] so grab the "SomeType"
typeid inner = arg.type.inner;
if (inner == char.typeid)
@@ -290,25 +234,18 @@ fn void! Formatter.out_str(Formatter* this, variant arg) @private
String* temp = (void*)arg.ptr;
void* ptr = (void*)temp.ptr;
usz len = temp.len;
this.out('[')?;
this.out('[')!;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")?;
this.out_str(variant { ptr, inner })?;
if (i != 0) this.out_substr(", ")!;
this.out_str(any { ptr, inner })!;
ptr += size;
}
this.out(']')?;
this.out(']')!;
case BOOL:
if (*(bool*)arg.ptr)
{
return this.out_substr("true");
}
else
{
return this.out_substr("false");
}
return this.out_substr(*(bool*)arg.ptr ? "true" : "false");
default:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
return this.out_substr("Invalid type");
}
}
@@ -318,7 +255,7 @@ fn void! Formatter.out_str(Formatter* this, variant arg) @private
fn void! out_buffer_fn(char c, void *data) @private
{
BufferData *buffer_data = data;
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED!;
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?;
buffer_data.buffer[buffer_data.written++] = c;
}
@@ -334,16 +271,9 @@ fn void! out_putchar_fn(char c, void* data @unused) @private
fn void! out_fputchar_fn(char c, void* data) @private
{
File* f = data;
f.putc(c)?;
f.putc(c)!;
}
fn void! out_string_append_fn(char c, void* data) @private
{
VarString* s = data;
s.append_char(c);
}
struct BufferData @private
{
char[] buffer;
@@ -351,7 +281,7 @@ struct BufferData @private
}
fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
fn usz! Formatter.vprintf(Formatter* this, String format, any[] anys)
{
if (!this.out_fn)
{
@@ -367,15 +297,15 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
if (c != '%')
{
// no
this.out(c)?;
this.out(c)!;
continue;
}
i++;
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
c = format[i];
if (c == '%')
{
this.out(c)?;
this.out(c)!;
continue;
}
// evaluate flags
@@ -391,11 +321,11 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
case '#': this.flags.hash = true;
default: break FLAG_EVAL;
}
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
c = format[i];
}
// evaluate width field
int w = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
c = format[i];
if (w < 0)
{
@@ -408,16 +338,16 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
if (c == '.')
{
this.flags.precision = true;
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
int prec = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
int prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
this.prec = prec < 0 ? 0 : prec;
c = format[i];
}
// evaluate specifier
uint base = 0;
if (variant_index >= variants.len) return PrintFault.MISSING_ARG!;
variant current = variants[variant_index++];
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
any current = anys[variant_index++];
switch (c)
{
case 'd':
@@ -442,38 +372,38 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
this.flags.uppercase = true;
nextcase;
case 'a':
this.atoa(float_from_variant(current)!!)?;
this.atoa(float_from_any(current)!!)!;
continue;
case 'F' :
this.flags.uppercase = true;
nextcase;
case 'f':
this.ftoa(float_from_variant(current)!!)?;
this.ftoa(float_from_any(current)!!)!;
continue;
case 'E':
this.flags.uppercase = true;
nextcase;
case 'e':
this.etoa(float_from_variant(current)!!)?;
this.etoa(float_from_any(current)!!)!;
continue;
case 'G':
this.flags.uppercase = true;
nextcase;
case 'g':
this.gtoa(float_from_variant(current)!!)?;
this.gtoa(float_from_any(current)!!)!;
continue;
case 'c':
this.out_char(current)?;
this.out_char(current)!;
continue;
case 's':
this.out_str(current)?;
this.out_str(current)!;
continue;
case 'p':
this.flags.zeropad = true;
this.flags.hash = true;
base = 16;
default:
return PrintFault.INVALID_FORMAT_STRING!;
return PrintFault.INVALID_FORMAT_STRING?;
}
if (base != 10)
{
@@ -484,9 +414,9 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
if (this.flags.precision) this.flags.zeropad = false;
bool is_neg;
uint128 v = int_from_variant(current, &is_neg)!!;
uint128 v = int_from_any(current, &is_neg)!!;
this.ntoa(v, is_neg, base)?;
this.ntoa(v, is_neg, base)!;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
@@ -495,8 +425,3 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
return this.idx;
}
typedef StringFunctionMap @private = HashMap<typeid, ToStringFunction>;
typedef FormatterFunctionMap @private = HashMap<typeid, ToFormatFunction>;
FormatterFunctionMap toformat_functions @private;
StringFunctionMap tostring_functions @private;

View File

@@ -1,18 +1,18 @@
module std::io;
typedef CloseStreamFn = fn void!(Stream*);
typedef FlushStreamFn = fn void!(Stream*);
typedef SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek);
typedef LenStreamFn = fn usz(Stream*);
typedef AvailableStreamFn = fn usz(Stream*);
typedef ReadStreamFn = fn usz!(Stream*, char[] bytes);
typedef ReadFromStreamFn = fn usz!(Stream*, Stream*);
typedef ReadByteStreamFn = fn char!(Stream*);
typedef PushbackByteStreamFn = fn void!(Stream*);
typedef WriteStreamFn = fn usz!(Stream*, char[] bytes);
typedef WriteToStreamFn = fn usz!(Stream*, Stream* out);
typedef WriteByteStreamFn = fn void!(Stream*, char c);
typedef DestroyStreamFn = fn void!(Stream*);
def CloseStreamFn = fn void!(Stream*);
def FlushStreamFn = fn void!(Stream*);
def SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek);
def LenStreamFn = fn usz(Stream*);
def AvailableStreamFn = fn usz(Stream*);
def ReadStreamFn = fn usz!(Stream*, char[] bytes);
def ReadFromStreamFn = fn usz!(Stream*, Stream*);
def ReadByteStreamFn = fn char!(Stream*);
def PushbackByteStreamFn = fn void!(Stream*);
def WriteStreamFn = fn usz!(Stream*, char[] bytes);
def WriteToStreamFn = fn usz!(Stream*, Stream* out);
def WriteByteStreamFn = fn void!(Stream*, char c);
def DestroyStreamFn = fn void!(Stream*);
struct StreamInterface
{
@@ -60,7 +60,7 @@ fn void! Stream.close(Stream* s) @inline @maydiscard
fn usz! Stream.seek(Stream* s, isz offset, Seek seek) @inline
{
if (SeekStreamFn func = s.fns.seek_fn) return func(s, offset, seek);
return IoError.NOT_SEEKABLE!;
return IoError.NOT_SEEKABLE?;
}
fn usz! Stream.available(Stream* s) @inline
@@ -68,12 +68,12 @@ fn usz! Stream.available(Stream* s) @inline
if (AvailableStreamFn func = s.fns.available_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
usz curr = func(s, 0, Seek.CURSOR)?;
usz len = func(s, 0, Seek.END)?;
func(s, curr, Seek.SET)?;
usz curr = func(s, 0, Seek.CURSOR)!;
usz len = func(s, 0, Seek.END)!;
func(s, curr, Seek.SET)!;
return len - curr;
}
return IoError.NOT_SEEKABLE!;
return IoError.NOT_SEEKABLE?;
}
fn usz! Stream.read(Stream* s, char[] buffer)
@@ -88,19 +88,19 @@ fn usz! Stream.read(Stream* s, char[] buffer)
if (catch err = c)
{
case IoError.EOF: return len;
default: return err!;
default: return err?;
}
*cptr = c;
len++;
}
}
return IoError.UNSUPPORTED_OPERATION!;
return IoError.UNSUPPORTED_OPERATION?;
}
fn char! Stream.read_byte(Stream* s) @inline
{
if (ReadByteStreamFn func = s.fns.read_byte_fn) return func(s);
return IoError.UNSUPPORTED_OPERATION!;
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.write(Stream* s, char[] bytes) @inline
@@ -108,34 +108,34 @@ fn usz! Stream.write(Stream* s, char[] bytes) @inline
if (WriteStreamFn func = s.fns.write_fn) return func(s, bytes);
if (WriteByteStreamFn func = s.fns.write_byte_fn)
{
foreach (c : bytes) func(s, c)?;
foreach (c : bytes) func(s, c)!;
return bytes.len;
}
return IoError.UNSUPPORTED_OPERATION!;
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.write_byte(Stream* s, char b) @inline
{
if (WriteByteStreamFn func = s.fns.write_byte_fn) return func(s, b);
return IoError.UNSUPPORTED_OPERATION!;
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.write_to(Stream* s, Stream* to) @inline
{
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, to);
return IoError.UNSUPPORTED_OPERATION!;
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.read_from(Stream* s, Stream* from) @inline
{
if (ReadFromStreamFn func = s.fns.read_stream_fn) return func(s, from);
return IoError.UNSUPPORTED_OPERATION!;
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.flush(Stream* s) @inline @maydiscard
{
if (FlushStreamFn func = s.fns.flush_fn) return func(s);
return IoError.UNSUPPORTED_OPERATION!;
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.len(Stream* s) @inline
@@ -143,12 +143,12 @@ fn usz! Stream.len(Stream* s) @inline
if (LenStreamFn func = s.fns.len_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
usz curr = func(s, 0, Seek.CURSOR)?;
usz len = func(s, 0, Seek.END)?;
func(s, curr, Seek.SET)?;
usz curr = func(s, 0, Seek.CURSOR)!;
usz len = func(s, 0, Seek.END)!;
func(s, curr, Seek.SET)!;
return len;
}
return IoError.NOT_SEEKABLE!;
return IoError.NOT_SEEKABLE?;
}
fn void! Stream.pushback_byte(Stream* s) @inline
@@ -156,13 +156,13 @@ fn void! Stream.pushback_byte(Stream* s) @inline
if (PushbackByteStreamFn func = s.fns.pushback_byte_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
func(s, -1, CURSOR)?;
func(s, -1, CURSOR)!;
return;
}
return IoError.UNSUPPORTED_OPERATION!;
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.write_string(Stream* s, String str) @inline => (void)(s.write((char[])str)?);
fn void! Stream.write_string(Stream* s, String str) @inline => (void)(s.write((char[])str)!);
fn usz! Stream.copy_to(Stream* s, Stream* dst, char[] buffer = {})
{
@@ -195,11 +195,11 @@ macro usz! copy_through_buffer(Stream* s, Stream* dst, char[] buffer) @local
if (catch err = len)
{
case IoError.EOF: return total_copied;
default: return err!;
default: return err?;
}
if (!len) return total_copied;
usz written = dst.write(buffer[:len])?;
usz written = dst.write(buffer[:len])!;
total_copied += len;
if (written != len) return IoError.INCOMPLETE_WRITE!;
if (written != len) return IoError.INCOMPLETE_WRITE?;
}
}

View File

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

View File

@@ -1,33 +1,33 @@
module std::io::os;
import libc;
typedef FopenFn = fn void*!(String, String);
typedef FreopenFn = fn void*!(void*, String, String);
typedef FcloseFn = fn void!(void*);
typedef FseekFn = fn void!(void*, isz, Seek);
typedef FtellFn = fn usz!(void*);
typedef FwriteFn = fn usz!(void*, char[] buffer);
typedef FreadFn = fn usz!(void*, char[] buffer);
def FopenFn = fn void*!(String, String);
def FreopenFn = fn void*!(void*, String, String);
def FcloseFn = fn void!(void*);
def FseekFn = fn void!(void*, isz, Seek);
def FtellFn = fn usz!(void*);
def FwriteFn = fn usz!(void*, char[] buffer);
def FreadFn = fn usz!(void*, char[] buffer);
$if (!$defined(native_fopen_fn))
$if !$defined(native_fopen_fn):
FopenFn native_fopen_fn @weak;
$endif
$if (!$defined(native_fclose_fn))
$if !$defined(native_fclose_fn):
FcloseFn native_fclose_fn @weak;
$endif
$if (!$defined(native_freopen_fn))
$if !$defined(native_freopen_fn):
FreopenFn native_freopen_fn @weak;
$endif
$if (!$defined(native_fseek_fn))
$if !$defined(native_fseek_fn):
FseekFn native_fseek_fn @weak;
$endif
$if (!$defined(native_ftell_fn))
$if !$defined(native_ftell_fn):
FtellFn native_ftell_fn @weak;
$endif
$if (!$defined(native_fwrite_fn))
$if !$defined(native_fwrite_fn):
FwriteFn native_fwrite_fn @weak;
$endif
$if (!$defined(native_fread_fn))
$if !$defined(native_fread_fn):
FreadFn native_fread_fn @weak;
$endif
@@ -38,18 +38,18 @@ $endif
**/
fn void*! native_fopen(String filename, String mode) @inline
{
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fopen_fn) return native_fopen_fn(filename, mode);
unreachable("Tried to call fopen without support.");
$else
@pool()
{
$if (env::os_is_win32())
void* file = (CFile)_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())?;
$if env::os_is_win32():
void* file = (CFile)_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())!;
$else
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
$endif
return file ?: file_open_errno()!;
return file ?: file_open_errno()?;
};
$endif
}
@@ -60,56 +60,56 @@ $endif
**/
fn void*! native_freopen(void* file, String filename, String mode) @inline
{
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_freopen_fn) return native_freopen_fn(file, filename, mode);
unreachable("Tried to call freopen without support.");
$else
@pool()
{
$if (env::os_is_win32())
file = _wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)?;
$if env::os_is_win32():
file = _wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)!;
$else
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
$endif
return file ?: file_open_errno()!;
return file ?: file_open_errno()?;
};
$endif
}
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
{
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
unreachable("Tried to call fseek without support.");
$else
$if (env::os_is_win32())
$if env::os_is_win32():
bool success = _fseeki64(file, (long)offset, (int)seek_mode) == 0;
$else
bool success = libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode) == 0;
$endif
if (!success) return file_seek_errno()!;
if (!success) return file_seek_errno()?;
$endif
}
fn usz! native_ftell(CFile file) @inline
{
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_ftell_fn) return native_ftell_fn(file);
unreachable("Tried to call ftell without support.");
$else
$if (env::os_is_win32())
$if env::os_is_win32():
long index = _ftelli64(file);
return index >= 0 ? index : file_seek_errno()!;
return index >= 0 ? index : file_seek_errno()?;
$else
SeekIndex index = libc::ftell(file);
return index >= 0 ? index : file_seek_errno()!;
return index >= 0 ? index : file_seek_errno()?;
$endif
$endif
}
fn usz! native_fwrite(CFile file, char[] buffer) @inline
{
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fwrite_fn) return native_fwrite_fn(file, buffer);
unreachable("Tried to call fwrite without support.");
$else
@@ -119,7 +119,7 @@ $endif
fn usz! native_fread(CFile file, char[] buffer) @inline
{
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fread_fn) return native_fread_fn(file, buffer);
unreachable("Tried to call fread without support.");
$else
@@ -127,7 +127,7 @@ $else
$endif
}
macro anyerr file_open_errno() @local
macro anyfault file_open_errno() @local
{
switch (libc::errno())
{
@@ -155,7 +155,7 @@ macro anyerr file_open_errno() @local
}
}
macro anyerr file_seek_errno() @local
macro anyfault file_seek_errno() @local
{
switch (libc::errno())
{
@@ -175,13 +175,13 @@ macro anyerr file_seek_errno() @local
}
// Win functions
$if (env::os_is_win32())
$if env::os_is_win32():
extern fn void* _wfopen(Char16*, Char16*) @local;
extern fn void* _wfreopen(Char16*, Char16*, CFile) @local;
extern fn int _fseeki64(CFile, long, int) @local;
extern fn long _ftelli64(CFile) @local;
$endif
$if (env::os_is_posix())
$if env::os_is_posix():
extern fn CInt access(ZString path, CInt mode);
$endif

View File

@@ -1,8 +1,7 @@
module std::io::file::os;
import libc;
$if (env::os_is_darwin())
$if env::os_is_darwin():
struct DarwinTimespec @private
{
@@ -44,26 +43,26 @@ const S_IFSOCK = 0o140000; // socket
fn usz! native_file_size(String path)
{
Darwin64Stat stat;
read_stat(&stat, path)?;
read_stat(&stat, path)!;
return stat.st_size;
}
fn bool native_file_or_dir_exists(String path)
{
Darwin64Stat stat;
return try? read_stat(&stat, path);
return @ok(read_stat(&stat, path));
}
fn bool native_is_file(String path)
{
Darwin64Stat stat;
return try? read_stat(&stat, path) && (stat.st_mode & S_IFREG);
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFREG;
}
fn bool native_is_dir(String path)
{
Darwin64Stat stat;
return try? read_stat(&stat, path) && (stat.st_mode & S_IFDIR);
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFDIR;
}
fn void! read_stat(Darwin64Stat* stat, String path) @local
@@ -76,25 +75,25 @@ fn void! read_stat(Darwin64Stat* stat, String path) @local
switch (libc::errno())
{
case errno::EBADF:
return IoError.FILE_NOT_VALID!;
return IoError.FILE_NOT_VALID?;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return IoError.GENERAL_ERROR!;
return IoError.GENERAL_ERROR?;
case errno::EACCES:
return IoError.NO_PERMISSION!;
return IoError.NO_PERMISSION?;
case errno::ELOOP:
return IoError.NO_PERMISSION!;
return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG:
return IoError.NAME_TOO_LONG!;
return IoError.NAME_TOO_LONG?;
case errno::ENOENT:
return IoError.FILE_NOT_FOUND!;
return IoError.FILE_NOT_FOUND?;
case errno::ENOTDIR:
return IoError.FILE_NOT_DIR!;
return IoError.FILE_NOT_DIR?;
case errno::EOVERFLOW:
return IoError.GENERAL_ERROR!;
return IoError.GENERAL_ERROR?;
default:
return IoError.UNKNOWN_ERROR!;
return IoError.UNKNOWN_ERROR?;
}
}
};

View File

@@ -1,7 +1,7 @@
module std::io::file::os;
// native_temp_directory, for non Win32
$if (!env::os_is_win32())
$if !env::os_is_win32():
fn Path! native_temp_directory(Allocator* using = mem::heap())
{
@@ -13,10 +13,11 @@ fn Path! native_temp_directory(Allocator* using = mem::heap())
return path::new("/tmp", using);
}
$if (env::COMPILER_LIBC_AVAILABLE)
$if env::COMPILER_LIBC_AVAILABLE:
extern fn void* opendir(ZString);
extern fn void closedir(void*);
extern fn int remove(ZString);
const DT_UNKNOWN = 0;
const DT_FIFO = 1;
@@ -34,7 +35,7 @@ fn PathList! native_readdir(Path dir, bool no_dirs, bool no_symlinks, String mas
list.init(.using = using);
void* directory = opendir(dir.as_str() ? dir.as_zstr() : (ZString)".");
defer if (directory) closedir(directory);
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)!;
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
NativeDirentry* entry;
while ((entry = readdir(directory)))
{
@@ -48,20 +49,51 @@ fn PathList! native_readdir(Path dir, bool no_dirs, bool no_symlinks, String mas
return list;
}
/**
* @require dir.as_str()
**/
fn void! native_rmtree(Path dir)
{
void* directory = opendir(dir.as_zstr());
defer if (directory) closedir(directory);
if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?;
NativeDirentry* entry;
while ((entry = readdir(directory)))
{
@pool()
{
String name = ((ZString)&entry.name).as_str();
if (!name || name == "." || name == "..") continue;
Path new_path = dir.tappend(name)!;
if (entry.type == DT_DIR)
{
native_rmtree(new_path)!;
continue;
}
if (remove(new_path.as_zstr()))
{
// TODO improve
return IoError.GENERAL_ERROR?;
}
};
}
os::native_rmdir(dir)!;
}
$endif
$endif
$if (!env::os_is_darwin() && !env::os_is_win32())
$if !env::os_is_darwin() && !env::os_is_win32():
fn usz! native_file_size(String path)
{
File f = file::open(path, "r")?;
File f = file::open(path, "r")!;
defer (void)f.close();
return f.seek(0, Seek.END)?;
return f.seek(0, Seek.END)!;
}
$if (env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE)
$if env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE:
fn bool native_file_or_dir_exists(String path)
{
@@ -75,7 +107,7 @@ fn bool native_is_file(String path)
{
File! f = file::open(path, "r");
defer (void)f.close();
return try? f;
return @ok(f);
}
fn bool native_is_dir(String path)
@@ -106,10 +138,16 @@ $endif
$switch (env::OS_TYPE)
$case IOS:
$case MACOSX:
$case MACOS:
$case TVOS:
$case WATCHOS:
$if env::ARCH_TYPE == X86_64:
extern fn NativeDirentry* readdir(void*) @extern("readdir$INODE64");
$else
extern fn NativeDirentry* readdir(void*) @extern("readdir");
$endif
struct NativeDirentry
{
usz ino;

View File

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

View File

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

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

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

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

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

View File

@@ -6,7 +6,7 @@ const char PREFERRED_SEPARATOR_WIN32 = '\\';
const char PREFERRED_SEPARATOR_POSIX = '/';
const char PREFERRED_SEPARATOR = env::os_is_win32() ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
typedef PathList = List<Path>;
def PathList = List<Path>;
fault PathResult
{
@@ -58,6 +58,51 @@ macro bool is_win32_separator(char c)
return c == '/' || c == '\\';
}
fn Path[]! ls(Path path)
{
unreachable();
}
enum MkdirPermissions
{
NORMAL,
USER_ONLY,
USER_AND_ADMIN
}
fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
if (is_dir(path)) return false;
if (exists(path)) return IoError.FILE_NOT_DIR?;
if (recursive)
{
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
}
if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?;
return os::native_mkdir(path, permissions);
}
fn bool! rmdir(Path path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
return os::native_rmdir(path);
}
fn void! rmtree(Path path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
$if $defined(os::native_rmtree):
return os::native_rmtree(path);
$else
assert(false, "rmtree is not available");
$endif
}
fn Path! new(String path, Allocator* using = mem::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
{
return { normalize(path.copy(using), path_env), path_env };
@@ -87,7 +132,7 @@ fn bool Path.equals(Path p1, Path p2)
**/
fn Path! Path.append(Path path, String filename, Allocator* using = mem::heap())
{
if (!path.path_string.len) return new(filename, using, path.env)?;
if (!path.path_string.len) return new(filename, using, path.env)!;
assert(!is_separator(path.path_string[^1], path.env));
@stack_mem(256; Allocator* mem)
@@ -134,9 +179,9 @@ fn String Path.dirname(Path path)
fn String! Path.extension(Path path)
{
String basename = path.basename();
usz index = basename.rindex_of(".")?;
usz index = basename.rindex_of(".")!;
// Plain ".foo" does not have an
if (index == 0) return SearchResult.MISSING!;
if (index == 0) return SearchResult.MISSING?;
if (index == basename.len) return "";
return basename[index + 1..];
}
@@ -166,9 +211,9 @@ fn usz! volume_name_len(String path, PathEnv path_env) @local
{
char c = path[i];
if (is_win32_separator(c)) return i;
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH!;
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
}
return PathResult.INVALID_PATH!;
return PathResult.INVALID_PATH?;
case 'A'..'Z':
case 'a'..'z':
return path[1] == ':' ? 2 : 0;
@@ -179,7 +224,7 @@ fn usz! volume_name_len(String path, PathEnv path_env) @local
fn Path! Path.parent(Path path)
{
if (path.path_string.len == 1 && is_separator(path.path_string[0], path.env)) return PathResult.NO_PARENT!;
if (path.path_string.len == 1 && is_separator(path.path_string[0], path.env)) return PathResult.NO_PARENT?;
foreach_r(i, c : path.path_string)
{
if (is_separator(c, path.env))
@@ -187,13 +232,13 @@ fn Path! Path.parent(Path path)
return { path.path_string[:i], path.env };
}
}
return PathResult.NO_PARENT!;
return PathResult.NO_PARENT?;
}
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
{
if (!path_str.len) return path_str;
usz path_start = volume_name_len(path_str, path_env)?;
usz path_start = volume_name_len(path_str, path_env)!;
usz path_len = path_str.len;
if (path_start == path_len) return path_str;
char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
@@ -225,7 +270,7 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
// The rest are names of the path elements, so check that the
// characters are valid.
if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH!;
if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH?;
// If we have '.' after a separator
if (c == '.' && previous_was_separator)
@@ -251,7 +296,7 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
continue;
case 2:
// This is an error: /a/../..
if (len == path_start && has_root) return PathResult.INVALID_PATH!;
if (len == path_start && has_root) return PathResult.INVALID_PATH?;
// If this .. at the start, or after ../? If so, we just copy ..
if (len == path_start ||

View File

@@ -19,7 +19,7 @@ fn Stream ByteReader.as_stream(ByteReader* reader)
fn usz! ByteReader.read(ByteReader* reader, char[] bytes)
{
if (reader.index >= reader.bytes.len) return IoError.EOF!;
if (reader.index >= reader.bytes.len) return IoError.EOF?;
usz len = math::min(reader.bytes.len - reader.index, bytes.len);
if (len == 0) return 0;
mem::copy(bytes.ptr, &reader.bytes[reader.index], len);
@@ -29,13 +29,13 @@ fn usz! ByteReader.read(ByteReader* reader, char[] bytes)
fn char! ByteReader.read_byte(ByteReader* reader)
{
if (reader.index >= reader.bytes.len) return IoError.EOF!;
if (reader.index >= reader.bytes.len) return IoError.EOF?;
return reader.bytes[reader.index++];
}
fn void! ByteReader.pushback_byte(ByteReader* reader)
{
if (!reader.index) return IoError.INVALID_PUSHBACK!;
if (!reader.index) return IoError.INVALID_PUSHBACK?;
reader.index--;
}
@@ -48,7 +48,7 @@ fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek)
case CURSOR: new_index = reader.index + offset;
case END: new_index = reader.bytes.len + offset;
}
if (new_index < 0) return IoError.INVALID_POSITION!;
if (new_index < 0) return IoError.INVALID_POSITION?;
reader.index = new_index;
return new_index;
}
@@ -56,7 +56,7 @@ fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek)
fn usz! ByteReader.write_stream(ByteReader* reader, Stream* writer)
{
if (reader.index >= reader.bytes.len) return 0;
usz written = writer.write(reader.bytes[reader.index..])?;
usz written = writer.write(reader.bytes[reader.index..])!;
reader.index += written;
assert(reader.index <= reader.bytes.len);
return written;

View File

@@ -52,16 +52,16 @@ fn String ByteWriter.as_str(ByteWriter* writer)
fn void! ByteWriter.ensure_capacity(ByteWriter* writer, usz len) @inline
{
if (writer.bytes.len > len) return;
if (!writer.allocator) return IoError.OUT_OF_SPACE!;
if (!writer.allocator) return IoError.OUT_OF_SPACE?;
if (len < 16) len = 16;
usz new_capacity = math::next_power_of_2(len);
char* new_ptr = realloc_checked(writer.bytes.ptr, new_capacity, .using = writer.allocator)?;
char* new_ptr = realloc_checked(writer.bytes.ptr, new_capacity, .using = writer.allocator)!;
writer.bytes = new_ptr[:new_capacity];
}
fn usz! ByteWriter.write(ByteWriter* writer, char[] bytes)
{
writer.ensure_capacity(writer.index + bytes.len)?;
writer.ensure_capacity(writer.index + bytes.len)!;
mem::copy(&writer.bytes[writer.index], bytes.ptr, bytes.len);
writer.index += bytes.len;
return bytes.len;
@@ -69,7 +69,7 @@ fn usz! ByteWriter.write(ByteWriter* writer, char[] bytes)
fn void! ByteWriter.write_byte(ByteWriter* writer, char c)
{
writer.ensure_capacity(writer.index + 1)?;
writer.ensure_capacity(writer.index + 1)!;
writer.bytes[writer.index++] = c;
}
@@ -82,10 +82,10 @@ fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader)
if (reader.supports_available())
{
usz total_read = 0;
while (usz available = reader.available()?)
while (usz available = reader.available()!)
{
writer.ensure_capacity(writer.index + available)?;
usz len = reader.read(writer.bytes[writer.index..])?;
writer.ensure_capacity(writer.index + available)!;
usz len = reader.read(writer.bytes[writer.index..])!;
total_read += len;
writer.index += len;
}
@@ -99,10 +99,10 @@ fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader)
// Less than 16 bytes? Double the capacity
if (len_to_read < 16)
{
writer.ensure_capacity(writer.bytes.len * 2)?;
writer.ensure_capacity(writer.bytes.len * 2)!;
}
// Read into the rest of the buffer
usz read = reader.read(writer.bytes[writer.index..])?;
usz read = reader.read(writer.bytes[writer.index..])!;
writer.index += read;
// Ok, we reached the end.
if (read < len_to_read) return total_read;

View File

@@ -31,11 +31,11 @@ fn void errno_set(Errno e)
os::errno_set((int)e);
}
typedef TerminateFunction = fn void();
typedef CompareFunction = fn int(void*, void*);
typedef JmpBuf = uptr[$$JMP_BUF_SIZE];
def TerminateFunction = fn void();
def CompareFunction = fn int(void*, void*);
def JmpBuf = uptr[$$JMP_BUF_SIZE];
$if (env::COMPILER_LIBC_AVAILABLE)
$if env::COMPILER_LIBC_AVAILABLE:
extern fn double atof(char* str);
extern fn int atoi(char* str);
@@ -59,7 +59,7 @@ extern fn int rand();
extern fn void srand(uint seed);
extern fn void longjmp(JmpBuf* buffer, CInt value);
$if (env::OS_TYPE == OsType.WIN32)
$if env::os_is_win32():
// TODO win32 aarch64
extern fn CInt _setjmp(void* frameptr, JmpBuf* buffer);
macro CInt setjmp(JmpBuf* buffer) => _setjmp($$frameaddress(), buffer);
@@ -148,11 +148,11 @@ $endif
// stdio
typedef Fpos = long;
typedef CFile = void*;
def Fpos = long;
def CFile = void*;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.LINUX:
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == LINUX:
extern CFile __stdin @extern("stdin");
extern CFile __stdout @extern("stdout");
extern CFile __stderr @extern("stderr");
@@ -162,7 +162,7 @@ $case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.LINUX:
macro CFile stdin() { return __stdin; }
macro CFile stdout() { return __stdout; }
macro CFile stderr() { return __stderr; }
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.MACOSX:
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_darwin():
extern CFile __stdinp;
extern CFile __stdoutp;
extern CFile __stderrp;
@@ -171,7 +171,7 @@ $case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.MACOSX:
macro CFile stdin() { return __stdinp; }
macro CFile stdout() { return __stdoutp; }
macro CFile stderr() { return __stderrp; }
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.WIN32:
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
extern fn CFile __acrt_iob_func(CInt c);
extern fn usz _msize(void* ptr);
macro usz malloc_size(void* ptr) { return _msize(ptr); }
@@ -185,9 +185,9 @@ $default:
$endswitch
const HAS_MALLOC_SIZE =
env::OS_TYPE == OsType.LINUX
|| env::OS_TYPE == OsType.WIN32
|| env::OS_TYPE == OsType.MACOSX;
env::OS_TYPE == LINUX
|| env::os_is_win32()
|| env::os_is_darwin();
// The following needs to be set per arch+os
// For now I have simply pulled the defaults from MacOS
@@ -202,10 +202,10 @@ const int EOF = -1;
const int FOPEN_MAX = 20;
const int FILENAME_MAX = 1024;
typedef Errno = distinct CInt;
typedef SeekIndex = CLong;
def Errno = distinct CInt;
def SeekIndex = CLong;
$if (env::COMPILER_LIBC_AVAILABLE)
$if env::COMPILER_LIBC_AVAILABLE:
extern fn int fclose(CFile stream);
extern fn void clearerr(CFile stream);
@@ -320,9 +320,7 @@ $endif
// time.h
typedef TimeOffset = CLong;
struct Tm
struct TmCommon @private
{
int tm_sec; /* seconds after the minute [0-60] */
int tm_min; /* minutes after the hour [0-59] */
@@ -333,18 +331,70 @@ struct Tm
int tm_wday; /* days since Sunday [0-6] */
int tm_yday; /* days since January 1 [0-365] */
int tm_isdst; /* Daylight Savings Time flag */
}
$switch (env::OS_TYPE)
$case WIN32:
def Tm = TmCommon;
$case WASI:
def TimeOffset = int;
struct Tm
{
inline TmCommon common;
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
char *tm_zone; /* timezone abbreviation */
int tm_nsec;
}
$case MACOS:
$case IOS:
$case TVOS:
$case WATCHOS:
$case OPENBSD:
$case FREEBSD:
$default:
def TimeOffset = CLong;
struct Tm
{
inline TmCommon common;
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
char *tm_zone; /* timezone abbreviation */
}
$endswitch
$if env::os_is_win32():
struct TimeSpec
{
Time s;
Time_t s;
ulong ns;
}
const int TIME_UTC = 1;
def Time_t = long;
def Clock_t = ulong;
$else
struct TimeSpec
{
Time_t s;
CLong ns;
}
def Time_t = CLong;
def Clock_t = CULong;
$endif
const int TIME_UTC = 1;
extern fn int timespec_get(TimeSpec* ts, int base);
extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
@@ -352,22 +402,37 @@ extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
// Likely wrong, must be per platform.
const CLOCKS_PER_SEC = 1000000;
// Time also needs to be per platform
typedef Time = long;
typedef Clock = ulong;
extern fn ZString asctime(Tm *timeptr);
extern fn Clock clock();
extern fn ZString ctime(Time *timer);
extern fn double difftime(Time time1, Time time2);
extern fn Tm* gmtime(Time *timer);
extern fn Tm* localtime(Time *timer);
extern fn Time mktime(Tm *timeptr);
extern fn Clock_t clock();
extern fn ZString ctime(Time_t *timer);
extern fn double difftime(Time_t time1, Time_t time2);
extern fn Tm* gmtime(Time_t *timer);
extern fn Tm* localtime(Time_t *timer);
$if env::os_is_win32():
extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer);
extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer);
extern fn void _get_timezone(CLong *timezone);
macro Tm* gmtime_r(Time_t *timer, Tm* buf) => _gmtime64_s(buf, timer);
macro Tm* localtime_r(Time_t *timer, Tm* buf) => _localtime64_s(buf, timer);
extern fn Time_t mktime(Tm *timeptr) @extern("_mktime64");
extern fn Time_t timegm(Tm *timeptr) @extern("_mkgmtime64");
$else
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf);
extern fn Tm* localtime_r(Time_t *timer, Tm* buf);
extern fn Time_t mktime(Tm *timeptr);
extern fn Time_t timegm(Tm *timeptr);
$endif
extern fn usz strftime(char* str, usz maxsize, char* format, Tm *timeptr);
extern fn Time time(Time *timer);
extern fn Time_t time(Time_t *timer);
// signal
typedef SignalFunction = fn void(int);
def SignalFunction = fn void(int);
extern fn SignalFunction signal(int sig, SignalFunction function);
// Incomplete
@@ -386,7 +451,7 @@ const Errno ENOEXEC = 8; // Exec format error
const Errno EBADF = 9; // Bad file number
const Errno ECHILD = 10; // No child processes
$if (env::OS_TYPE == MACOSX)
$if env::os_is_darwin():
const Errno EAGAIN = 35; // Try again Macos
$else
const Errno EAGAIN = 11; // Try again
@@ -418,7 +483,7 @@ const Errno ERANGE = 34; // Math result not representable
$switch (env::OS_TYPE)
$case MACOSX:
$case MACOS:
const Errno EDEADLK = 11; // Resource deadlock would occur MacOS
const Errno ENAMETOOLONG = 63; // File name too long MacOS
const Errno ELOOP = 62; // Too many symbolic links encountered
@@ -428,6 +493,7 @@ const Errno ENETDOWN = 50; // Network is down MacOS
const Errno ENETUNREACH = 51; // Network is unreachable MacOS
const Errno ENETRESET = 52; // Network dropped connection because of reset MacOS
const Errno EOPNOTSUPP = 45; // Operation not supported on transport endpoint
const Errno ENOTEMPTY = 66; // Directory not empty
$case WIN32:
const Errno EDEADLK = 36; // Resource deadlock would occur Win32
@@ -439,6 +505,7 @@ const Errno ECONNRESET = 108; // Connection reset by peer
const Errno ENETUNREACH = 118; // Network is unreachable
const Errno ENETRESET = 117; // Network dropped connection because of reset
const Errno EOPNOTSUPP = 130; // Operation not supported on transport endpoint
const Errno ENOTEMPTY = 41; // Directory not empty
$default:
const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?)
@@ -450,6 +517,7 @@ const Errno ECONNRESET = 104; // Connection reset by peer
const Errno ENETUNREACH = 101; // Network is unreachable
const Errno ENETRESET = 102; // Network dropped connection because of reset
const Errno EOPNOTSUPP = 95; // Operation not supported on transport endpoint
const Errno ENOTEMPTY = 39; // Directory not empty
$endswitch
@@ -458,7 +526,6 @@ $endswitch
/*
const Errno ENOLCK = 37; /* No record locks available */
const Errno ENOSYS = 38; /* Function not implemented */
const Errno ENOTEMPTY = 39; /* Directory not empty */
const Errno ENOMSG = 42; /* No message of desired type */
const Errno EIDRM = 43; /* Identifier removed */
@@ -529,7 +596,7 @@ const Errno EHOSTUNREACH = 113; /* No route to host */
$switch (env::OS_TYPE)
$case MACOSX:
$case MACOS:
const Errno ETIMEDOUT = 60; // Connection timed out
const Errno EINPROGRESS = 36; // Operation now in progress MacOS
const Errno EALREADY = 37; // Operation already in progress MacOS

View File

@@ -9,7 +9,7 @@ extern fn int* __errno_location();
macro int errno() => *__errno_location();
macro void errno_set(int err) => *(__errno_location()) = err;
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == MACOSX:
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == MACOS:
extern fn int* __error();
macro int errno() => *__error();

View File

@@ -85,29 +85,33 @@ fault MatrixError
MATRIX_INVERSE_DOESNT_EXIST,
}
typedef Complexf = Complex<float>;
typedef Complex = Complex<double>;
define complexf_identity = complex::identity<float>;
define complex_identity = complex::identity<double>;
def Complexf = Complex<float>;
def Complex = Complex<double>;
def complexf_identity = complex::identity<float>;
def complex_identity = complex::identity<double>;
typedef Quaternionf = Quaternion<float>;
typedef Quaternion = Quaternion<double>;
define quaternionf_identity = quaternion::identity<float>;
define quaternion_identity = quaternion::identity<double>;
def Quaternionf = Quaternion<float>;
def Quaternion = Quaternion<double>;
def quaternionf_identity = quaternion::identity<float>;
def quaternion_identity = quaternion::identity<double>;
typedef Matrix2f = Matrix2x2<float>;
typedef Matrix2 = Matrix2x2<double>;
typedef Matrix3f = Matrix3x3<float>;
typedef Matrix3 = Matrix3x3<double>;
typedef Matrix4f = Matrix4x4<float>;
typedef Matrix4 = Matrix4x4<double>;
define matrix4_ortho = matrix::ortho<double>;
define matrix4_perspective = matrix::perspective<double>;
define matrix4f_ortho = matrix::ortho<float>;
define matrix4f_perspective = matrix::perspective<float>;
macro Matrix4 matrix4_indentity() => { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } };
macro Matrix3 matrix3_indentity() => { .m = { [0] = 1, [4] = 1, [8] = 1 } };
macro Matrix2 matrix2_indentity() => { .m = { [0] = 1, [3] = 1 } };
def Matrix2f = Matrix2x2<float>;
def Matrix2 = Matrix2x2<double>;
def Matrix3f = Matrix3x3<float>;
def Matrix3 = Matrix3x3<double>;
def Matrix4f = Matrix4x4<float>;
def Matrix4 = Matrix4x4<double>;
def matrix4_ortho = matrix::ortho<double>;
def matrix4_perspective = matrix::perspective<double>;
def matrix4f_ortho = matrix::ortho<float>;
def matrix4f_perspective = matrix::perspective<float>;
def MATRIX2_IDENTITY = matrix::IDENTITY2<double>;
def MATRIX2F_IDENTITY = matrix::IDENTITY2<float>;
def MATRIX3_IDENTITY = matrix::IDENTITY3<double>;
def MATRIX3F_IDENTITY = matrix::IDENTITY3<float>;
def MATRIX4_IDENTITY = matrix::IDENTITY4<double>;
def MATRIX4F_IDENTITY = matrix::IDENTITY4<float>;
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
@@ -120,7 +124,7 @@ macro abs(x) => $$abs(x);
macro sign(x)
{
var $Type = $typeof(x);
$if ($Type.kindof == TypeKind.UNSIGNED_INT)
$if $Type.kindof == TypeKind.UNSIGNED_INT:
return ($Type)(x > 0);
$else
return ($Type)(x > 0) - ($Type)(x < 0);
@@ -134,7 +138,7 @@ macro sign(x)
**/
macro atan2(x, y)
{
$if (@typeis(x, float) && @typeis(y, float))
$if @typeis(x, float) && @typeis(y, float):
return _atan2f(x, y);
$else
return _atan2(x, y);
@@ -146,9 +150,9 @@ macro atan2(x, y)
* @checked (*y)[0] = x, y.len
* @require y.len == 2
**/
macro @sincos(x, y)
macro sincos(x, y)
{
$if ($typeof(y[0]).typeid == float.typeid)
$if $typeof(y[0]).typeid == float.typeid:
return _sincosf(x, y);
$else
return _sincos(x, y);
@@ -161,13 +165,73 @@ macro @sincos(x, y)
**/
macro atan(x)
{
$if ($typeof(x).typeid == float.typeid)
$if $typeof(x).typeid == float.typeid:
return _atanf(x);
$else
return _atan(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro atanh(x)
{
$if $typeof(x).typeid == float.typeid:
return _atanhf(x);
$else
return _atanh(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro acos(x)
{
$if $typeof(x).typeid == float.typeid:
return _acosf(x);
$else
return _acos(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro acosh(x)
{
$if $typeof(x).typeid == float.typeid:
return _acoshf(x);
$else
return _acosh(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro asin(x)
{
$if $typeof(x).typeid == float.typeid:
return _asinf(x);
$else
return _asin(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro asinh(x)
{
$if $typeof(x).typeid == float.typeid:
return _asinhf(x);
$else
return _asinh(x);
$endif
}
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
@@ -276,7 +340,7 @@ macro log10(x) => $$log10(values::promote_int(x));
**/
macro max(x, y, ...)
{
$if ($vacount == 0)
$if $vacount == 0:
return $$max(x, y);
$else
var m = $$max(x, y);
@@ -293,7 +357,7 @@ macro max(x, y, ...)
**/
macro min(x, y, ...)
{
$if ($vacount == 0)
$if $vacount == 0:
return $$min(x, y);
$else
var m = $$min(x, y);
@@ -321,7 +385,7 @@ macro nearbyint(x) => $$nearbyint(x);
**/
macro pow(x, exp)
{
$if (types::is_floatlike($typeof(exp)))
$if types::is_floatlike($typeof(exp)):
return $$pow(x, ($typeof(x))exp);
$else
return $$pow_int(x, exp);
@@ -333,7 +397,7 @@ macro pow(x, exp)
**/
macro frexp(x, int* e)
{
$switch($typeof(x))
$switch ($typeof(x))
$case float:
$case float16:
return _frexpf((float)x, e);
@@ -347,7 +411,7 @@ macro frexp(x, int* e)
**/
macro int signbit(x)
{
$switch($typeof(x))
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) >> 31;
@@ -422,7 +486,7 @@ $endswitch
**/
macro bool is_finite(x)
{
$switch($typeof(x))
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) & 0x7fffffff < 0x7f800000;
@@ -441,7 +505,7 @@ macro is_nan(x)
$case float16:
return bitcast((float)x, uint) & 0x7fffffff > 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ff << 52;
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ffu64 << 52;
$endswitch
}
@@ -455,7 +519,7 @@ macro is_inf(x)
$case float16:
return bitcast((float)x, uint) & 0x7fffffff == 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ff << 52;
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ffu64 << 52;
$endswitch
}
@@ -568,6 +632,13 @@ macro bool[<*>] double[<*>].comp_gt(double[<*>] x, double[<*>] y) => $$veccompgt
macro bool[<*>] double[<*>].comp_ge(double[<*>] x, double[<*>] y) => $$veccompge(x, y);
macro bool[<*>] double[<*>].comp_ne(double[<*>] x, double[<*>] y) => $$veccompne(x, y);
macro bool[<*>] ichar[<*>].comp_lt(ichar[<*>] x, ichar[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ichar[<*>].comp_le(ichar[<*>] x, ichar[<*>] y) => $$veccomple(x, y);
macro bool[<*>] ichar[<*>].comp_eq(ichar[<*>] x, ichar[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] ichar[<*>].comp_gt(ichar[<*>] x, ichar[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] ichar[<*>].comp_ge(ichar[<*>] x, ichar[<*>] y) => $$veccompge(x, y);
macro bool[<*>] ichar[<*>].comp_ne(ichar[<*>] x, ichar[<*>] y) => $$veccompne(x, y);
macro ichar ichar[<*>].sum(ichar[<*>] x) => $$reduce_add(x);
macro ichar ichar[<*>].product(ichar[<*>] x) => $$reduce_mul(x);
macro ichar ichar[<*>].and(ichar[<*>] x) => $$reduce_and(x);
@@ -576,6 +647,13 @@ macro ichar ichar[<*>].xor(ichar[<*>] x) => $$reduce_xor(x);
macro ichar ichar[<*>].max(ichar[<*>] x) => $$reduce_max(x);
macro ichar ichar[<*>].min(ichar[<*>] x) => $$reduce_min(x);
macro bool[<*>] short[<*>].comp_lt(short[<*>] x, short[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] short[<*>].comp_le(short[<*>] x, short[<*>] y) => $$veccomple(x, y);
macro bool[<*>] short[<*>].comp_eq(short[<*>] x, short[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] short[<*>].comp_gt(short[<*>] x, short[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] short[<*>].comp_ge(short[<*>] x, short[<*>] y) => $$veccompge(x, y);
macro bool[<*>] short[<*>].comp_ne(short[<*>] x, short[<*>] y) => $$veccompne(x, y);
macro short short[<*>].sum(short[<*>] x) => $$reduce_add(x);
macro short short[<*>].product(short[<*>] x) => $$reduce_mul(x);
macro short short[<*>].and(short[<*>] x) => $$reduce_and(x);
@@ -599,6 +677,12 @@ macro int int[<*>].xor(int[<*>] x) => $$reduce_xor(x);
macro int int[<*>].max(int[<*>] x) => $$reduce_max(x);
macro int int[<*>].min(int[<*>] x) => $$reduce_min(x);
macro bool[<*>] long[<*>].comp_lt(long[<*>] x, long[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] long[<*>].comp_le(long[<*>] x, long[<*>] y) => $$veccomple(x, y);
macro bool[<*>] long[<*>].comp_eq(long[<*>] x, long[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] long[<*>].comp_gt(long[<*>] x, long[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] long[<*>].comp_ge(long[<*>] x, long[<*>] y) => $$veccompge(x, y);
macro bool[<*>] long[<*>].comp_ne(long[<*>] x, long[<*>] y) => $$veccompne(x, y);
macro long long[<*>].sum(long[<*>] x) => $$reduce_add(x);
macro long long[<*>].product(long[<*>] x) => $$reduce_mul(x);
macro long long[<*>].and(long[<*>] x) => $$reduce_and(x);
@@ -607,6 +691,12 @@ macro long long[<*>].xor(long[<*>] x) => $$reduce_xor(x);
macro long long[<*>].max(long[<*>] x) => $$reduce_max(x);
macro long long[<*>].min(long[<*>] x) => $$reduce_min(x);
macro bool[<*>] int128[<*>].comp_lt(int128[<*>] x, int128[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] int128[<*>].comp_le(int128[<*>] x, int128[<*>] y) => $$veccomple(x, y);
macro bool[<*>] int128[<*>].comp_eq(int128[<*>] x, int128[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] int128[<*>].comp_gt(int128[<*>] x, int128[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] int128[<*>].comp_ge(int128[<*>] x, int128[<*>] y) => $$veccompge(x, y);
macro bool[<*>] int128[<*>].comp_ne(int128[<*>] x, int128[<*>] y) => $$veccompne(x, y);
macro int128 int128[<*>].sum(int128[<*>] x) => $$reduce_add(x);
macro int128 int128[<*>].product(int128[<*>] x) => $$reduce_mul(x);
macro int128 int128[<*>].and(int128[<*>] x) => $$reduce_and(x);
@@ -630,6 +720,13 @@ macro bool bool[<*>].xor(bool[<*>] x) => $$reduce_xor(x);
macro bool bool[<*>].max(bool[<*>] x) => $$reduce_max(x);
macro bool bool[<*>].min(bool[<*>] x) => $$reduce_min(x);
macro bool[<*>] char[<*>].comp_lt(char[<*>] x, char[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] char[<*>].comp_le(char[<*>] x, char[<*>] y) => $$veccomple(x, y);
macro bool[<*>] char[<*>].comp_eq(char[<*>] x, char[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] char[<*>].comp_gt(char[<*>] x, char[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] char[<*>].comp_ge(char[<*>] x, char[<*>] y) => $$veccompge(x, y);
macro bool[<*>] char[<*>].comp_ne(char[<*>] x, char[<*>] y) => $$veccompne(x, y);
macro char char[<*>].sum(char[<*>] x) => $$reduce_add(x);
macro char char[<*>].product(char[<*>] x) => $$reduce_mul(x);
macro char char[<*>].and(char[<*>] x) => $$reduce_and(x);
@@ -638,6 +735,12 @@ macro char char[<*>].xor(char[<*>] x) => $$reduce_xor(x);
macro char char[<*>].max(char[<*>] x) => $$reduce_max(x);
macro char char[<*>].min(char[<*>] x) => $$reduce_min(x);
macro bool[<*>] ushort[<*>].comp_lt(ushort[<*>] x, ushort[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ushort[<*>].comp_le(ushort[<*>] x, ushort[<*>] y) => $$veccomple(x, y);
macro bool[<*>] ushort[<*>].comp_eq(ushort[<*>] x, ushort[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] ushort[<*>].comp_gt(ushort[<*>] x, ushort[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] ushort[<*>].comp_ge(ushort[<*>] x, ushort[<*>] y) => $$veccompge(x, y);
macro bool[<*>] ushort[<*>].comp_ne(ushort[<*>] x, ushort[<*>] y) => $$veccompne(x, y);
macro ushort ushort[<*>].sum(ushort[<*>] x) => $$reduce_add(x);
macro ushort ushort[<*>].product(ushort[<*>] x) => $$reduce_mul(x);
@@ -647,6 +750,13 @@ macro ushort ushort[<*>].xor(ushort[<*>] x) => $$reduce_xor(x);
macro ushort ushort[<*>].max(ushort[<*>] x) => $$reduce_max(x);
macro ushort ushort[<*>].min(ushort[<*>] x) => $$reduce_min(x);
macro bool[<*>] uint[<*>].comp_lt(uint[<*>] x, uint[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] uint[<*>].comp_le(uint[<*>] x, uint[<*>] y) => $$veccomple(x, y);
macro bool[<*>] uint[<*>].comp_eq(uint[<*>] x, uint[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] uint[<*>].comp_gt(uint[<*>] x, uint[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] uint[<*>].comp_ge(uint[<*>] x, uint[<*>] y) => $$veccompge(x, y);
macro bool[<*>] uint[<*>].comp_ne(uint[<*>] x, uint[<*>] y) => $$veccompne(x, y);
macro uint uint[<*>].sum(uint[<*>] x) => $$reduce_add(x);
macro uint uint[<*>].product(uint[<*>] x) => $$reduce_mul(x);
macro uint uint[<*>].and(uint[<*>] x) => $$reduce_and(x);
@@ -655,6 +765,13 @@ macro uint uint[<*>].xor(uint[<*>] x) => $$reduce_xor(x);
macro uint uint[<*>].max(uint[<*>] x) => $$reduce_max(x);
macro uint uint[<*>].min(uint[<*>] x) => $$reduce_min(x);
macro bool[<*>] ulong[<*>].comp_lt(ulong[<*>] x, ulong[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ulong[<*>].comp_le(ulong[<*>] x, ulong[<*>] y) => $$veccomple(x, y);
macro bool[<*>] ulong[<*>].comp_eq(ulong[<*>] x, ulong[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] ulong[<*>].comp_gt(ulong[<*>] x, ulong[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] ulong[<*>].comp_ge(ulong[<*>] x, ulong[<*>] y) => $$veccompge(x, y);
macro bool[<*>] ulong[<*>].comp_ne(ulong[<*>] x, ulong[<*>] y) => $$veccompne(x, y);
macro ulong ulong[<*>].sum(ulong[<*>] x) => $$reduce_add(x);
macro ulong ulong[<*>].product(ulong[<*>] x) => $$reduce_mul(x);
macro ulong ulong[<*>].and(ulong[<*>] x) => $$reduce_and(x);
@@ -663,6 +780,13 @@ macro ulong ulong[<*>].xor(ulong[<*>] x) => $$reduce_xor(x);
macro ulong ulong[<*>].max(ulong[<*>] x) => $$reduce_max(x);
macro ulong ulong[<*>].min(ulong[<*>] x) => $$reduce_min(x);
macro bool[<*>] uint128[<*>].comp_lt(uint128[<*>] x, uint128[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] uint128[<*>].comp_le(uint128[<*>] x, uint128[<*>] y) => $$veccomple(x, y);
macro bool[<*>] uint128[<*>].comp_eq(uint128[<*>] x, uint128[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] uint128[<*>].comp_gt(uint128[<*>] x, uint128[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] uint128[<*>].comp_ge(uint128[<*>] x, uint128[<*>] y) => $$veccompge(x, y);
macro bool[<*>] uint128[<*>].comp_ne(uint128[<*>] x, uint128[<*>] y) => $$veccompne(x, y);
macro uint128 uint128[<*>].sum(uint128[<*>] x) => $$reduce_add(x);
macro uint128 uint128[<*>].product(uint128[<*>] x) => $$reduce_mul(x);
macro uint128 uint128[<*>].and(uint128[<*>] x) => $$reduce_and(x);
@@ -753,7 +877,6 @@ macro uint float.word(float d) => bitcast(d, uint);
macro double scalbn(double x, int n) => _scalbn(x, n);
extern fn double _atan(double x) @extern("atan");
extern fn float _atanf(float x) @extern("atanf");
extern fn double _atan2(double, double) @extern("atan2");
@@ -763,6 +886,17 @@ extern fn void _sincosf(float, float*) @extern("sincosf");
extern fn double _tan(double x) @extern("tan");
extern fn float _tanf(float x) @extern("tanf");
extern fn double _scalbn(double x, int n) @extern("scalbn");
extern fn double _acos(double x) @extern("acos");
extern fn double _asin(double x) @extern("asin");
extern fn double _acosh(double x) @extern("acosh");
extern fn double _asinh(double x) @extern("asinh");
extern fn double _atanh(double x) @extern("atanh");
extern fn float _acosf(float x) @extern("acosf");
extern fn float _asinf(float x) @extern("asinf");
extern fn float _acoshf(float x) @extern("acoshf");
extern fn float _asinhf(float x) @extern("asinhf");
extern fn float _atanhf(float x) @extern("atanhf");
fn double _frexp(double x, int* e)
{

View File

@@ -276,7 +276,7 @@ fn Matrix4x4 Matrix4x4.adjoint(Matrix4x4* mat)
fn Matrix2x2! Matrix2x2.inverse(Matrix2x2* m)
{
Real det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
Matrix2x2 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
@@ -284,7 +284,7 @@ fn Matrix2x2! Matrix2x2.inverse(Matrix2x2* m)
fn Matrix3x3! Matrix3x3.inverse(Matrix3x3* m)
{
Real det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
Matrix3x3 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
@@ -292,7 +292,7 @@ fn Matrix3x3! Matrix3x3.inverse(Matrix3x3* m)
fn Matrix4x4! Matrix4x4.inverse(Matrix4x4* m)
{
Real det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
Matrix4x4 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
@@ -411,6 +411,9 @@ fn Matrix4x4 perspective(Real fov, Real aspect_ratio, Real near, Real far)
};
}
const Matrix2x2 IDENTITY2 = { .m = { [0] = 1, [3] = 1 } };
const Matrix3x3 IDENTITY3 = { .m = { [0] = 1, [4] = 1, [8] = 1 } };
const Matrix4x4 IDENTITY4 = { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } };
macro matrix_component_mul(mat, val) @private
{

View File

@@ -6,9 +6,9 @@ struct Random
void* state;
}
typedef RandomSeedFn = fn void(Random*, char[] seed);
typedef RandomNextBytesFn = fn void(Random*, char[] buffer);
typedef RandomNextFn = fn uint(Random*, int bits);
def RandomSeedFn = fn void(Random*, char[] seed);
def RandomNextBytesFn = fn void(Random*, char[] buffer);
def RandomNextFn = fn uint(Random*, int bits);
struct RandomInterface
{

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_tanf.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc::atan;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
const double[*] ATANHI @private = {
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
fn double _ceil(double x) @weak @extern("ceil") @nostrip
{

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
fn float _cosf(float x) @extern("cosf") @weak @nostrip
{

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
macro uint _top12f(float x) @private => bitcast(x, uint) >> 20;

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
fn double _floor(double x) @weak @extern("floor") @nostrip
{

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
fn float powf_broken(float x, float f) @extern("powf") @weak @nostrip
{

View File

@@ -1,7 +1,7 @@
module std::math::nolibc;
import std::math;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2f.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
fn double _round(double x) @extern("round") @weak @nostrip
{

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
fn double _scalbn(double x, int n) @weak @extern("scalbn") @nostrip
{

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/s_tan.c */
/*

View File

@@ -1,6 +1,6 @@
module std::math::nolibc::trig;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
fn double sincos_broken(double x) @extern("sincos") @weak @nostrip
{

View File

@@ -1,6 +1,6 @@
module std::math::nolibc;
$if (!env::COMPILER_LIBC_AVAILABLE)
$if !env::COMPILER_LIBC_AVAILABLE:
fn double _trunc(double x) @weak @extern("trunc") @nostrip
{

View File

@@ -18,6 +18,8 @@ macro Quaternion Quaternion.scale(Quaternion a, Real s) => Quaternion { .v = a.v
macro Quaternion Quaternion.normalize(Quaternion q) => { .v = q.v.normalize() };
macro Real Quaternion.length(Quaternion q) => q.v.length();
macro Quaternion Quaternion.lerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount) };
macro Matrix4f Quaternion.to_matrixf(Quaternion* q) => into_matrix(q, Matrix4f);
macro Matrix4 Quaternion.to_matrix(Quaternion* q) => into_matrix(q, Matrix4);
fn Quaternion Quaternion.nlerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount).normalize() };
fn Quaternion Quaternion.invert(Quaternion q)
@@ -65,4 +67,32 @@ fn Quaternion Quaternion.mul(Quaternion a, Quaternion b)
a.l * b.l - a.i * b.i - a.j * a.j - a.k * a.k };
}
macro into_matrix(Quaternion* q, $Type) @private
{
$Type result = { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } };
Quaternion norm = q.normalize();
var ii = norm.i*norm.i;
var jj = norm.j*norm.j;
var kk = norm.k*norm.k;
var ik = norm.i*norm.k;
var ij = norm.i*q.j;
var jk = norm.j*norm.k;
var li = norm.l*norm.i;
var lj = norm.l*norm.j;
var lk = norm.l*norm.k;
result.m00 = 1 - 2*(jj + kk);
result.m01 = 2*(ik + lk);
result.m02 = 2*(ij - lj);
result.m10 = 2*(ij - lk);
result.m11 = 1 - 2*(ii + kk);
result.m12 = 2*(jk + li);
result.m20 = 2*(ik + lj);
result.m21 = 2*(jk - li);
result.m22 = 1 - 2*(ii + jj);
return result;
}

View File

@@ -1,13 +1,13 @@
module std::math::vector;
import std::math;
typedef Vec2f = float[<2>];
typedef Vec3f = float[<3>];
typedef Vec4f = float[<4>];
def Vec2f = float[<2>];
def Vec3f = float[<3>];
def Vec4f = float[<4>];
typedef Vec2 = double[<2>];
typedef Vec3 = double[<3>];
typedef Vec4 = double[<4>];
def Vec2 = double[<2>];
def Vec3 = double[<3>];
def Vec4 = double[<4>];
macro Vec2f.length_sq(Vec2f v) => v.dot(v);
macro Vec3f.length_sq(Vec3f v) => v.dot(v);
@@ -138,8 +138,9 @@ macro cross3(v1, v2) @private
macro transform2(v, mat) @private
{
return $typeof(v) { mat.m00 * v[0] + mat.m10 * v[1] + mat.30,
mat.m01 * v[0] + mar.m11 * v[1] + mat.31 };
return $typeof(v) {
mat.m00 * v[0] + mat.m10 * v[1] + mat.m30 ,
mat.m01 * v[0] + mat.m11 * v[1] + mat.m31 };
}
macro transform3(v, mat) @private
@@ -216,7 +217,7 @@ return v;
var view_proj = m1.mul(m2);
var invert = view_proj.invert();
// Create quaternion from source point
$if ($typeof(v[0]).typeid == float.typeid)
$if $typeof(v[0]).typeid == float.typeid:
Quaternionf quat = { v.x, v.y, v.z, 1 };
$else
Quaternion quat = { v.x, v.y, v.z, 1 };

View File

@@ -1,6 +1,6 @@
module std::math;
typedef SimpleRandom = distinct ulong;
def SimpleRandom = distinct ulong;
const long SIMPLE_RANDOM_MULTIPLIER @local = 0x5DEECE66D;
const long SIMPLE_RANDOM_ADDEND @local = 0xB;

View File

@@ -37,46 +37,42 @@ struct InetAddress
}
}
static initialize
{
io::formatter_register_type(InetAddress);
}
fn void! InetAddress.to_format(InetAddress* addr, Formatter* formatter)
fn void! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic
{
if (addr.is_ipv6)
{
formatter.printf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
addr.ipv6.a, addr.ipv6.b, addr.ipv6.c, addr.ipv6.d,
addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)?;
addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)!;
return;
}
formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)?;
formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!;
}
fn String! InetAddress.to_string(InetAddress* addr, Allocator* using = mem::heap())
fn String InetAddress.to_string(InetAddress* addr, Allocator* using = mem::heap()) @dynamic
{
if (addr.is_ipv6)
{
char[8 * 5 + 1] buffer;
String res = (String)io::bprintf(&buffer, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
addr.ipv6.a, addr.ipv6.b, addr.ipv6.c, addr.ipv6.d,
addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)?;
addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)!!;
return res.copy(using);
}
char[3 * 4 + 3 + 1] buffer;
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)?;
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!!;
return res.copy(using);
}
fn InetAddress! ipv6_from_str(String s)
{
uint sections = 0;
if (s.len < 2) return NetError.INVALID_IP_STRING!;
if (s.len < 2) return NetError.INVALID_IP_STRING?;
foreach (c : s) if (c == ':') sections++;
int zero_segment_len = s[0] == ':' || s[^1] == ':' ? 9 - sections : 8 - sections;
if (zero_segment_len == 7 && s.len == 2) return { .is_ipv6 = true };
if (zero_segment_len > 7) return NetError.INVALID_IP_STRING!;
if (zero_segment_len > 7) return NetError.INVALID_IP_STRING?;
usz index = 0;
bool last_was_colon, found_zero;
int current = -1;
@@ -92,7 +88,7 @@ fn InetAddress! ipv6_from_str(String s)
last_was_colon = true;
continue;
}
if (current < 0 || current > 65535) return NetError.INVALID_IP_STRING!;
if (current < 0 || current > 65535) return NetError.INVALID_IP_STRING?;
addr.ipv6arr[index++].val = (ushort)current;
current = -1;
last_was_colon = true;
@@ -100,9 +96,9 @@ fn InetAddress! ipv6_from_str(String s)
}
assert(current == -1);
// Check that this is the first ::
if (found_zero) return NetError.INVALID_IP_STRING!;
if (found_zero) return NetError.INVALID_IP_STRING?;
// Also check that the zeroed section is at least 2
if (zero_segment_len < 2) return NetError.INVALID_IP_STRING!;
if (zero_segment_len < 2) return NetError.INVALID_IP_STRING?;
// Skip (will be zero by default
index += zero_segment_len;
found_zero = true;
@@ -110,7 +106,7 @@ fn InetAddress! ipv6_from_str(String s)
continue;
}
last_was_colon = false;
if (index > 7 || !c.is_xdigit()) return NetError.INVALID_IP_STRING!;
if (index > 7 || !c.is_xdigit()) return NetError.INVALID_IP_STRING?;
if (current < 0) current = 0;
current = current * 16 + (c <= '9' ? c - '0' : (c | 32) - 'a' + 10);
}
@@ -118,7 +114,7 @@ fn InetAddress! ipv6_from_str(String s)
if (index == 8 && current == -1) return addr;
// Ends with number
if (index != 7 || current < 0 || current > 65535) return NetError.INVALID_IP_STRING!;
if (index != 7 || current < 0 || current > 65535) return NetError.INVALID_IP_STRING?;
addr.ipv6arr[7].val = (ushort)current;
return addr;
}
@@ -132,20 +128,20 @@ fn InetAddress! ipv4_from_str(String s)
{
if (c == '.')
{
if (current < 0) return NetError.INVALID_IP_STRING!;
if (current > 255) return NetError.INVALID_IP_STRING!;
if (current < 0) return NetError.INVALID_IP_STRING?;
if (current > 255) return NetError.INVALID_IP_STRING?;
switch (element)
{
case 0: addr.ipv4.a = (char)current;
case 1: addr.ipv4.b = (char)current;
case 2: addr.ipv4.c = (char)current;
default: return NetError.INVALID_IP_STRING!;
default: return NetError.INVALID_IP_STRING?;
}
current = -1;
element++;
continue;
}
if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING!;
if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING?;
if (current < 0)
{
current = c - '0';
@@ -153,7 +149,7 @@ fn InetAddress! ipv4_from_str(String s)
}
current = current * 10 + c - '0';
}
if (element != 3 || current < 0 || current > 255) return NetError.INVALID_IP_STRING!;
if (element != 3 || current < 0 || current > 255) return NetError.INVALID_IP_STRING?;
addr.ipv4.d = (char)current;
return addr;
}

View File

@@ -19,13 +19,13 @@ fn uint! ipv4toint(String s)
{
if (c == '.')
{
if (current < 0) return NetError.INVALID_IP_STRING!;
if (current < 0) return NetError.INVALID_IP_STRING?;
out = out << 8 + current;
current = -1;
element++;
continue;
}
if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING!;
if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING?;
if (current < 0)
{
current = c - '0';
@@ -33,7 +33,7 @@ fn uint! ipv4toint(String s)
}
current = current * 10 + c - '0';
}
if (element != 3 || current < 0) return NetError.INVALID_IP_STRING!;
if (element != 3 || current < 0) return NetError.INVALID_IP_STRING?;
out = out << 8 + current;
return out;
}
@@ -41,6 +41,6 @@ fn uint! ipv4toint(String s)
fn String! inttoipv4(uint val, Allocator* using = mem::heap())
{
char[3 * 4 + 3 + 1] buffer;
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)?;
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!;
return res.copy(using);
}

View File

@@ -1,8 +1,8 @@
module std::net::os;
$if ($defined(PLATFORM_AF_INET))
$if $defined(PLATFORM_AF_INET):
$if (!$defined(PLATFORM_O_NONBLOCK))
$if !$defined(PLATFORM_O_NONBLOCK):
const PLATFORM_O_NONBLOCK = 0;
$endif

View File

@@ -1,7 +1,7 @@
module std::net::os;
import libc;
$if (env::OS_TYPE == OsType.MACOSX)
$if env::os_is_darwin():
const AI_NUMERICSERV = 0x1000;
const AI_ALL = 0x100;

View File

@@ -1,7 +1,7 @@
module std::net::os;
import libc;
$if (env::OS_TYPE == OsType.LINUX)
$if env::OS_TYPE == LINUX:
struct AddrInfo
{

View File

@@ -1,12 +1,12 @@
module std::net::os;
import libc;
$if (env::OS_TYPE != OsType.WIN32 && $defined(AddrInfo))
$if !env::os_is_win32() && $defined(AddrInfo):
const int F_GETFL = 3;
const int F_SETFL = 4;
typedef NativeSocket = distinct int;
def NativeSocket = distinct int;
extern fn NativeSocket socket(int af, int type, int protocol) @extern("socket");
extern fn int getaddrinfo(ZString nodename, ZString servname, AddrInfo* hints, AddrInfo** res);
@@ -19,8 +19,8 @@ macro void! NativeSocket.close(NativeSocket this)
{
if (close(this))
{
if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET!;
return NetError.GENERAL_ERROR!;
if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET?;
return NetError.GENERAL_ERROR?;
}
}
@@ -29,8 +29,8 @@ macro void! NativeSocket.set_non_blocking(NativeSocket this)
int flags = fcntl(this, F_GETFL, 0);
if (fcntl(this, F_SETFL, flags | O_NONBLOCK) == -1)
{
if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET!;
return NetError.GENERAL_ERROR!;
if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET?;
return NetError.GENERAL_ERROR?;
}
}

View File

@@ -1,6 +1,6 @@
module std::net::os;
$if (env::OS_TYPE == OsType.WIN32)
$if env::os_is_win32():
const int PLATFORM_AF_INET = 1;
const int PLATFORM_AF_IPX = 6;
@@ -22,7 +22,7 @@ struct AddrInfo
AddrInfo* ai_next;
}
typedef NativeSocket = distinct uptr;
def NativeSocket = distinct uptr;
extern fn int wsa_startup(int, void*) @extern("WSAStartup");
extern fn int ioctlsocket(NativeSocket, long cmd, ulong *argp);

View File

@@ -1,10 +1,10 @@
module std::os::macos::cf;
$if (env::OS_TYPE == OsType.MACOSX)
$if env::os_is_darwin():
typedef CFAllocatorRef = distinct void*;
typedef CFAllocatorContextRef = distinct void*;
typedef CFOptionFlags = usz;
def CFAllocatorRef = distinct void*;
def CFAllocatorContextRef = distinct void*;
def CFOptionFlags = usz;
macro CFAllocatorRef default_allocator() => _macos_CFAllocatorGetDefault();
macro void CFAllocatorRef.dealloc(CFAllocatorRef allocator, void* ptr) => _macos_CFAllocatorDeallocate(allocator, ptr);

View File

@@ -1,10 +1,10 @@
module std::os::macos::cf;
$if (env::OS_TYPE == OsType.MACOSX)
$if env::os_is_darwin():
typedef CFArrayRef = distinct void*;
typedef CFArrayCallBacksRef = distinct void*;
typedef CFMutableArrayRef = distinct void*;
def CFArrayRef = distinct void*;
def CFArrayCallBacksRef = distinct void*;
def CFMutableArrayRef = distinct void*;
extern fn CFArrayRef _macos_CFArrayCreate(CFAllocatorRef allocator, void** values, CFIndex num_values, CFArrayCallBacksRef callBacks) @extern("CFArrayCreate");
extern fn CFArrayRef _macos_CFArrayCopy(CFAllocatorRef allocator, CFArrayRef array) @extern("CFArrayCopy");
extern fn CFIndex _macos_CFArrayGetCount(CFArrayRef array) @extern("CFArrayGetCount");

View File

@@ -1,9 +1,9 @@
module std::os::macos::cf;
$if (env::OS_TYPE == OsType.MACOSX)
$if env::os_is_darwin():
typedef CFTypeRef = distinct void*;
typedef CFIndex = isz;
def CFTypeRef = distinct void*;
def CFIndex = isz;
struct CFRange
{
CFIndex location;

View File

@@ -1,11 +1,11 @@
module std::os::macos::objc;
$if (env::OS_TYPE == OsType.MACOSX)
$if env::os_is_darwin():
typedef Class = distinct void*;
typedef Method = distinct void*;
typedef Ivar = distinct void*;
typedef Selector = distinct void*;
def Class = distinct void*;
def Method = distinct void*;
def Ivar = distinct void*;
def Selector = distinct void*;
fault ObjcFailure
{
@@ -24,7 +24,7 @@ macro Selector selector_register(char* c) => _macos_sel_registerName(c);
macro Class! class_by_name(char* c)
{
Class cls = _macos_objc_lookUpClass(c);
if (!cls) return ObjcFailure.CLASS_NOT_FOUND!;
if (!cls) return ObjcFailure.CLASS_NOT_FOUND?;
return cls;
}

10
lib/std/os/posix/files.c3 Normal file
View File

@@ -0,0 +1,10 @@
module std::os::posix;
$if env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE:
extern fn int rmdir(ZString);
extern fn int mkdir(ZString, ushort mode_t);
extern fn int chdir(ZString);
extern fn ZString getcwd(char* pwd, usz len);
$endif

View File

@@ -1,29 +0,0 @@
module std::os::win32::wsa;
$if (env::OS_TYPE == OsType.WIN32)
extern fn int get_last_error() @extern("WSAGetLastError");
extern fn void set_last_error(int error) @extern("WSASetLastError");
const int INVALID_HANDLE = 6;
const int NOT_ENOUGHT_MEMORY = 8;
const int INVALID_PARAMETER = 87;
const int OPERATION_ABORTED = 995;
const int IO_INCOMPLETE = 996;
const int IO_PENDING = 997;
const int EINTR = 10004;
const int EBADF = 10009;
const int ACCESS = 10013;
const int EFAULT = 10014;
const int EINVAL = 10022;
const int EMFILE = 10024;
const int EWOULDBLOCK = 10035;
const int EINPROGRESS = 10036;
const int EALREADY = 10037;
const int ENOTSOCK = 10038;
const int EDESTADDRREQ = 10039;
const int EMSGSIZE = 10040;
const int EPROTOTYPE = 10041;
const int ENOPROTOOPT = 10042;
const int EPROTONOSUPPORT = 10043;
const int ESOCKTNOSUPPORT = 10044;
$endif

View File

@@ -1,6 +1,6 @@
module std::os::win32::files;
module std::os::win32;
$if (env::os_is_win32())
$if env::os_is_win32():
enum Win32_GET_FILEEX_INFO_LEVELS
{
@@ -8,11 +8,6 @@ enum Win32_GET_FILEEX_INFO_LEVELS
MAX,
}
struct Win32_FILETIME
{
Win32_DWORD dwLowDateTime;
Win32_DWORD dwHighDateTime;
}
struct Win32_FILE_ATTRIBUTE_DATA
{
@@ -24,18 +19,47 @@ struct Win32_FILE_ATTRIBUTE_DATA
Win32_DWORD nFileSizeLow;
}
const MAX_PATH = 260;
struct Win32_WIN32_FIND_DATAW
{
Win32_DWORD dwFileAttributes;
Win32_FILETIME ftCreationTime;
Win32_FILETIME ftLastAccessTime;
Win32_FILETIME ftLastWriteTime;
Win32_DWORD nFileSizeHigh;
Win32_DWORD nFileSizeLow;
Win32_DWORD dwReserved0;
Win32_DWORD dwReserved1;
Win32_WCHAR[260] cFileName;
Win32_WCHAR[14] cAlternateFileName;
Win32_DWORD dwFileType; // Obsolete. Do not use.
Win32_DWORD dwCreatorType; // Obsolete. Do not use
Win32_WORD wFinderFlags; // Obsolete. Do not use
}
def Win32_LPWIN32_FIND_DATAW = Win32_WIN32_FIND_DATAW*;
extern fn Win32_BOOL win32_CloseHandle(Win32_HANDLE) @extern("CloseHandle");
extern fn Win32_BOOL win32_CreatePipe(Win32_PHANDLE hReadPipe, Win32_PHANDLE hWritePipe, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes, Win32_DWORD nSize) @extern("CreatePipe");
extern fn Win32_BOOL win32_GetFileAttributesExW(Win32_LPCWSTR, Win32_GET_FILEEX_INFO_LEVELS, Win32_LPVOID) @extern("GetFileAttributesExW");
extern fn Win32_BOOL win32_PathFileExistsW(Win32_LPCWSTR) @extern("PathFileExistsW");
extern fn Win32_DWORD win32_GetTempPathW(Win32_DWORD nBufferLength, Win32_LPWSTR lpBuffer) @extern("GetTempPathW");
extern fn Win32_BOOL win32_SetCurrentDirectoryW(Win32_LPCTSTR buffer) @extern("SetCurrentDirectoryW");
extern fn Win32_BOOL win32_RemoveDirectoryW(Win32_LPCWSTR lpPathName) @extern("RemoveDirectoryW");
extern fn Win32_BOOL win32_CreateDirectoryW(Win32_LPCWSTR lpPathName, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes) @extern("CreateDirectoryW");
extern fn Win32_BOOL win32_DeleteFileW(Win32_LPCWSTR lpFileName) @extern("DeleteFileW");
extern fn Win32_HANDLE win32_FindFirstFileW(Win32_LPCWSTR lpFileName, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindFirstFileW");
extern fn Win32_BOOL win32_FindNextFileW(Win32_HANDLE hFindFile, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindNextFileW");
extern fn Win32_BOOL win32_FindClose(Win32_HANDLE hFindFile) @extern("FindClose");
extern fn Char16* _wgetcwd(Char16* buffer, int maxlen);
extern fn usz wcslen(Char16* str);
/*
extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extern("GetCurrentDirectoryW");
extern bool _win32_CreateSymbolicLinkW(Char16* symlink_file, Char16* target_file, ulong flags) @extern("CreateSymbolicLinkW");
extern bool _win32_CreateDirectoryW(Char16* path_name, void* security_attributes) @extern("CreateDirectoryW");
extern bool _win32_DeleteFileW(Char16* file) @extern("DeleteFileW");
extern bool _win32_CopyFileW(Char16* from_file, Char16* to_file, bool no_overwrite) @extern("CopyFileW");
extern ulong _win32_GetFullPathNameW(Char16* file_name, ulong buffer_len, Char16* buffer, Char16** file_part) @extern("GetFullPathNameW");
*/

227
lib/std/os/win32/general.c3 Normal file
View File

@@ -0,0 +1,227 @@
module std::os::win32;
$if env::os_is_win32():
extern fn Win32_DWORD win32_GetLastError() @extern("GetLastError");
const Win32_DWORD ERROR_INVALID_FUNCTION = 0x1;
const Win32_DWORD ERROR_FILE_NOT_FOUND = 0x2;
const Win32_DWORD ERROR_PATH_NOT_FOUND = 0x3;
const Win32_DWORD ERROR_TOO_MANY_OPEN_FILES = 0x4;
const Win32_DWORD ERROR_ACCESS_DENIED = 0x5;
const Win32_DWORD ERROR_INVALID_HANDLE = 0x6;
const Win32_DWORD ERROR_ARENA_TRASHED = 0x7;
const Win32_DWORD ERROR_NOT_ENOUGH_MEMORY = 0x8;
const Win32_DWORD ERROR_INVALID_BLOCK = 0x9;
const Win32_DWORD ERROR_BAD_ENVIRONMENT = 0xA;
const Win32_DWORD ERROR_BAD_FORMAT = 0xB;
const Win32_DWORD ERROR_INVALID_ACCESS = 0xC;
const Win32_DWORD ERROR_INVALID_DATA = 0xD;
const Win32_DWORD ERROR_OUTOFMEMORY = 0xE;
const Win32_DWORD ERROR_INVALID_DRIVE = 0xF;
const Win32_DWORD ERROR_CURRENT_DIRECTORY = 0x10;
const Win32_DWORD ERROR_NOT_SAME_DEVICE = 0x11;
const Win32_DWORD ERROR_NO_MORE_FILES = 0x12;
const Win32_DWORD ERROR_WRITE_PROTECT = 0x13;
const Win32_DWORD ERROR_BAD_UNIT = 0x14;
const Win32_DWORD ERROR_NOT_READY = 0x15;
const Win32_DWORD ERROR_BAD_COMMAND = 0x16;
const Win32_DWORD ERROR_CRC = 0x17;
const Win32_DWORD ERROR_BAD_LENGTH = 0x18;
const Win32_DWORD ERROR_SEEK = 0x19;
const Win32_DWORD ERROR_NOT_DOS_DISK = 0x1A;
const Win32_DWORD ERROR_SECTOR_NOT_FOUND = 0x1B;
const Win32_DWORD ERROR_OUT_OF_PAPER = 0x1C;
const Win32_DWORD ERROR_WRITE_FAULT = 0x1D;
const Win32_DWORD ERROR_READ_FAULT = 0x1E;
const Win32_DWORD ERROR_GEN_FAILURE = 0x1F;
const Win32_DWORD ERROR_SHARING_VIOLATION = 0x20;
const Win32_DWORD ERROR_LOCK_VIOLATION = 0x21;
const Win32_DWORD ERROR_WRONG_DISK = 0x22;
const Win32_DWORD ERROR_SHARING_BUFFER_EXCEEDED = 0x24;
const Win32_DWORD ERROR_HANDLE_EOF = 0x26;
const Win32_DWORD ERROR_HANDLE_DISK_FULL = 0x27;
const Win32_DWORD ERROR_NOT_SUPPORTED = 0x32;
const Win32_DWORD ERROR_REM_NOT_LIST = 0x33;
const Win32_DWORD ERROR_DUP_NAME = 0x34;
const Win32_DWORD ERROR_BAD_NETPATH = 0x35;
const Win32_DWORD ERROR_NETWORK_BUSY = 0x36;
const Win32_DWORD ERROR_DEV_NOT_EXIST = 0x37;
const Win32_DWORD ERROR_TOO_MANY_CMDS = 0x38;
const Win32_DWORD ERROR_ADAP_HDW_ERR = 0x39;
const Win32_DWORD ERROR_BAD_NET_RESP = 0x3A;
const Win32_DWORD ERROR_UNEXP_NET_ERR = 0x3B;
const Win32_DWORD ERROR_BAD_REM_ADAP = 0x3C;
const Win32_DWORD ERROR_PRINTQ_FULL = 0x3D;
const Win32_DWORD ERROR_NO_SPOOL_SPACE = 0x3E;
const Win32_DWORD ERROR_PRINT_CANCELLED = 0x3F;
const Win32_DWORD ERROR_NETNAME_DELETED = 0x40;
const Win32_DWORD ERROR_NETWORK_ACCESS_DENIED = 0x41;
const Win32_DWORD ERROR_BAD_DEV_TYPE = 0x42;
const Win32_DWORD ERROR_BAD_NET_NAME = 0x43;
const Win32_DWORD ERROR_TOO_MANY_NAMES = 0x44;
const Win32_DWORD ERROR_TOO_MANY_SESS = 0x45;
const Win32_DWORD ERROR_SHARING_PAUSED = 0x46;
const Win32_DWORD ERROR_REQ_NOT_ACCEP = 0x47;
const Win32_DWORD ERROR_REDIR_PAUSED = 0x48;
const Win32_DWORD ERROR_FILE_EXISTS = 0x50;
const Win32_DWORD ERROR_CANNOT_MAKE = 0x52;
const Win32_DWORD ERROR_FAIL_I24 = 0x53;
const Win32_DWORD ERROR_OUT_OF_STRUCTURES = 0x54;
const Win32_DWORD ERROR_ALREADY_ASSIGNED = 0x55;
const Win32_DWORD ERROR_INVALID_PASSWORD = 0x56;
const Win32_DWORD ERROR_INVALID_PARAMETER = 0x57;
const Win32_DWORD ERROR_NET_WRITE_FAULT = 0x58;
const Win32_DWORD ERROR_NO_PROC_SLOTS = 0x59;
const Win32_DWORD ERROR_TOO_MANY_SEMAPHORES = 0x64;
const Win32_DWORD ERROR_EXCL_SEM_ALREADY_OWNED = 0x65;
const Win32_DWORD ERROR_SEM_IS_SET = 0x66;
const Win32_DWORD ERROR_TOO_MANY_SEM_REQUESTS = 0x67;
const Win32_DWORD ERROR_INVALID_AT_INTERRUPT_TIME = 0x68;
const Win32_DWORD ERROR_SEM_OWNER_DIED = 0x69;
const Win32_DWORD ERROR_SEM_USER_LIMIT = 0x6A;
const Win32_DWORD ERROR_DISK_CHANGE = 0x6B;
const Win32_DWORD ERROR_DRIVE_LOCKED = 0x6C;
const Win32_DWORD ERROR_BROKEN_PIPE = 0x6D;
const Win32_DWORD ERROR_OPEN_FAILED = 0x6E;
const Win32_DWORD ERROR_BUFFER_OVERFLOW = 0x6F;
const Win32_DWORD ERROR_DISK_FULL = 0x70;
const Win32_DWORD ERROR_NO_MORE_SEARCH_HANDLES = 0x71;
const Win32_DWORD ERROR_INVALID_TARGET_HANDLE = 0x72;
const Win32_DWORD ERROR_INVALID_CATEGORY = 0x75;
const Win32_DWORD ERROR_INVALID_VERIFY_SWITCH = 0x76;
const Win32_DWORD ERROR_BAD_DRIVER_LEVEL = 0x77;
const Win32_DWORD ERROR_CALL_NOT_IMPLEMENTED = 0x78;
const Win32_DWORD ERROR_SEM_TIMEOUT = 0x79;
const Win32_DWORD ERROR_INSUFFICIENT_BUFFER = 0x7A;
const Win32_DWORD ERROR_INVALID_NAME = 0x7B;
const Win32_DWORD ERROR_INVALID_LEVEL = 0x7C;
const Win32_DWORD ERROR_NO_VOLUME_LABEL = 0x7D;
const Win32_DWORD ERROR_MOD_NOT_FOUND = 0x7E;
const Win32_DWORD ERROR_PROC_NOT_FOUND = 0x7F;
const Win32_DWORD ERROR_WAIT_NO_CHILDREN = 0x80;
const Win32_DWORD ERROR_CHILD_NOT_COMPLETE = 0x81;
const Win32_DWORD ERROR_DIRECT_ACCESS_HANDLE = 0x82;
const Win32_DWORD ERROR_NEGATIVE_SEEK = 0x83;
const Win32_DWORD ERROR_SEEK_ON_DEVICE = 0x84;
const Win32_DWORD ERROR_IS_JOIN_TARGET = 0x85;
const Win32_DWORD ERROR_IS_JOINED = 0x86;
const Win32_DWORD ERROR_IS_SUBSTED = 0x87;
const Win32_DWORD ERROR_NOT_JOINED = 0x88;
const Win32_DWORD ERROR_NOT_SUBSTED = 0x89;
const Win32_DWORD ERROR_JOIN_TO_JOIN = 0x8A;
const Win32_DWORD ERROR_SUBST_TO_SUBST = 0x8B;
const Win32_DWORD ERROR_JOIN_TO_SUBST = 0x8C;
const Win32_DWORD ERROR_SUBST_TO_JOIN = 0x8D;
const Win32_DWORD ERROR_BUSY_DRIVE = 0x8E;
const Win32_DWORD ERROR_SAME_DRIVE = 0x8F;
const Win32_DWORD ERROR_DIR_NOT_ROOT = 0x90;
const Win32_DWORD ERROR_DIR_NOT_EMPTY = 0x91;
const Win32_DWORD ERROR_IS_SUBST_PATH = 0x92;
const Win32_DWORD ERROR_IS_JOIN_PATH = 0x93;
const Win32_DWORD ERROR_PATH_BUSY = 0x94;
const Win32_DWORD ERROR_IS_SUBST_TARGET = 0x95;
const Win32_DWORD ERROR_SYSTEM_TRACE = 0x96;
const Win32_DWORD ERROR_INVALID_EVENT_COUNT = 0x97;
const Win32_DWORD ERROR_TOO_MANY_MUXWAITERS = 0x98;
const Win32_DWORD ERROR_INVALID_LIST_FORMAT = 0x99;
const Win32_DWORD ERROR_LABEL_TOO_LONG = 0x9A;
const Win32_DWORD ERROR_TOO_MANY_TCBS = 0x9B;
const Win32_DWORD ERROR_SIGNAL_REFUSED = 0x9C;
const Win32_DWORD ERROR_DISCARDED = 0x9D;
const Win32_DWORD ERROR_NOT_LOCKED = 0x9E;
const Win32_DWORD ERROR_BAD_THREADID_ADDR = 0x9F;
const Win32_DWORD ERROR_BAD_ARGUMENTS = 0xA0;
const Win32_DWORD ERROR_BAD_PATHNAME = 0xA1;
const Win32_DWORD ERROR_SIGNAL_PENDING = 0xA2;
const Win32_DWORD ERROR_MAX_THRDS_REACHED = 0xA4;
const Win32_DWORD ERROR_LOCK_FAILED = 0xA7;
const Win32_DWORD ERROR_BUSY = 0xAA;
const Win32_DWORD ERROR_DEVICE_SUPPORT_IN_PROGRESS = 0xAB;
const Win32_DWORD ERROR_CANCEL_VIOLATION = 0xAD;
const Win32_DWORD ERROR_ATOMIC_LOCKS_NOT_SUPPORTED = 0xAE;
const Win32_DWORD ERROR_INVALID_SEGMENT_NUMBER = 0xB4;
const Win32_DWORD ERROR_INVALID_ORDINAL = 0xB6;
const Win32_DWORD ERROR_ALREADY_EXISTS = 0xB7;
const Win32_DWORD ERROR_INVALID_FLAG_NUMBER = 0xBA;
const Win32_DWORD ERROR_SEM_NOT_FOUND = 0xBB;
const Win32_DWORD ERROR_INVALID_STARTING_CODESEG = 0xBC;
const Win32_DWORD ERROR_INVALID_STACKSEG = 0xBD;
const Win32_DWORD ERROR_INVALID_MODULETYPE = 0xBE;
const Win32_DWORD ERROR_INVALID_EXE_SIGNATURE = 0xBF;
const Win32_DWORD ERROR_EXE_MARKED_INVALID = 0xC0;
const Win32_DWORD ERROR_BAD_EXE_FORMAT = 0xC1;
const Win32_DWORD ERROR_ITERATED_DATA_EXCEEDS_64K = 0xC2;
const Win32_DWORD ERROR_INVALID_MINALLOCSIZE = 0xC3;
const Win32_DWORD ERROR_DYNLINK_FROM_INVALID_RING = 0xC4;
const Win32_DWORD ERROR_IOPL_NOT_ENABLED = 0xC5;
const Win32_DWORD ERROR_INVALID_SEGDPL = 0xC6;
const Win32_DWORD ERROR_AUTODATASEG_EXCEEDS_64K = 0xC7;
const Win32_DWORD ERROR_RING2SEG_MUST_BE_MOVABLE = 0xC8;
const Win32_DWORD ERROR_RELOC_CHAIN_XEEDS_SEGLIM = 0xC9;
const Win32_DWORD ERROR_INFLOOP_IN_RELOC_CHAIN = 0xCA;
const Win32_DWORD ERROR_ENVVAR_NOT_FOUND = 0xCB;
const Win32_DWORD ERROR_NO_SIGNAL_SENT = 0xCD;
const Win32_DWORD ERROR_FILENAME_EXCED_RANGE = 0xCE;
const Win32_DWORD ERROR_RING2_STACK_IN_USE = 0xCF;
const Win32_DWORD ERROR_META_EXPANSION_TOO_LONG = 0xD0;
const Win32_DWORD ERROR_INVALID_SIGNAL_NUMBER = 0xD1;
const Win32_DWORD ERROR_THREAD_1_INACTIVE = 0xD2;
const Win32_DWORD ERROR_LOCKED = 0xD4;
const Win32_DWORD ERROR_TOO_MANY_MODULES = 0xD6;
const Win32_DWORD ERROR_NESTING_NOT_ALLOWED = 0xD7;
const Win32_DWORD ERROR_EXE_MACHINE_TYPE_MISMATCH = 0xD8;
const Win32_DWORD ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY = 0xD9;
const Win32_DWORD ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY = 0xDA;
const Win32_DWORD ERROR_FILE_CHECKED_OUT = 0xDC;
const Win32_DWORD ERROR_CHECKOUT_REQUIRED = 0xDD;
const Win32_DWORD ERROR_BAD_FILE_TYPE = 0xDE;
const Win32_DWORD ERROR_FILE_TOO_LARGE = 0xDF;
const Win32_DWORD ERROR_FORMS_AUTH_REQUIRED = 0xE0;
const Win32_DWORD ERROR_VIRUS_INFECTED = 0xE1;
const Win32_DWORD ERROR_VIRUS_DELETED = 0xE2;
const Win32_DWORD ERROR_PIPE_LOCAL = 0xE5;
const Win32_DWORD ERROR_BAD_PIPE = 0xE6;
const Win32_DWORD ERROR_PIPE_BUSY = 0xE7;
const Win32_DWORD ERROR_NO_DATA = 0xE8;
const Win32_DWORD ERROR_PIPE_NOT_CONNECTED = 0xE9;
const Win32_DWORD ERROR_MORE_DATA = 0xEA;
const Win32_DWORD ERROR_VC_DISCONNECTED = 0xF0;
const Win32_DWORD ERROR_INVALID_EA_NAME = 0xFE;
const Win32_DWORD ERROR_EA_LIST_INCONSISTENT = 0xFF;
const Win32_DWORD WAIT_TIMEOUT = 0x102;
const Win32_DWORD ERROR_NO_MORE_ITEMS = 0x103;
const Win32_DWORD ERROR_CANNOT_COPY = 0x10A;
const Win32_DWORD ERROR_DIRECTORY = 0x10B;
const Win32_DWORD ERROR_EAS_DIDNT_FIT = 0x113;
const Win32_DWORD ERROR_EA_FILE_CORRUPT = 0x114;
const Win32_DWORD ERROR_EA_TABLE_FULL = 0x115;
const Win32_DWORD ERROR_INVALID_EA_HANDLE = 0x116;
const Win32_DWORD ERROR_EAS_NOT_SUPPORTED = 0x11A;
const Win32_DWORD ERROR_NOT_OWNER = 0x120;
const Win32_DWORD ERROR_TOO_MANY_POSTS = 0x12A;
const Win32_DWORD ERROR_PARTIAL_COPY = 0x12A;
const Win32_DWORD ERROR_OPLOCK_NOT_GRANTED = 0x12C;
const Win32_DWORD ERROR_INVALID_OPLOCK_PROTOCOL = 0x12D;
const Win32_DWORD ERROR_DISK_TOO_FRAGMENTED = 0x12E;
const Win32_DWORD ERROR_DELETE_PENDING = 0x12F;
const Win32_DWORD ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING = 0x130;
const Win32_DWORD ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME = 0x131;
const Win32_DWORD ERROR_SECURITY_STREAM_IS_INCONSISTENT = 0x132;
const Win32_DWORD ERROR_INVALID_LOCK_RANGE = 0x133;
const Win32_DWORD ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT = 0x134;
const Win32_DWORD ERROR_NOTIFICATION_GUID_ALREADY_DEFINED = 0x135;
const Win32_DWORD ERROR_INVALID_EXCEPTION_HANDLER = 0x136;
const Win32_DWORD ERROR_DUPLICATE_PRIVILEGES = 0x137;
const Win32_DWORD ERROR_NO_RANGES_PROCESSED = 0x138;
const Win32_DWORD ERROR_NOT_ALLOWED_ON_SYSTEM_FILE = 0x139;
const Win32_DWORD ERROR_DISK_RESOURCES_EXHAUSTED = 0x13A;
const Win32_DWORD ERROR_INVALID_TOKEN = 0x13B;
const Win32_DWORD ERROR_DEVICE_FEATURE_NOT_SUPPORTED = 0x13C;
const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D;
const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E;
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
$endif

View File

@@ -1,6 +1,6 @@
module std::os::win32::process;
module std::os::win32;
$if (env::os_is_win32())
$if env::os_is_win32():
extern fn bool win32_CreateProcessW(
Win32_LPCWSTR lpApplicationName,
@@ -13,6 +13,6 @@ extern fn bool win32_CreateProcessW(
Win32_LPCWSTR lpCurrentDirectory,
Win32_LPSTARTUPINFOW lpStartupInfo,
Win32_LPPROCESS_INFORMATION lpProcessInformation
);
) @extern("CreateProcessW");
$endif

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