Compare commits

..

12 Commits

Author SHA1 Message Date
Christoffer Lerno
cf99c2be5c Fix of ordering. 2022-04-24 23:46:09 +02:00
Christoffer Lerno
50f43b3c71 Some conversions cleaned up. 2022-04-24 23:43:54 +02:00
Christoffer Lerno
40a0e71b24 More win tests. 2022-04-24 23:36:22 +02:00
Christoffer Lerno
bbb4bbe570 More win tests. 2022-04-24 23:04:34 +02:00
Christoffer Lerno
54422fd5e5 More win tests. 2022-04-24 22:52:19 +02:00
Christoffer Lerno
e08bac422a More win tests. 2022-04-24 22:50:46 +02:00
Christoffer Lerno
e86cf074c8 More win tests. 2022-04-24 22:31:57 +02:00
Christoffer Lerno
26cc87d3fc More win tests. 2022-04-24 22:22:14 +02:00
Christoffer Lerno
0bb2afb0ed More win tests. 2022-04-24 21:59:39 +02:00
Christoffer Lerno
633eb65e18 More win tests. 2022-04-24 21:57:18 +02:00
Christoffer Lerno
b717b84046 Build system improvements. Target changes x64-windows -> windows-x64, x64-darwin -> macos-x64. Improved mac support. LLD linking for Mac, Windows, Linux. Cross linking for Mac, Windows. Clean up string use. 2022-04-24 21:10:21 +02:00
Christoffer Lerno
0cf110f763 Build system improvements. Target changes x64-windows -> windows-x64, x64-darwin -> macos-x64. Improved mac support. LLD linking for Mac, Windows, Linux. Cross linking for Mac, Windows. Clean up string use. 2022-04-22 18:02:11 +02:00
1189 changed files with 20377 additions and 85891 deletions

View File

@@ -6,9 +6,6 @@ on:
pull_request:
branches: [ master ]
env:
LLVM_RELEASE_VERSION: 15
jobs:
build-msvc:
@@ -28,40 +25,17 @@ jobs:
- name: CMake
run: |
cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build --config ${{ matrix.build_type }}
- name: Compile and run some examples
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\hello_world_many.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
cmake --build build
- name: Build testproject
run: |
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log run hello_world_win32
- name: Build testproject lib
run: |
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
../../build/c3c run --debug-log
- name: run compiler tests
run: |
cd test
python3.exe src/tester.py ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
- name: Compile run unit tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -g1 --safe
- name: upload artifacts
uses: actions/upload-artifact@v3
with:
name: c3-windows-${{ matrix.build_type }}
path: build\${{ matrix.build_type }}\c3c.exe
python3 src/tester.py ../build/c3c.exe test_suite/
build-msys2-mingw:
runs-on: windows-latest
@@ -84,39 +58,27 @@ jobs:
install: git binutils mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python
- shell: msys2 {0}
run: |
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-15.0.3-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-15.0.3-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-13.0.1-2-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-13.0.1-2-any.pkg.tar.zst
- name: CMake
run: |
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c.exe test_suite2/
python3 src/tester.py ../build/c3c.exe test_suite/
build-msys2-clang:
runs-on: windows-latest
if: ${{ false }}
#if: ${{ false }}
strategy:
# Don't abort runners if a single one fails
fail-fast: false
@@ -139,24 +101,11 @@ jobs:
run: |
cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
cd test
@@ -169,7 +118,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [13, 14, 15, 16]
llvm_version: [12, 13, 14, 15]
steps:
- uses: actions/checkout@v3
@@ -177,44 +126,26 @@ jobs:
run: |
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build
- name: Install Clang ${{matrix.llvm_version}}
- name: Install Clang ${{ matrix.llvm_version }}
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
if [[ "${{matrix.llvm_version}}" < 16 ]]; then
if [[ "${{matrix.llvm_version}}" < 15 ]]; 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
if [[ "${{matrix.llvm_version}}" > 12 ]]; then
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
fi
- name: CMake
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 -B build -G Ninja -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit -g1 --safe
- name: Build testproject
run: |
cd resources/testproject
@@ -228,28 +159,7 @@ jobs:
- name: run compiler tests
run: |
cd test
if [[ "${{matrix.llvm_version}}" < 15 ]]; then
python3 src/tester.py ../build/c3c test_suite/
else
python3 src/tester.py ../build/c3c test_suite2/
fi
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
run: |
mkdir linux
cp -r lib linux
cp msvc_build_libraries.py linux
cp build/c3c linux
tar czf c3-linux-${{matrix.build_type}}.tar.gz linux
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
uses: actions/upload-artifact@v3
with:
name: c3-linux-${{matrix.build_type}}
path: c3-linux-${{matrix.build_type}}.tar.gz
python3 src/tester.py ../build/c3c test_suite/
build-mac:
runs-on: macos-latest
@@ -258,12 +168,11 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [13, 14, 15]
llvm_version: [12, 13]
steps:
- uses: actions/checkout@v3
- name: Download LLVM
run: |
brew update && brew install --overwrite python && brew install python-tk
brew install llvm@${{ matrix.llvm_version }} botan ninja
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
@@ -274,18 +183,6 @@ jobs:
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit -g1 --safe
- name: Build testproject
run: |
cd resources/testproject
@@ -296,142 +193,7 @@ jobs:
cd resources/testproject
../../build/c3c run --debug-log --forcelinker
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
cd test
if [[ "${{matrix.llvm_version}}" < 15 ]]; then
python3 src/tester.py ../build/c3c test_suite/
else
python3 src/tester.py ../build/c3c test_suite2/
fi
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
run: |
mkdir macos
cp -r lib macos
cp msvc_build_libraries.py macos
cp build/c3c macos
zip -r c3-macos-${{matrix.build_type}}.zip macos
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
uses: actions/upload-artifact@v3
with:
name: c3-macos-${{matrix.build_type}}
path: c3-macos-${{matrix.build_type}}.zip
release:
runs-on: ubuntu-latest
needs: [build-msvc, build-linux, build-mac]
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v3
- name: delete tag
continue-on-error: true
uses: actions/github-script@v6
with:
script: |
github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'tags/latest',
sha: context.sha
})
- name: create tag
uses: actions/github-script@v6
with:
script: |
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/latest',
sha: context.sha
})
- uses: actions/download-artifact@v3
- run: cp -r lib c3-windows-Release
- run: cp -r lib c3-windows-Debug
- run: cp msvc_build_libraries.py c3-windows-Release
- run: cp msvc_build_libraries.py c3-windows-Debug
- run: cp install_win_reqs.bat c3-windows-Release
- run: cp install_win_reqs.bat c3-windows-Debug
- run: zip -r c3-windows-Release.zip c3-windows-Release
- run: zip -r c3-windows-Debug.zip c3-windows-Debug
- id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: latest
release_name: latest
draft: false
prerelease: true
- name: upload windows
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-windows-Release.zip
asset_name: c3-windows.zip
asset_content_type: application/zip
- name: upload windows debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-windows-Debug.zip
asset_name: c3-windows-debug.zip
asset_content_type: application/zip
- name: upload linux
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-linux-Release/c3-linux-Release.tar.gz
asset_name: c3-linux.tar.gz
asset_content_type: application/gzip
- name: upload linux debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-linux-Debug/c3-linux-Debug.tar.gz
asset_name: c3-linux-debug.tar.gz
asset_content_type: application/gzip
- name: upload macos
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-macos-Release/c3-macos-Release.zip
asset_name: c3-macos.zip
asset_content_type: application/zip
- name: upload macos debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-macos-Debug/c3-macos-Debug.zip
asset_name: c3-macos-debug.zip
asset_content_type: application/zip
python3 src/tester.py ../build/c3c test_suite/

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.14)
project(c3c)
include(FetchContent)
include(FeatureSummary)
@@ -8,23 +8,10 @@ set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if(MSVC)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi")
else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3")
endif()
#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1 -fsanitize=undefined")
#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O1 -fsanitize=undefined")
#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined")
#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fsanitize=undefined")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
option(C3_USE_TB "Enable TB" OFF)
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
option(C3_USE_MIMALLOC "Use built-in mimalloc" OFF)
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
@@ -44,29 +31,29 @@ if(C3_USE_MIMALLOC)
endif()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
if (${C3_LLVM_VERSION} VERSION_LESS 13 OR ${C3_LLVM_VERSION} VERSION_GREATER 16)
if (${C3_LLVM_VERSION} VERSION_LESS 12 OR ${C3_LLVM_VERSION} VERSION_GREATER 15)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
endif()
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "14")
set(C3_LLVM_VERSION "13")
endif()
FetchContent_Declare(
LLVM_Windows
URL https://github.com/c3lang/win-llvm/releases/download/llvm1406/llvm-14.0.6-windows-amd64-msvc17-libcmt.7z
URL https://github.com/c3lang/win-llvm/releases/download/lld-llvm${C3_LLVM_VERSION}-release/llvm-${C3_LLVM_VERSION}.0.0-windows-amd64-msvc16-msvcrt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm1406/llvm-14.0.6-windows-amd64-msvc17-libcmt-dbg.7z
URL https://github.com/c3lang/win-llvm/releases/download/lld-llvm${C3_LLVM_VERSION}-release/llvm-${C3_LLVM_VERSION}.0.0-windows-amd64-msvc16-msvcrt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
message("Loading Windows LLVM debug libraries...")
FetchContent_MakeAvailable(LLVM_Windows_debug)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_debug_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
else()
message("Loading Windows LLVM libraries, this may take a while...")
message("Loading Windows LLVM libraries...")
FetchContent_MakeAvailable(LLVM_Windows)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
endif()
@@ -84,7 +71,6 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
add_definitions(${LLVM_DEFINITIONS})
set(LLVM_LINK_COMPONENTS
@@ -126,29 +112,28 @@ file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
if(C3_USE_TB)
find_library(TB_LIB NAMES tildebackend.a tildebackend.lib PATHS ${CMAKE_SOURCE_DIR}/tb/)
endif()
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
find_library(LLD_MACHO NAMES lldMachO2.lib lldMachO2.a liblldMachO2.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
else ()
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
endif ()
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
if(true)
message(STATUS "using find_library")
# find_library(TB_LIB NAMES tinybackend.a PATHS ${CMAKE_SOURCE_DIR}/resources/tblib)
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS})
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
find_library(LLD_MACHO NAMES lldMachO2.lib lldMachO2.a liblldMachO2.a PATHS ${LLVM_LIBRARY_DIRS})
else()
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS})
endif()
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS})
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
find_library(LLD_CORE NAMES lldCore.lib lldCore.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_DRIVER NAMES lldDriver.lib lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_READER_WRITER NAMES lldReaderWriter.lib lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_YAML NAMES lldYAML.lib lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
endif ()
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
find_library(LLD_CORE NAMES lldCore.lib lldCore.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_DRIVER NAMES lldDriver.lib lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_READER_WRITER NAMES lldReaderWriter.lib lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_YAML NAMES lldYAML.lib lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS})
endif()
set(lld_libs
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
@@ -160,12 +145,19 @@ set(lld_libs
${LLD_YAML}
${LLD_CORE}
)
if (APPLE)
set(lld_libs ${lld_libs} xar)
endif ()
if(APPLE)
set(lld_libs ${lld_libs} xar)
endif()
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
message(STATUS "linking to llvm libs ${llvm_libs} ${lld_libs}")
else()
if(${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
set(lld_libs lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO2 lldYAML)
else()
set(lld_libs lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO lldYAML)
endif()
endif()
message(STATUS "Found LLD ${lld_libs}")
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
@@ -217,9 +209,6 @@ add_executable(c3c
src/compiler/sema_expr.c
src/compiler/sema_internal.h
src/compiler/sema_name_resolution.c
src/compiler/sema_errors.c
src/compiler/sema_builtins.c
src/compiler/sema_initializers.c
src/compiler/semantic_analyser.c
src/compiler/sema_passes.c
src/compiler/sema_stmts.c
@@ -227,7 +216,6 @@ add_executable(c3c
src/compiler/source_file.c
src/compiler/symtab.c
src/compiler/target.c
src/compiler/sema_asm.c
src/compiler/tb_codegen.c
src/compiler/tilde_codegen.c
src/compiler/tilde_codegen_instr.c
@@ -249,20 +237,19 @@ add_executable(c3c
src/utils/vmem.c
src/utils/vmem.h
src/utils/whereami.c
src/utils/cpus.c
src/compiler/decltable.c
src/compiler/mac_support.c
src/compiler/tilde_codegen_storeload.c
src/compiler/llvm_codegen_storeload.c
src/compiler/tilde_codegen_expr.c
src/compiler/tilde_codegen_stmt.c
src/compiler/tilde_codegen_type.c
src/compiler/windows_support.c
src/compiler/codegen_asm.c
src/compiler/asm_target.c
src/compiler/llvm_codegen_builtins.c
src/compiler/expr.c src/utils/time.c)
src/compiler/tilde_codegen_type.c src/compiler/windows_support.c)
if(NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC")
message(STATUS "using gcc/clang warning switches")
target_compile_options(c3c PRIVATE -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
endif()
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/src/")
@@ -277,40 +264,23 @@ target_include_directories(c3c_wrappers PRIVATE
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
target_link_libraries(c3c ${llvm_libs} c3c_wrappers ${lld_libs})
if(C3_USE_TB)
target_link_libraries(c3c c3c_wrappers ${TB_LIB})
target_compile_definitions(c3c PUBLIC TB_BACKEND=1)
else()
target_compile_definitions(c3c PUBLIC TB_BACKEND=0)
endif()
# target_link_libraries(c3c m ${llvm_libs} c3c_wrappers ${TB_LIB} ${lld_libs})
if(C3_USE_MIMALLOC)
target_link_libraries(c3c mimalloc-static)
target_link_libraries(c3c m mimalloc-static)
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
target_link_libraries(c3c Advapi32)
target_link_options(c3c_wrappers PRIVATE nowarn)
endif()
if(MSVC)
message("Adding MSVC options")
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(c3c PUBLIC /MTd)
target_compile_options(c3c_wrappers PUBLIC /MTd)
else()
target_compile_options(c3c PUBLIC /MT)
target_compile_options(c3c_wrappers PUBLIC /MT)
endif()
else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
if (WIN32)
if (WIN32)
if (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILE_ID STREQUAL "GNU")
target_link_options(c3c PRIVATE -pthread)
target_compile_definitions(c3c PRIVATE USE_PTHREAD=1)
target_compile_options(c3c PRIVATE -mlong-double-64)
endif()
endif()
install(TARGETS c3c DESTINATION bin)
feature_summary(WHAT ALL)

105
README.md
View File

@@ -29,11 +29,12 @@ The following code shows [generic modules](http://www.c3-lang.org/generics/) (mo
```c++
module stack <Type>;
// Above: the parameterized type is applied to the entire module.
import std::mem;
struct Stack
{
usz capacity;
usz size;
usize capacity;
usize size;
Type* elems;
}
@@ -46,7 +47,7 @@ fn void Stack.push(Stack* this, Type element)
if (this.capacity == this.size)
{
this.capacity *= 2;
this.elems = mem::realloc(this.elems, Type.sizeof * this.capacity);
this.elems = mem::realloc(this.elems, $sizeof(Type) * this.capacity);
}
this.elems[this.size++] = element;
}
@@ -128,7 +129,7 @@ fn void test()
### Current status
The current version of the compiler is alpha release 0.3.
The current version of the compiler is alpha release 0.1.
It's possible to try out the current C3 compiler in the browser: https://ide.judge0.com/ this is courtesy of the
developer of Judge0.
@@ -141,7 +142,8 @@ Follow the issues [here](https://github.com/c3lang/c3c/issues).
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
or discuss C3 on its dedicated Discord: [https://discord.gg/qN76R87](https://discord.gg/qN76R87).
The compiler is currently verified to compile on Linux, Windows and MacOS.
The compiler should compile on Linux, Windows (under MSVC, Mingw or MSYS2) and MacOS,
but needs some install documentation for Windows.
@@ -149,7 +151,8 @@ The compiler is currently verified to compile on Linux, Windows and MacOS.
- If you wish to contribute with ideas, please file issues or discuss on Discord.
- Interested in contributing to the stdlib? Please get in touch on Discord.
- Compilation instructions for other Linux and Unix variants are appreciated.
- Are you a Windows dev and know your way around Github CI? Please help us get MSVC CI working!
- Install instructions for other Linux and Unix variants are appreciated.
- Would you like to contribute bindings to some library? It would be nice to have support for SDL, Raylib and more.
- Build something with C3 and show it off and give feedback. The language is still open for significant tweaks.
- Start work on the C -> C3 converter which takes C code and does a "best effort" to translate it to C3. The first version only needs to work on C headers.
@@ -157,34 +160,20 @@ The compiler is currently verified to compile on Linux, Windows and MacOS.
### Installing
#### Installing on Windows with precompiled binaries
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++" (there is also `c3c/resources/install_win_reqs.bat` to automate this)
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-windows-debug.zip))
3. Unzip exe and standard lib.
4. Run `c3c.exe`.
#### Installing on Ubuntu 20.10
#### Installing on Debian with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-linux-debug.tar.gz))
2. Unpack executable and standard lib.
3. Run `./c3c`.
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
2. Install CMake: `sudo apt install cmake`
3. Install LLVM 12 (or greater: C3C supports LLVM 12-15): `sudo apt-get install clang-12 zlib1g zlib1g-dev libllvm12 llvm-12 llvm-12-dev llvm-12-runtime liblld-12-dev liblld-12`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
7. Change directory to the build directory `cd build`
8. Build: `cmake --build .`
#### Installing on Mac with precompiled binaries
1. Make sure you have XCode with command line tools installed.
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-macos-debug.zip))
3. Unzip executable and standard lib.
4. Run `./c3c`.
You should now have a `c3c` executable.
#### Installing on Arch Linux
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git)
You can use your AUR package manager or clone it manually:
```sh
git clone https://aur.archlinux.org/c3c-git.git
cd c3c-git
makepkg -si
```
You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
#### Building via Docker
@@ -234,57 +223,7 @@ Then run
c3c compile main.c3
```
The generated binary will by default be named after the module that contains the main
function. In our case that is `hello_world`, so the resulting binary will be
called `hello_world` or `hello_world.exe`depending on platform.
### Compiling
#### Compiling on Windows
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++" (there is also `c3c/resources/install_win_reqs.bat` to automate this)
2. Install CMake
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`.
5. Set up the CMake build `cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release`
6. Build: `cmake --build build --config Release`
7. You should now have the c3c.exe
You should now have a `c3c` executable.
You can try it out by running some sample code: `c3c.exe compile ../resources/examples/hash.c3`
*Note that if you run into linking issues when building, make sure that you are using the latest version of VS17.*
#### Compiling on Ubuntu 20.10
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
2. Install CMake: `sudo apt install cmake`
3. Install LLVM 13 (or greater: C3C supports LLVM 13-16): `sudo apt-get install clang-13 zlib1g zlib1g-dev libllvm13 llvm-13 llvm-13-dev llvm-13-runtime liblld-13-dev liblld-13`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
7. Change directory to the build directory `cd build`
8. Set up CMake build: `cmake ..`
9. Build: `cmake --build .`
You should now have a `c3c` executable.
You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
#### Compiling on other Linux / Unix variants
1. Install CMake.
2. Install or compile LLVM and LLD *libraries* (version 13+ or higher)
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`.
5. Create a build directory `mkdir build`
6. Change directory to the build directory `cd build`
7. Set up CMake build for debug: `cmake ..`. At this point you may need to manually
provide the link path to the LLVM CMake directories, e.g. `cmake -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm/ ..`
8. Build: `cmake --build .`
The generated binary will be called `a.out`.
#### Licensing
@@ -293,4 +232,4 @@ MIT licensed.
#### Editor plugins
Editor plugins can be found at https://github.com/c3lang/editor-plugins.
Editor plugins can be found at https://github.com/c3lang/editor-plugins.

View File

@@ -29,14 +29,17 @@ else
fi
TAG="$1"
if [ "$1" = 21 ]; then
if [ "$1" = 20 ]; then
UBUNTU_VERSION="20.04"
LLVM_VERSION="12"
elif [ "$1" = 21 ]; then
UBUNTU_VERSION="21.10"
LLVM_VERSION="13"
elif [ "$1" = 22 ]; then
UBUNTU_VERSION="22.04"
LLVM_VERSION="14"
else
echo "ERROR: expected 21 or 22 as Ubuntu version argument" 1>&2
echo "ERROR: expected 20, 21 or 22 as Ubuntu version argument" 1>&2
exit 2
fi
IMAGE="$IMAGE:$TAG"

View File

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

25
lib/std/array.c3 Normal file
View File

@@ -0,0 +1,25 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::array;
import std::mem;
/**
* @require elements > 0
**/
macro alloc($Type, usize elements)
{
assert($Type.max / elements < $Type.sizeof);
$Type* ptr = mem::alloc($Type.sizeof * elements, $alignof($Type));
return ptr[0..(elements - 1)];
}
/**
* @require elements > 0
**/
macro calloc($Type, usize elements)
{
assert($Type.max / elements < $Type.sizeof);
$Type* ptr = mem::calloc($sizeof($Type) * elements, $alignof($Type));
return ptr[0..(elements - 1)];
}

View File

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

View File

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

176
lib/std/builtin.c3 Normal file
View File

@@ -0,0 +1,176 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::builtin;
fault VarCastResult
{
TYPE_MISMATCH
}
/**
* Stores a variable on the stack, then restores it at the end of the
* macro scope.
*
* @param variable `the variable to store and restore`
**/
macro void scope(&variable; @body) @autoimport
{
$typeof(variable) temp = variable;
defer variable = temp;
@body();
}
/**
* Convert a variant type to a type, returning an failure if there is a type mismatch.
*
* @param v `the variant to convert to the given type.`
* @param $Type `the type to convert to`
* @return `The variant.ptr converted to its type.`
**/
macro varcast(variant v, $Type) @autoimport
{
if (v.type != $Type.typeid) return VarCastResult.TYPE_MISMATCH!;
return ($Type*)v.ptr;
}
extern fn void printf(char*, ...);
struct CallstackElement
{
CallstackElement* prev;
char* function;
char* file;
uint line;
}
fn void panic(char* message, char *file, char *function, uint line) @autoimport
{
CallstackElement* stack = $$stacktrace();
$if ($defined(libc::stderr) && $defined(libc::fprintf)):
if (stack) stack = stack.prev;
if (stack)
{
libc::fprintf(@libc::stderr(), "\nERROR: '%s'\n", message);
}
else
{
libc::fprintf(@libc::stderr(), "\nERROR: '%s', function %s (%s:%d)\n", message, function, file, line);
}
while (stack)
{
libc::fprintf(@libc::stderr(), " at function %s (%s:%u)\n", stack.function, stack.file, stack.line);
if (stack == stack.prev) break;
stack = stack.prev;
}
$endif;
$$trap();
}
macro unreachable($string = "Unreachable statement reached.") @autoimport @noreturn
{
panic($string, $$FILE, $$FUNC, $$LINE);
$$unreachable();
}
/*
enum TypeKind
{
VOID,
BOOL,
FLOAT,
INTEGER,
STRUCT,
UNION,
ERROR,
ENUM,
ARRAY,
POINTER,
VAR_ARRAY,
SUBARRAY,
OPAQUE
// ALIAS,
}
struct TypeData
{
typeid typeId;
TypeKind kind;
int size;
int alignment;
char* name;
char* fullName;
}
struct TypeAlias
{
TypeData data;
typeid aliasType;
}
struct TypeError
{
TypeData data;
TypeErrorValue[] errors;
}
struct TypeArray
{
TypeData data;
typeid elementType;
ulong elements;
}
struct TypeVarArray
{
TypeData data;
typeid elementType;
}
struct TypeSubarray
{
TypeData data;
typeid elementType;
}
struct TypePointer
{
TypeData data;
typeid baseType;
}
struct TypeStruct
{
TypeData data;
TypeData*[] fields;
}
struct TypeUnion
{
TypeData data;
TypeData*[] variants;
}
struct TypeEnum
{
TypeData data;
typeid valueType;
TypeData*[] associated_value_types;
}
struct TypeEnumValue
{
char* name;
ulong value;
void*[] associated_values;
}
struct TypeErrorValue
{
char* name;
ulong value;
}
*/

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::cinterop;
module std::cinterop;
const C_INT_SIZE = $$C_INT_SIZE;
const C_LONG_SIZE = $$C_LONG_SIZE;

View File

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

View File

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

View File

@@ -1,117 +0,0 @@
module std::core::mem::allocator;
import libc;
private const Allocator _NULL_ALLOCATOR = { &null_allocator_fn };
private const Allocator _SYSTEM_ALLOCATOR = { &libc_allocator_fn };
private fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
{
switch (kind)
{
case ALLOC:
case CALLOC:
case REALLOC:
case ALIGNED_ALLOC:
case ALIGNED_REALLOC:
case ALIGNED_CALLOC:
return AllocationFailure.OUT_OF_MEMORY!;
default:
return null;
}
}
private struct AlignedBlock
{
usz len;
void* start;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
private fn void* _libc_aligned_alloc(usz bytes, usz alignment, usz offset) @inline
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
void* data = libc::malloc(header + bytes);
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
assert(mem > data);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
*desc = { bytes, data };
return mem;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
private fn void* _libc_aligned_calloc(usz bytes, usz alignment, usz offset) @inline
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
void* data = libc::calloc(header + bytes, 1);
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
*desc = { bytes, data };
return mem;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
private fn void* _libc_aligned_realloc(void* old_pointer, usz bytes, usz alignment, usz offset) @inline
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = _libc_aligned_calloc(bytes, alignment, offset);
mem::copy(new_data, old_pointer, desc.len > bytes ? desc.len : bytes, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
libc::free(data_start);
return new_data;
}
private fn void _libc_aligned_free(void* old_pointer) @inline
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
libc::free(desc.start);
}
fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @inline
{
if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT;
assert(math::is_power_of_2(alignment), "Alignment was not a power of 2");
void* data;
switch (kind)
{
case ALIGNED_ALLOC:
data = _libc_aligned_alloc(bytes, alignment, offset);
case ALLOC:
data = libc::malloc(bytes);
case ALIGNED_CALLOC:
data = _libc_aligned_calloc(bytes, alignment, offset);
case CALLOC:
data = libc::calloc(bytes, 1);
case ALIGNED_REALLOC:
if (!bytes) nextcase ALIGNED_FREE;
if (!old_pointer) nextcase ALIGNED_CALLOC;
data = _libc_aligned_realloc(old_pointer, bytes, alignment, offset);
case REALLOC:
if (!bytes) nextcase FREE;
if (!old_pointer) nextcase CALLOC;
data = libc::realloc(old_pointer, bytes);
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION!;
case ALIGNED_FREE:
_libc_aligned_free(old_pointer);
return null;
case FREE:
libc::free(old_pointer);
return null;
default:
unreachable();
}
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
return data;
}

View File

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

View File

@@ -1,40 +0,0 @@
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;
}
macro index_of(array, element)
{
foreach (i, &e : array)
{
if (*e == element) return i;
}
return SearchResult.MISSING!;
}
macro concat(arr1, arr2)
{
var $Type = $typeof(arr1[0]);
$Type[] result = array::alloc($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;
}

View File

@@ -1,82 +0,0 @@
module std::core::bitorder;
bitstruct ShortBE : short @bigendian
{
short val : 0..15;
}
bitstruct UShortBE : ushort @bigendian
{
ushort val : 0..15;
}
bitstruct IntBE : int @bigendian
{
int val : 0..31;
}
bitstruct UIntBE : int @bigendian
{
uint val : 0..31;
}
bitstruct LongBE : long @bigendian
{
long val : 0..63;
}
bitstruct ULongBE : ulong @bigendian
{
ulong val : 0..63;
}
bitstruct Int128BE : int128 @bigendian
{
int128 val : 0..127;
}
bitstruct UInt128BE : uint128 @bigendian
{
uint128 val : 0..127;
}
bitstruct ShortLE : short @littleendian
{
short val : 0..15;
}
bitstruct UShortLE : ushort @littleendian
{
ushort val : 0..15;
}
bitstruct IntLE : int @littleendian
{
int val : 0..31;
}
bitstruct UIntLE : int @littleendian
{
uint val : 0..31;
}
bitstruct LongLE : long @littleendian
{
long val : 0..63;
}
bitstruct ULongLE : ulong @littleendian
{
ulong val : 0..63;
}
bitstruct Int128LE : int128 @littleendian
{
int128 val : 0..127;
}
bitstruct UInt128LE : uint128 @littleendian
{
uint128 val : 0..127;
}

View File

@@ -1,167 +0,0 @@
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::builtin;
import libc;
import std::hash;
fault IteratorResult
{
NO_MORE_ELEMENT
}
fault SearchResult
{
MISSING
}
fault VarCastResult
{
TYPE_MISMATCH
}
/**
* Stores a variable on the stack, then restores it at the end of the
* macro scope.
*
* @param variable `the variable to store and restore`
**/
macro void @scope(&variable; @body) @builtin
{
var temp = variable;
defer variable = temp;
@body();
}
macro void @swap(&a, &b) @builtin
{
var temp = a;
a = b;
b = temp;
}
/**
* Convert a variant type to a type, returning an failure if there is a type mismatch.
*
* @param v `the variant to convert to the given type.`
* @param $Type `the type to convert to`
* @return `The variant.ptr converted to its type.`
**/
macro varcast(variant v, $Type) @builtin
{
if (v.type != $Type.typeid) return VarCastResult.TYPE_MISMATCH!;
return ($Type*)v.ptr;
}
struct CallstackElement
{
CallstackElement* prev;
char[] function;
char[] file;
uint line;
}
fn void default_panic(char[] message, char[] file, char[] function, uint line)
{
CallstackElement* stack = $$stacktrace();
$if ($defined(libc::stderr) && $defined(libc::fprintf)):
if (stack) stack = stack.prev;
if (stack)
{
libc::fprintf(libc::stderr(), "\nERROR: '%.*s'\n", (int)message.len, message.ptr);
}
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);
}
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);
if (stack == stack.prev) break;
stack = stack.prev;
}
$endif;
$$trap();
}
define PanicFn = fn void(char[] message, char[] file, char[] function, uint line);
PanicFn panic = &default_panic;
macro void unreachable($string = "Unreachable statement reached.") @builtin @noreturn
{
panic($string, $$FILE, $$FUNC, $$LINE);
$$unreachable();
}
macro bitcast(expr, $Type) @builtin
{
var $size = (usz)($sizeof(expr));
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
$Type x = void;
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
return x;
}
/**
* @require $Type.kindof == TypeKind.ENUM `Only enums may be used`
**/
macro enum_by_name($Type, char[] enum_name) @builtin
{
typeid x = $Type.typeid;
foreach (i, name : x.names)
{
if (str::compare(name, enum_name)) return ($Type)i;
}
return SearchResult.MISSING!;
}
/**
* Locality for prefetch, levels 0 - 3, corresponding
* to "extremely local" to "no locality"
**/
enum PrefetchLocality
{
NO_LOCALITY,
FAR,
NEAR,
VERY_NEAR,
}
/**
* Prefetch a pointer.
* @param [in] ptr `Pointer to prefetch`
* @param $locality `Locality ranging from none to extremely local`
* @param $write `Prefetch for write, otherwise prefetch for read.`
**/
macro prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write = false) @builtin
{
$$prefetch(ptr, $write ? 1 : 0, $locality.ordinal);
}
macro bool @castable(#expr, $To) @builtin
{
return $checks(($To)#expr);
}
macro bool @convertible(#expr, $To) @builtin
{
return $checks($To x = #expr);
}
macro uint int.hash(int i) = i;
macro uint uint.hash(uint i) = i;
macro uint short.hash(short s) = s;
macro uint ushort.hash(ushort s) = s;
macro uint char.hash(char c) = c;
macro uint ichar.hash(ichar c) = c;
macro uint long.hash(long i) = (uint)((i >> 32) ^ i);
macro uint ulong.hash(ulong i) = (uint)((i >> 32) ^ i);
macro uint bool.hash(bool b) = (uint)b;
macro uint typeid.hash(typeid t) = (uint)(((uptr)t >> 32) ^ (uptr)t);
macro uint char[].hash(char[] c) = (uint)fnv32a::encode(c);

View File

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

View File

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

View File

@@ -1,269 +0,0 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem;
macro @volatile_load(&x)
{
return $$volatile_load(&x);
}
macro @volatile_store(&x, y)
{
return $$volatile_store(&x, y);
}
/**
* @require math::is_power_of_2(alignment)
**/
fn usz aligned_offset(usz offset, usz alignment)
{
return alignment * ((offset + alignment - 1) / alignment);
}
macro void* aligned_pointer(void* ptr, usz alignment)
{
return (void*)(uptr)aligned_offset((uptr)ptr, alignment);
}
/**
* @require math::is_power_of_2(alignment)
**/
fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
{
return (uptr)ptr & ((uptr)alignment - 1) == 0;
}
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if ($inlined):
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
$else:
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
$endif;
}
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if ($inlined):
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
$else:
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
$endif;
}
macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
{
$$memmove(dst, src, len, $is_volatile, $dst_align, $src_align);
}
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if ($inlined):
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
$else:
$$memset(dst, val, len, $is_volatile, $dst_align);
$endif;
}
/**
* @require $typeof(a).kindof == TypeKind.SUBARRAY || $typeof(a).kindof == TypeKind.POINTER
* @require $typeof(b).kindof == TypeKind.SUBARRAY || $typeof(b).kindof == TypeKind.POINTER
* @require $typeof(a).kindof != TypeKind.SUBARRAY || len == -1
* @require $typeof(a).kindof != TypeKind.POINTER || len > -1
* @checked (a = b), (b = a)
**/
macro bool equals(a, b, isz len = -1, usz $align = 0)
{
$if (!$align):
$align = $typeof(a[0]).alignof;
$endif;
void* x = void;
void* y = void;
$if ($typeof(a).kindof == TypeKind.SUBARRAY):
len = a.len;
if (len != b.len) return false;
x = a.ptr;
y = b.ptr;
$else:
x = a;
y = b;
assert(len >= 0, "A zero or positive length must be given when comparing pointers.");
$endif;
if (!len) return true;
var $Type;
$switch ($align):
$case 1:
$Type = char;
$case 2:
$Type = ushort;
$case 4:
$Type = uint;
$case 8:
$default:
$Type = ulong;
$endswitch;
var $step = $Type.sizeof;
usz end = len / $step;
for (usz i = 0; i < end; i++)
{
if ((($Type*)x)[i] != (($Type*)y)[i]) return false;
}
usz last = len % $align;
for (usz i = len - last; i < len; i++)
{
if (((char*)x)[i] != ((char*)y)[i]) return false;
}
return true;
}
macro @clone(&value) @builtin
{
$typeof(value)* x = malloc($typeof(value));
*x = value;
return x;
}
macro @tclone(&value) @builtin
{
$typeof(value)* x = talloc($typeof(value));
*x = value;
return x;
}
fn void* malloc(usz size) @builtin @inline
{
return thread_allocator.alloc(size)!!;
}
fn void*! malloc_checked(usz size) @builtin @inline
{
return thread_allocator.alloc(size);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! malloc_aligned(usz size, usz alignment) @builtin @inline
{
return thread_allocator.alloc_aligned(size, alignment);
}
fn char[] alloc_bytes(usz bytes) @inline
{
return ((char*)thread_allocator.alloc(bytes))[:bytes]!!;
}
macro alloc($Type)
{
return ($Type*)thread_allocator.alloc($Type.sizeof)!!;
}
fn void* calloc(usz size) @builtin @inline
{
return thread_allocator.calloc(size)!!;
}
fn void*! calloc_checked(usz size) @builtin @inline
{
return thread_allocator.calloc(size);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! calloc_aligned(usz size, usz alignment) @builtin @inline
{
return thread_allocator.calloc_aligned(size, alignment);
}
fn void* realloc(void *ptr, usz new_size) @builtin @inline
{
return thread_allocator.realloc(ptr, new_size)!!;
}
fn void*! realloc_checked(void *ptr, usz new_size) @builtin @inline
{
return thread_allocator.realloc(ptr, new_size);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline
{
return thread_allocator.realloc_aligned(ptr, new_size, alignment);
}
fn void free(void* ptr) @builtin @inline
{
return thread_allocator.free(ptr)!!;
}
fn void free_aligned(void* ptr) @builtin @inline
{
return thread_allocator.free_aligned(ptr)!!;
}
/**
* Run with a specific allocator inside of the macro body.
**/
macro void @scoped(Allocator* allocator; @body())
{
Allocator* old_allocator = thread_allocator;
thread_allocator = allocator;
defer thread_allocator = old_allocator;
@body();
}
macro talloc($Type) @builtin
{
return temp_allocator().alloc_aligned($Type.sizeof, $Type.alignof)!!;
}
fn void* tmalloc(usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
{
return temp_allocator().alloc_aligned(size, alignment)!!;
}
fn void* tcalloc(usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
{
return temp_allocator().calloc_aligned(size, alignment)!!;
}
fn void* trealloc(void* ptr, usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
{
return temp_allocator().realloc_aligned(ptr, size, alignment)!!;
}
macro void @pool(;@body) @builtin
{
TempAllocator* temp = temp_allocator();
usz mark = temp.used;
defer temp.reset(mark);
@body();
}
private tlocal Allocator* thread_allocator = allocator::LIBC_ALLOCATOR;
private tlocal TempAllocator* thread_temp_allocator = null;
macro TempAllocator* temp_allocator()
{
if (!thread_temp_allocator)
{
thread_temp_allocator = allocator::new_temp(env::TEMP_ALLOCATOR_SIZE, allocator::LIBC_ALLOCATOR)!!;
}
return thread_temp_allocator;
}
macro Allocator* current_allocator()
{
return thread_allocator;
}

View File

@@ -1,181 +0,0 @@
module std::core::mem::allocator;
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
const DEFAULT_SIZE_PREFIX = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR;
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR;
define AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
struct Allocator
{
AllocatorFunction function;
}
enum AllocationKind
{
ALLOC,
CALLOC,
REALLOC,
FREE,
ALIGNED_ALLOC,
ALIGNED_CALLOC,
ALIGNED_REALLOC,
ALIGNED_FREE,
RESET,
MARK,
}
fault AllocationFailure
{
OUT_OF_MEMORY,
UNSUPPORTED_OPERATION,
CHUNK_TOO_LARGE,
}
fn void*! Allocator.alloc(Allocator* allocator, usz size) @inline
{
return allocator.function(allocator, size, 0, 0, null, ALLOC);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! Allocator.alloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
{
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_ALLOC);
}
fn void*! Allocator.realloc(Allocator* allocator, void* old_pointer, usz size) @inline
{
return allocator.function(allocator, size, 0, 0, old_pointer, REALLOC);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! Allocator.realloc_aligned(Allocator* allocator, void* old_pointer, usz size, usz alignment, usz offset = 0) @inline
{
return allocator.function(allocator, size, alignment, offset, old_pointer, ALIGNED_REALLOC);
}
fn usz! Allocator.mark(Allocator* allocator) @inline
{
return (usz)(uptr)allocator.function(allocator, 0, 0, 0, null, MARK);
}
fn void*! Allocator.calloc(Allocator* allocator, usz size) @inline
{
return allocator.function(allocator, size, 0, 0, null, CALLOC);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! Allocator.calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
{
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_CALLOC);
}
fn void! Allocator.free(Allocator* allocator, void* old_pointer) @inline
{
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)?;
}
fn void! Allocator.free_aligned(Allocator* allocator, void* old_pointer) @inline
{
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)?;
}
fn void Allocator.reset(Allocator* allocator, usz mark = 0)
{
allocator.function(allocator, mark, 0, 0, null, RESET)!!;
}
private fn usz alignment_for_allocation(usz alignment) @inline
{
if (alignment < DEFAULT_MEM_ALIGNMENT)
{
alignment = DEFAULT_MEM_ALIGNMENT;
}
return alignment;
}
struct DynamicArenaAllocator
{
inline Allocator allocator;
Allocator* backing_allocator;
DynamicArenaPage* page;
DynamicArenaPage* unused_page;
usz page_size;
}
/**
* @require page_size >= 128
* @require this != null
**/
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usz page_size, Allocator* backing_allocator = mem::current_allocator())
{
this.function = &dynamic_arena_allocator_function;
this.page = null;
this.unused_page = null;
this.page_size = page_size;
this.backing_allocator = backing_allocator;
}
/**
* @require this != null
**/
fn void DynamicArenaAllocator.destroy(DynamicArenaAllocator* this)
{
DynamicArenaPage* page = this.page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
this.backing_allocator.free(page)!!;
page = next_page;
}
page = this.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
this.backing_allocator.free(page)!!;
page = next_page;
}
this.page = null;
this.unused_page = null;
}
struct ArenaAllocator
{
inline Allocator allocator;
char[] data;
usz used;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void ArenaAllocator.init(ArenaAllocator* this, char[] data)
{
this.function = &arena_allocator_function;
this.data = data;
this.used = 0;
}
/**
* @require this != null
**/
fn void ArenaAllocator.reset(ArenaAllocator* this)
{
this.used = 0;
}

View File

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

View File

@@ -1,344 +0,0 @@
module std::core::str;
define ZString = distinct char*;
define Char32 = uint;
define Char16 = ushort;
private const uint SURROGATE_OFFSET = 0x10000;
private const uint SURROGATE_GENERIC_MASK = 0xF800;
private const uint SURROGATE_MASK = 0xFC00;
private const uint SURROGATE_CODEPOINT_MASK = 0x03FF;
private const uint SURROGATE_BITS = 10;
private const uint SURROGATE_LOW_VALUE = 0xDC00;
private const uint SURROGATE_HIGH_VALUE = 0xD800;
fault NumberConversion
{
EMPTY_STRING,
NEGATIVE_VALUE,
MALFORMED_INTEGER,
INTEGER_OVERFLOW,
}
fn String join(char[][] s, char[] joiner)
{
if (!s.len) return (String)null;
usz total_size = joiner.len * s.len;
foreach (char[]* &str : s)
{
total_size += str.len;
}
String res = string::new_with_capacity(total_size);
res.append(s[0]);
foreach (char[]* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res;
}
macro bool char_in_set(char c, char[] set)
{
foreach (ch : set)
{
if (ch == c) return true;
}
return false;
}
private macro char_is_space_tab(char c)
{
return c == ' ' || c == '\t';
}
private macro to_signed_integer($Type, char[] string)
{
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 int128! to_int128(char[] string) = to_signed_integer(int128, string);
fn long! to_long(char[] string) = to_signed_integer(long, string);
fn int! to_int(char[] string) = to_signed_integer(int, string);
fn short! to_short(char[] string) = to_signed_integer(short, string);
fn ichar! to_ichar(char[] string) = to_signed_integer(ichar, string);
fn char[] trim(char[] string, char[] to_trim = "\t\n\r ")
{
usz start = 0;
usz len = string.len;
while (start < len && char_in_set(string[start], to_trim)) start++;
if (start == len) return string[:0];
usz end = len - 1;
while (end > start && char_in_set(string[end], to_trim)) end--;
return string[start..end];
}
fn bool starts_with(char[] s, char[] needle)
{
if (needle.len > s.len) return false;
foreach (i, c : needle)
{
if (c != s[i]) return false;
}
return true;
}
fn char[][] tsplit(char[] s, char[] needle) = split(s, needle, mem::temp_allocator()) @inline;
fn char[][] split(char[] s, char[] needle, Allocator* allocator = mem::current_allocator())
{
usz capacity = 16;
usz i = 0;
char[]* holder = allocator.alloc(char[].sizeof * capacity)!!;
while (s.len)
{
usz! index = index_of(s, needle);
char[] res = void;
if (try index)
{
res = s[:index];
s = s[index + needle.len..];
}
else
{
res = s;
s = s[:0];
}
if (i == capacity)
{
capacity *= 2;
holder = allocator.realloc(holder, char[].sizeof * capacity)!!;
}
holder[i++] = res;
}
return holder[:i];
}
fn usz! index_of(char[] s, char[] needle)
{
usz match = 0;
usz needed = needle.len;
if (!needed) return SearchResult.MISSING!;
usz index_start = 0;
char search = needle[0];
foreach (usz i, char c : s)
{
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start;
search = needle[match];
continue;
}
if (match)
{
match = 0;
search = needle[0];
}
}
return SearchResult.MISSING!;
}
fn ZString copy_zstring(char[] s, Allocator* allocator = mem::current_allocator())
{
usz len = s.len;
char* str = allocator.alloc(len + 1)!!;
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn char[] copyz(char[] s, Allocator* allocator = mem::current_allocator())
{
usz len = s.len;
char* str = allocator.alloc(len + 1)!!;
mem::copy(str, s.ptr, len);
str[len] = 0;
return str[:len];
}
fn ZString tcopy_zstring(char[] s)
{
return copy_zstring(s, mem::temp_allocator());
}
fn bool compare(char[] a, char[] 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(char[] utf8)
{
usz len = 0;
foreach (char c : utf8)
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
fn Char32[]! utf8to32(char[] utf8, Allocator* allocator = mem::current_allocator)
{
usz codepoints = conv::utf8_codepoints(utf8);
Char32* data = allocator.alloc(Char32.sizeof * (codepoints + 1))?;
conv::utf8to32_unsafe(utf8, data)?;
data[codepoints] = 0;
return data[:codepoints];
}
fn char[] utf32to8(Char32[] utf32, Allocator* allocator = mem::current_allocator)
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = allocator.alloc(len + 1)!!;
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return data[:len];
}
fn Char16[]! utf8to16(char[] utf8, Allocator* allocator = mem::current_allocator)
{
usz len16 = conv::utf16len_for_utf8(utf8);
Char16* data = allocator.alloc((len16 + 1) * Char16.sizeof)?;
conv::utf8to16_unsafe(utf8, data)?;
data[len16] = 0;
return data[:len16];
}
fn char[]! utf16to8(Char16[] utf16, Allocator* allocator = mem::current_allocator())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = allocator.alloc(len + 1)?;
conv::utf16to8_unsafe(utf16, data)?;
return data[:len];
}
fn char[] copy(char[] s, Allocator* allocator = mem::current_allocator())
{
usz len = s.len;
ZString str_copy = copy_zstring(s, allocator) @inline;
return str_copy[:len];
}
fn char[] tcopy(char[] s)
{
usz len = s.len;
ZString str_copy = tcopy_zstring(s) @inline;
return str_copy[:len];
}
fn char[] tconcat(char[] s1, char[] 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 str[:full_len];
}
fn char[] concat(char[] s1, char[] 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 str[:full_len];
}
fn char[] ZString.as_str(ZString str)
{
return ((char*)str)[:str.len()];
}
fn usz ZString.len(ZString str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0])
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}

View File

@@ -1,311 +0,0 @@
module std::core::string;
import libc;
define String = distinct void*;
private struct StringData
{
Allocator* allocator;
usz len;
usz capacity;
char[*] chars;
}
const usz MIN_CAPACITY = 16;
fn String new_with_capacity(usz capacity, Allocator* allocator = mem::current_allocator())
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = allocator.alloc(StringData.sizeof + capacity)!!;
data.allocator = allocator;
data.len = 0;
data.capacity = capacity;
return (String)data;
}
fn String new(char[] c)
{
usz len = c.len;
String str = new_with_capacity(len);
StringData* data = str.data();
if (len)
{
data.len = len;
mem::copy(&data.chars, c.ptr, len);
}
return (String)data;
}
fn ZString String.zstr(String 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 String.len(String this)
{
if (!this) return 0;
return this.data().len;
}
/**
* @require new_size <= this.len()
*/
fn void String.chop(String this, usz new_size)
{
if (!this) return;
this.data().len = new_size;
}
fn char[] String.str(String str)
{
StringData* data = (StringData*)str;
if (!data) return char[] {};
return data.chars[:data.len];
}
fn void String.append_utf32(String* str, Char32[] chars)
{
str.reserve(chars.len);
foreach (Char32 c : chars)
{
str.append_char32(c);
}
}
/**
* @require index < str.len()
**/
fn void String.set(String str, usz index, char c)
{
str.data().chars[index] = c;
}
fn void String.append_repeat(String* 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 String.append_char32(String* 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 String String.tcopy(String* str) = str.copy(mem::temp_allocator());
fn String String.copy(String* str, Allocator* allocator = null)
{
if (!str)
{
if (allocator) return new_with_capacity(0, allocator);
return (String)null;
}
if (!allocator) allocator = mem::current_allocator();
StringData* data = str.data();
String 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 String.copy_zstr(String* str, Allocator* allocator = mem::current_allocator())
{
usz str_len = str.len();
if (!str_len)
{
return (ZString)allocator.calloc(1)!!;
}
char* zstr = allocator.alloc(str_len + 1)!!;
StringData* data = str.data();
mem::copy(zstr, &data.chars, str_len);
zstr[str_len] = 0;
return (ZString)zstr;
}
fn char[] String.copy_str(String* str, Allocator* allocator = mem::current_allocator())
{
return str.copy_zstr(allocator)[:str.len()];
}
fn char[] String.tcopy_str(String* str) = str.copy_str(mem::temp_allocator()) @inline;
fn bool String.equals(String str, String 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 String.destroy(String* str)
{
if (!*str) return;
StringData* data = str.data();
if (!data) return;
data.allocator.free(data)!!;
*str = (String)null;
}
fn bool String.less(String str, String 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 String.append_chars(String* this, char[] 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[] String.copy_utf32(String* this, Allocator* allocator = mem::current_allocator())
{
return str::utf8to32(this.str(), allocator) @inline!!;
}
fn void String.append_string(String* this, String str)
{
StringData* other = (StringData*)str;
if (!other) return;
this.append(str.str());
}
fn void String.append_char(String* 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 String.append(String* str, value)
{
var $Type = $typeof(value);
$switch ($Type):
$case char:
$case ichar:
str.append_char(value);
$case String:
str.append_string(value);
$case char[]:
str.append_chars(value);
$case Char32:
str.append_char32(value);
$default:
$if (@convertible($Type, Char32)):
str.append_char32(value);
$elif (@convertible($Type, char[])):
str.append_chars(value);
$else:
$assert("Unsupported type for appending");
$endif;
$endswitch;
}
private fn StringData* String.data(String str) @inline
{
return (StringData*)str;
}
private fn void String.reserve(String* str, usz addition)
{
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 = (String)data.allocator.realloc(data, StringData.sizeof + new_capacity)!!;
}
fn String String.new_concat(String a, String b, Allocator* allocator = mem::current_allocator())
{
String string = new_with_capacity(a.len() + b.len(), allocator);
string.append(a);
string.append(b);
return string;
}

View File

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

View File

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

View File

@@ -1,6 +0,0 @@
module std::core::values;
macro bool @is_int(#value) = types::is_int($typeof(#value));
macro bool @convertable_to(#a, #b) = $checks($typeof(#b) x = #a);
macro bool @is_floatlike(#value) = types::is_floatlike($typeof(#value));
macro bool @is_float(#value) = types::is_float($typeof(#value));

View File

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

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::env;
module std::env;
enum CompilerOptLevel
{
@@ -52,38 +52,7 @@ enum OsType
}
const OsType OS_TYPE = (OsType)($$OS_TYPE);
const bool COMPILER_LIBC_AVAILABLE = $$COMPILER_LIBC_AVAILABLE;
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)($$COMPILER_OPT_LEVEL);
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
const bool I128_SUPPORT = $$PLATFORM_I128_SUPPORTED;
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
const usz LLVM_VERSION = $$LLVM_VERSION;
const bool BENCHMARKING = $$BENCHMARKING;
const bool TESTING = $$TESTING;
const usz TEMP_ALLOCATOR_SIZE = 128 * 1024;
macro bool os_is_posix()
{
$switch (OS_TYPE):
$case IOS:
$case MACOSX:
$case NETBSD:
$case LINUX:
$case KFREEBSD:
$case FREEBSD:
$case OPENBSD:
$case SOLARIS:
$case TVOS:
$case WATCHOS:
return true;
$case WIN32:
$case WASI:
$case EMSCRIPTEN:
return false;
$default:
$echo("Assuming non-Posix environment");
return false;
$endswitch;
}

View File

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

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

@@ -0,0 +1,198 @@
// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::io;
import std::mem;
import libc;
import std::env;
struct File
{
CFile file;
}
fn int putchar(char c) @inline
{
return libc::putchar(c);
}
/**
* @param [&in] message
* @return `number of bytes printed.`
*/
fn int print(char* message)
{
char* pointer = message;
while (*pointer != '\0')
{
if (!putchar(*pointer)) return 0;
pointer++;
}
return 1;
}
/**
* @param [&in] message
* @return `number of bytes printed.`
*/
fn int println(char *message = "") @inline
{
return libc::puts(message);
}
fn void! File.open(File* file, char[] filename, char[] mode)
{
char* filename_copy = mem::talloc(filename.len + 1)!!;
char* mode_copy = mem::talloc(mode.len + 1)!!;
mem::copy(filename_copy, (char*)(filename), filename.len);
mem::copy(mode_copy, (char*)(mode), mode.len);
filename_copy[filename.len] = 0;
mode_copy[filename.len] = 0;
file.file = libc::fopen(filename_copy, mode_copy);
if (!file.file) return IoError.FILE_NOT_FOUND!;
}
enum Seek
{
SET = 0,
CURSOR = 1,
END = 2
}
fault IoError
{
FILE_NOT_FOUND,
FILE_NOT_SEEKABLE,
FILE_NOT_VALID,
FILE_INVALID_POSITION,
FILE_OVERFLOW,
FILE_IS_PIPE,
FILE_EOF,
FILE_INCOMPLETE_WRITE,
INTERRUPTED,
UNKNOWN_ERROR,
}
/**
* @require file.file != null
**/
fn void! File.seek(File *file, long offset, Seek seekMode = Seek.SET)
{
if (libc::fseek(file.file, (SeekIndex)(offset), (int)(seekMode)))
{
switch (libc::errno())
{
case EBADF: return IoError.FILE_NOT_SEEKABLE!;
case EINVAL: return IoError.FILE_INVALID_POSITION!;
case EOVERFLOW: return IoError.FILE_OVERFLOW!;
case ESPIPE: return IoError.FILE_IS_PIPE!;
default: return IoError.UNKNOWN_ERROR!;
}
}
}
/**
* @require file && file.file != null
*/
fn void! File.putc(File *file, char c)
{
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF!;
}
/**
* @require file != null
*/
fn void! File.close(File *file) @inline
{
if (file.file && libc::fclose(file.file))
{
switch (libc::errno())
{
case ECONNRESET:
case EBADF: return IoError.FILE_NOT_VALID!;
case EINTR: return IoError.INTERRUPTED!;
case EDQUOT:
case EFAULT:
case EAGAIN:
case EFBIG:
case ENETDOWN:
case ENETUNREACH:
case ENOSPC:
case EIO: return IoError.FILE_INCOMPLETE_WRITE!;
default: return IoError.UNKNOWN_ERROR!;
}
}
file.file = null;
}
/**
* @require file && file.file
*/
fn bool File.eof(File* file) @inline
{
return libc::feof(file.file) != 0;
}
/**
* @require file && file.file
*/
fn usize File.read(File* file, void* buffer, usize items, usize element_size = 1)
{
return libc::fread(buffer, element_size, items, file.file);
}
/**
* @param [&in] file
* @param [&out] buffer
* @param items
* @param element_size
* @require file.file `File must be initialized`
* @require element_size > 1
*/
fn usize File.write(File* file, void* buffer, usize items, usize element_size = 1)
{
return libc::fwrite(buffer, element_size, items, file.file);
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn usize! File.println(File* file, char[] string)
{
usize len = string.len;
if (len != libc::fwrite(string.ptr, 1, len, file.file)) return IoError.UNKNOWN_ERROR!;
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR!;
return len + 1;
}
/*
error FileError
{
ulong errno;
}
fn FileError errorFromErrno()
{
return FileError { };
}
pubic fn void! File.clearerr(File *file) @inline
{
clearerr(file->file);
}
fn void File.error(File *file) @inline
{
int err = ferror
}
*/

View File

@@ -1,183 +0,0 @@
module std::io::dir;
import std::io::os;
// In progress.
define Path = distinct char[];
const PREFERRED_SEPARATOR = USE_WIN32_FILESYSTEM ? '\\' : '/';
private const USE_WIN32_FILESYSTEM = env::OS_TYPE != OsType.WIN32;
fault PathResult
{
INVALID_PATH
}
fn char[]! getcwd(Allocator* allocator = mem::default_allocator())
{
return os::getcwd(allocator);
}
fn char[]! tgetcwd()
{
return getcwd(mem::temp_allocator()) @inline;
}
macro bool is_separator(char c)
{
$if (USE_WIN32_FILESYSTEM):
return c == '/' || c == '\\';
$else:
return c == '/';
$endif;
}
const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true,
['/'] = true,
};
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
[0..31] = true,
['>'] = true,
['<'] = true,
[':'] = true,
['\"'] = true,
['/'] = true,
['\\'] = true,
['|'] = true,
['?'] = true,
['*'] = true,
};
macro bool is_reserved_path_char(char c)
{
$if (USE_WIN32_FILESYSTEM):
return RESERVED_PATH_CHAR_WIN32[c];
$else:
return RESERVED_PATH_CHAR_POSIX[c];
$endif;
}
private fn usz! root_name_len(char[] path)
{
usz len = path.len;
if (!len) return 0;
$if (USE_WIN32_FILESYSTEM):
switch (path[0])
{
case '\\':
if (len < 2 || path[1] != '\\') break;
if (len == 2 || is_separator(path[2])) return PathResult.INVALID_PATH!;
for (usz i = 2; i < len; i++)
{
char c = path[i];
if (is_separator(c)) return i;
if (is_reserved_path_char(c)) return PathResult.INVALID_PATH!;
}
return len;
case 'A'..'Z':
case 'a'..'z':
if (len < 2 || path[1] != ':') break;
if (len < 3 || !is_separator(path[2])) return PathResult.INVALID_PATH!;
return 2;
}
$endif;
return 0;
}
private fn void! normalize_path(char[]* path_ref)
{
char[] path = *path_ref;
if (!path.len) return;
usz path_start = root_name_len(path)?;
usz len = path_start;
bool previous_was_separator = false;
usz path_len = path.len;
for (usz i = path_start; i < path_len; i++)
{
char c = path[i];
// Fold foo///bar into foo/bar
if (is_separator(c))
{
if (previous_was_separator)
{
continue;
}
path.ptr[len++] = PREFERRED_SEPARATOR;
previous_was_separator = true;
continue;
}
// If we get . we have different things that might happen:
if (c == '.' && i < path_len - 1)
{
// Is this ./ or /./ ?
if ((previous_was_separator || i == path_start) && is_separator(path[i + 1]))
{
// Then we skip this
i += 2;
continue;
}
// Is this /../ in that case we must walk back and erase(!)
if (i < path_len - 2 && previous_was_separator && path[i + 1] == '.' && is_separator(path[i + 2]))
{
assert(len > path_start);
len--;
while (len > path_start && !is_separator(path[len - 1]))
{
len--;
}
i += 2;
continue;
}
}
if (i != len) path[len] = c;
previous_was_separator = false;
len++;
}
path.ptr[len] = 0;
*path_ref = path[:len];
}
fn Path new_path(char[] path)
{
char[] copy = str::copy(path);
normalize_path(&copy)!!;
return (Path)copy;
}
fn Path Path.root_name(Path path)
{
char[] path_str = (char[])path;
usz len = root_name_len(path_str)!!;
if (!len) return (Path)"";
return (Path)path_str[0:len];
}
fn Path Path.root_directory(Path path)
{
char[] path_str = (char[])path;
usz len = path_str.len;
if (!len) return (Path)"";
$if (USE_WIN32_FILESYSTEM):
usz root_len = root_name_len(path_str)!!;
if (root_len == len || !is_separator(path_str[root_len])) return (Path)"";
return (Path)path_str[root_len..root_len];
$else:
if (!is_separator(path_str[0])) return (Path)"";
for (usz i = 1; i < len; i++)
{
if (is_separator(path_str[i]))
{
return (Path)path_str[:i];
}
}
return path;
$endif;
}
fn void Path.destroy(Path path)
{
free(path.ptr);
}

View File

@@ -1,111 +0,0 @@
// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::io;
import libc;
struct File
{
CFile file;
}
enum Seek
{
SET,
CURSOR,
END
}
fault IoError
{
FILE_NOT_FOUND,
FILE_NOT_SEEKABLE,
FILE_NOT_VALID,
FILE_INVALID_POSITION,
FILE_OVERFLOW,
FILE_IS_PIPE,
FILE_EOF,
FILE_INCOMPLETE_WRITE,
FILE_NOT_DIR,
NO_PERMISSION,
NAME_TOO_LONG,
INTERRUPTED,
GENERAL_ERROR,
UNKNOWN_ERROR,
}
fn int putchar(char c) @inline
{
return libc::putchar(c);
}
/**
* @param [&in] message
* @return `number of bytes printed.`
*/
fn int print(char* message)
{
char* pointer = message;
while (*pointer != '\0')
{
if (!putchar(*pointer)) return 0;
pointer++;
}
return 1;
}
/**
* @param [&in] message
* @return `number of bytes printed.`
*/
fn int println(char *message = "") @inline
{
return libc::puts(message);
}
fn File stdout()
{
return { libc::stdout() };
}
fn File stderr()
{
return { libc::stderr() };
}
fn File stdin()
{
return { libc::stdin() };
}
/*
error FileError
{
ulong errno;
}
fn FileError errorFromErrno()
{
return FileError { };
}
pubic fn void! File.clearerr(File *file) @inline
{
clearerr(file->file);
}
fn void File.error(File *file) @inline
{
int err = ferror
}
*/

View File

@@ -1,157 +0,0 @@
module std::io;
import libc;
fn void! File.open(File* file, char[] filename, char[] mode)
{
@pool()
{
char* filename_copy = tmalloc(filename.len + 1);
char* mode_copy = tmalloc(mode.len + 1);
mem::copy(filename_copy, (char*)(filename), filename.len);
mem::copy(mode_copy, (char*)(mode), mode.len);
filename_copy[filename.len] = 0;
mode_copy[filename.len] = 0;
file.file = libc::fopen(filename_copy, mode_copy);
if (!file.file) return IoError.FILE_NOT_FOUND!;
};
}
/**
* @require file.file != null
**/
fn void! File.seek(File *file, long offset, Seek seekMode = Seek.SET)
{
if (libc::fseek(file.file, (SeekIndex)(offset), (int)(seekMode)))
{
switch (libc::errno())
{
case errno::EBADF: return IoError.FILE_NOT_SEEKABLE!;
case errno::EINVAL: return IoError.FILE_INVALID_POSITION!;
case errno::EOVERFLOW: return IoError.FILE_OVERFLOW!;
case errno::ESPIPE: return IoError.FILE_IS_PIPE!;
default: return IoError.UNKNOWN_ERROR!;
}
}
}
/**
* @require file && file.file != null
*/
fn void! File.putc(File *file, char c)
{
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF!;
}
/**
* @require file != null
*/
fn void! File.close(File *file) @inline
{
if (file.file && libc::fclose(file.file))
{
switch (libc::errno())
{
case errno::ECONNRESET:
case errno::EBADF: return IoError.FILE_NOT_VALID!;
case errno::EINTR: return IoError.INTERRUPTED!;
case errno::EDQUOT:
case errno::EFAULT:
case errno::EAGAIN:
case errno::EFBIG:
case errno::ENETDOWN:
case errno::ENETUNREACH:
case errno::ENOSPC:
case errno::EIO: return IoError.FILE_INCOMPLETE_WRITE!;
default: return IoError.UNKNOWN_ERROR!;
}
}
file.file = null;
}
/**
* @require file && file.file
*/
fn bool File.eof(File* file) @inline
{
return libc::feof(file.file) != 0;
}
/**
* @require file && file.file
*/
fn usz File.read(File* file, void* buffer, usz items, usz element_size = 1)
{
return libc::fread(buffer, element_size, items, file.file);
}
/**
* @param [&in] file
* @param [&out] buffer
* @param items
* @param element_size
* @require file.file `File must be initialized`
* @require element_size > 1
*/
fn usz File.write(File* file, void* buffer, usz items, usz element_size = 1)
{
return libc::fwrite(buffer, element_size, items, file.file);
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn usz! File.println(File* file, char[] string)
{
usz len = string.len;
if (len != libc::fwrite(string.ptr, 1, len, file.file)) return IoError.UNKNOWN_ERROR!;
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR!;
return len + 1;
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn String File.getline(File* file, Allocator* allocator = mem::current_allocator())
{
String s = string::new_with_capacity(120, allocator);
while (!file.eof())
{
int c = libc::fgetc(file.file);
if (c == -1) break;
if (c == '\n') break;
s.append_char((char)c);
}
return s;
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
* @return "a zero terminated char[] (the pointer may be safely cast into a ZString)"
*/
fn char[] File.tgetline(File* file)
{
String s = file.getline(mem::temp_allocator());
ZString z = s.zstr();
return z[:s.len()];
}
fn char! File.getc(File* file)
{
int c = libc::fgetc(file.file);
if (c == -1) return IoError.FILE_EOF!;
return (char)c;
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn void File.flush(File* file)
{
libc::fflush(file.file);
}

View File

@@ -1,81 +0,0 @@
module std::io::file;
import libc;
struct FileInfo
{
ulong size;
}
$switch (env::OS_TYPE):
$case MACOSX:
$case IOS:
$case WATCHOS:
$case TVOS:
private struct DarwinTimespec
{
long tv_sec;
long tv_nsec;
}
private struct Darwin64Stat
{
int st_dev;
ushort st_mode;
ushort st_nlink;
ulong st_ino;
uint st_uid;
uint st_gid;
int st_rdev;
DarwinTimespec st_atimespec; // time of last access
DarwinTimespec st_mtimespec; // time of last data modification
DarwinTimespec st_ctimespec; // time of last status change
DarwinTimespec st_birthtimespec; // time of file creation(birth)
long st_size;
long st_blocks;
int st_blocksize;
uint st_flags;
uint st_gen;
int st_lspare;
long[2] st_qspare;
}
extern fn int _stat(ZString str, Darwin64Stat* stat) @extname("stat64");
fn void! FileInfo.read(FileInfo* info, Path path)
{
@pool()
{
Darwin64Stat stat;
int res = _stat(str::tcopy_zstring((char[])path), &stat);
if (res != 0)
{
switch (libc::errno())
{
case errno::EBADF:
return IoError.FILE_NOT_VALID!;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return IoError.GENERAL_ERROR!;
case errno::EACCES:
return IoError.NO_PERMISSION!;
case errno::ELOOP:
return IoError.NO_PERMISSION!;
case errno::ENAMETOOLONG:
return IoError.NAME_TOO_LONG!;
case errno::ENOENT:
return IoError.FILE_NOT_FOUND!;
case errno::ENOTDIR:
return IoError.FILE_NOT_DIR!;
case errno::EOVERFLOW:
return IoError.GENERAL_ERROR!;
default:
return IoError.UNKNOWN_ERROR!;
}
}
info.size = stat.st_size;
};
}
$default:
macro void! FileInfo.read(FileInfo* info, Path path)
{
$assert("Unsupported function");
}
$endswitch;

View File

@@ -1,556 +0,0 @@
module std::io;
private fn void! Formatter.left_adjust(Formatter* this, usz len)
{
if (!this.flags.left) return;
for (usz l = len; l < this.width; l++) this.out(' ')?;
}
private fn void! Formatter.right_adjust(Formatter* this, usz len)
{
if (this.flags.left) return;
for (usz l = len; l < this.width; l++) this.out(' ')?;
}
private fn uint128 int_from_variant(variant arg, bool *is_neg)
{
*is_neg = false;
if (arg.type.kindof == TypeKind.POINTER)
{
return (uint128)(uptr)*(void**)arg.ptr;
}
switch (arg)
{
case bool:
return (uint128)*arg;
case ichar:
int val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case short:
int val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int:
int val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case long:
long val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int128:
int128 val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case char:
return *arg;
case ushort:
return *arg;
case uint:
return *arg;
case ulong:
return *arg;
case uint128:
return *arg;
case float:
float f = *arg;
return (uint128)((*is_neg = f < 0) ? -f : f);
case double:
double d = *arg;
return (uint128)((*is_neg = d < 0) ? -d : d);
default:
return 0;
}
}
private fn FloatType float_from_variant(variant arg)
{
$if (env::F128_SUPPORT):
if (arg.type == float128.typeid) return *((float128*)arg.ptr);
$endif;
$if (env::F16_SUPPORT):
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
$endif;
if (arg.type.kindof == TypeKind.POINTER)
{
return (FloatType)(uptr)(void*)arg.ptr;
}
switch (arg)
{
case bool:
return (FloatType)*arg;
case ichar:
return *arg;
case short:
return *arg;
case int:
return *arg;
case long:
return *arg;
case int128:
return *arg;
case char:
return *arg;
case ushort:
return *arg;
case uint:
return *arg;
case ulong:
return *arg;
case uint128:
return *arg;
case float:
return (FloatType)*arg;
case double:
return (FloatType)*arg;
default:
return 0;
}
}
/**
* Read a simple integer value, typically for formatting.
*
* @param [inout] len_ptr "the length remaining."
* @param [in] buf "the buf to read from."
* @param maxlen "the maximum len that can be read."
* @return "The result of the atoi."
**/
private fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline
{
uint i = 0;
usz len = *len_ptr;
while (len < maxlen)
{
char c = buf[len];
if (c < '0' || c > '9') break;
i = i * 10 + c - '0';
len++;
}
*len_ptr = len;
return i;
}
private fn void! Formatter.out_substr(Formatter *this, char[] str)
{
usz l = conv::utf8_codepoints(str);
uint prec = this.prec;
if (this.flags.precision && l < prec) l = prec;
this.right_adjust(' ')?;
usz index = 0;
usz chars = str.len;
char* ptr = str.ptr;
while (index < chars)
{
char c = ptr[index];
// Break if we have precision set and we ran out...
if (c & 0xC0 != 0x80 && this.flags.precision && !prec--) break;
this.out(c)?;
index++;
}
return this.left_adjust(l);
}
union ConvUnion
{
ulong u;
double f;
}
private fn void! Formatter.etoa(Formatter* this, FloatType value)
{
// check for NaN and special values
if (value != value || value < -FloatType.max || value > FloatType.max)
{
return this.ftoa(value);
}
// determine the sign
bool negative = value < 0;
if (negative) value = -value;
// default precision
if (!this.flags.precision)
{
this.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// determine the decimal exponent
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
ConvUnion conv;
conv.f = (double)value;
int exp2 = (int)(conv.u >> 52 & 0x7FF) - 1023; // effectively log2
conv.u = (conv.u & (1u64 << 52 - 1)) | (1023u64 << 52); // drop the exponent so conv.F is now in [1,2)
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.f - 1.5) * 0.289529654602168);
// now we want to compute 10^expval but we want to be sure it won't overflow
exp2 = (int)(expval * 3.321928094887362 + 0.5);
double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
double z2 = z * z;
conv.u = (ulong)(exp2 + 1023) << 52;
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
conv.f *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
// correct for rounding errors
if (value < conv.f)
{
expval--;
conv.f /= 10;
}
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
uint minwidth = ((expval < 100) && (expval > -100)) ? 4 : 5;
// in "%g" mode, "prec" is the number of *significant figures* not decimals
if (this.flags.adapt_exp)
{
// do we want to fall-back to "%f" mode?
if (value >= 1e-4 && value < 1e6)
{
this.prec = this.prec > expval ? this.prec - expval - 1 : 0;
this.flags.precision = true; // make sure ftoa respects precision
// no characters in exponent
minwidth = 0;
expval = 0;
}
else
{
// we use one sigfig for the whole part
if (this.prec > 0 && this.flags.precision) this.prec--;
}
}
// Adjust width
uint fwidth = this.width > minwidth ? this.width - minwidth : 0;
// if we're padding on the right, DON'T pad the floating part
if (this.flags.left && minwidth) fwidth = 0;
// rescale the float value
if (expval) value /= conv.f;
// output the floating part
usz start_idx = this.idx;
PrintFlags old = this.flags;
this.flags.adapt_exp = false;
this.width = fwidth;
this.ftoa(negative ? -value : value)?;
this.flags = old;
// output the exponent part
if (minwidth)
{
// output the exponential symbol
this.out(this.flags.uppercase ? 'E' : 'e')?;
// output the exponent value
this.flags = { .zeropad = true, .plus = true };
this.width = minwidth - 1;
this.prec = 0;
this.ntoa((uint128)(expval < 0 ? -expval : expval), expval < 0, 10)?;
this.flags = old;
// might need to right-pad spaces
this.left_adjust(this.idx - start_idx)?;
}
}
// internal ftoa for fixed decimal floating point
private fn void! Formatter.ftoa(Formatter* this, FloatType value)
{
char[PRINTF_FTOA_BUFFER_SIZE] buf = void;
usz len = 0;
const FloatType[] POW10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
FloatType diff = 0.0;
// powers of 10
// test for special values
if (value != value)
{
return this.out_reverse("nan");
}
if (value < -FloatType.max)
{
return this.out_reverse("fni-");
}
if (value > FloatType.max)
{
if (this.flags.plus)
{
return this.out_reverse("fni+");
}
return this.out_reverse("fni");
}
// test for very large values
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
if (value > PRINTF_MAX_FLOAT || value < -PRINTF_MAX_FLOAT)
{
return this.etoa(value);
}
// test for negative
bool negative = value < 0;
if (negative) value = 0 - value;
// set default precision, if not set explicitly
if (!this.flags.precision) this.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while (this.prec > 9)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
this.prec--;
}
// Safe due to 1e9 limit.
int whole = (int)value;
FloatType tmp = (value - whole) * POW10[this.prec];
ulong frac = (ulong)tmp;
diff = tmp - frac;
switch (true)
{
case diff > 0.5:
++frac;
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
if (frac >= POW10[this.prec])
{
frac = 0;
++whole;
}
case diff < 0.5:
break;
case !frac && (frac & 1):
// if halfway, round up if odd OR if last digit is 0
++frac;
}
if (!this.prec)
{
diff = value - (FloatType)whole;
if ((!(diff < 0.5) || diff > 0.5) && (whole & 1))
{
// exactly 0.5 and ODD, then round up
// 1.5 -> 2, but 2.5 -> 2
++whole;
}
}
else
{
uint count = this.prec;
// now do fractional part, as an unsigned number
do
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
--count;
buf[len++] = (char)(48 + (frac % 10));
}
while (frac /= 10);
// add extra 0s
while (count-- > 0)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
}
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
// add decimal
buf[len++] = '.';
}
// do whole part, number is reversed
do
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = (char)(48 + (whole % 10));
}
while (whole /= 10);
// pad leading zeros
if (!this.flags.left && this.flags.zeropad)
{
if (this.width && (negative || this.flags.plus || this.flags.space)) this.width--;
while (len < this.width)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
}
}
char next = {|
if (negative) return '-';
if (this.flags.plus) return '+';
if (this.flags.space) return ' ';
return 0;
|};
if (next)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = next;
}
return this.out_reverse(buf[:len]);
}
private fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base)
{
char[PRINTF_NTOA_BUFFER_SIZE] buf = void;
usz len = 0;
// no hash for 0 values
if (!value) this.flags.hash = false;
// write if precision != 0 or value is != 0
if (!this.flags.precision || value)
{
char past_10 = (this.flags.uppercase ? 'A' : 'a') - 10;
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
char digit = (char)(value % base);
buf[len++] = digit + (digit < 10 ? '0' : past_10);
value /= base;
}
while (value);
}
return this.ntoa_format(buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
}
private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usz len, bool negative, uint base)
{
// pad leading zeros
if (!this.flags.left)
{
if (this.width && this.flags.zeropad && (negative || this.flags.plus || this.flags.space)) this.width--;
while (len < this.prec)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
}
while (this.flags.zeropad && len < this.width)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
}
}
// handle hash
if (this.flags.hash && base != 10)
{
if (!this.flags.precision && len && len == this.prec && len == this.width)
{
len--;
if (len) len--;
}
if (base != 10)
{
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
switch (base)
{
case 16:
buf[len++] = this.flags.uppercase ? 'X' : 'x';
case 8:
buf[len++] = this.flags.uppercase ? 'O' : 'o';
case 2:
buf[len++] = this.flags.uppercase ? 'B' : 'b';
default:
unreachable();
}
buf[len++] = '0';
}
}
switch (true)
{
case negative:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '-';
case this.flags.plus:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '+';
case this.flags.space:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = ' ';
}
if (!len) return;
return this.out_reverse(buf[:len]);
}
private fn void! Formatter.ntoa_variant(Formatter* this, variant arg, uint base)
{
bool is_neg;
uint128 val = int_from_variant(arg, &is_neg);
return this.ntoa(val, is_neg, base) @inline;
}
private fn void! Formatter.out_char(Formatter* this, variant arg)
{
uint l = 1;
// pre padding
this.right_adjust(l)?;
// char output
Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD;
switch (true)
{
case c < 0x7f:
this.out((char)c)?;
case c < 0x7ff:
this.out((char)(0xC0 | c >> 6))?;
this.out((char)(0x80 | (c & 0x3F)))?;
case c < 0xffff:
this.out((char)(0xE0 | c >> 12))?;
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
this.out((char)(0x80 | (c & 0x3F)))?;
default:
this.out((char)(0xF0 | c >> 18))?;
this.out((char)(0x80 | (c >> 12 & 0x3F)))?;
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
this.out((char)(0x80 | (c & 0x3F)))?;
}
return this.left_adjust(l);
}
private fn void! Formatter.out_reverse(Formatter* this, char[] buf)
{
usz buffer_start_idx = this.idx;
usz len = buf.len;
// pad spaces up to given width
if (!this.flags.left && !this.flags.zeropad)
{
for (usz i = len; i < this.width; i++)
{
this.out(' ')?;
}
}
// reverse string
while (len) this.out(buf[--len])?;
// append pad spaces up to given width
return this.left_adjust(this.idx - buffer_start_idx);
}
private fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline
{
usz val = ++(*index_ptr);
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!;
}
private fn variant! next_variant(variant* args_ptr, usz args_len, usz* arg_index_ptr) @inline
{
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG!;
return args_ptr[(*arg_index_ptr)++];
}
private fn int! printf_parse_format_field(variant* args_ptr, usz args_len, usz* args_index_ptr, char* format_ptr, usz format_len, usz* index_ptr) @inline
{
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);
if (catch intval) return FormattingFault.INVALID_WIDTH_ARG!;
return intval;
}

View File

@@ -1,497 +0,0 @@
module std::io;
import std::map;
import libc;
const int PRINTF_NTOA_BUFFER_SIZE = 256;
const int PRINTF_FTOA_BUFFER_SIZE = 256;
const float PRINTF_MAX_FLOAT = 1e9;
const uint PRINTF_DEFAULT_FLOAT_PRECISION = 6;
fault PrintFault
{
BUFFER_EXCEEDED,
INTERNAL_BUFFER_EXCEEDED,
INVALID_FORMAT_STRING,
MISSING_ARG,
}
define OutputFn = fn void!(char c, void* buffer);
define ToStringFunction = fn char[](void* value, Allocator *allocator);
define ToFormatFunction = fn void!(void* value, Formatter* formatter);
define FloatType = double;
private define StringFunctionMap = std::map::HashMap<typeid, ToStringFunction>;
private define FormatterFunctionMap = std::map::HashMap<typeid, ToFormatFunction>;
private FormatterFunctionMap toformat_functions;
private StringFunctionMap tostring_functions;
struct Formatter
{
void *data;
OutputFn out_fn;
struct
{
PrintFlags flags;
uint width;
uint prec;
usz idx;
}
}
bitstruct PrintFlags : uint
{
bool zeropad : 0;
bool left : 1;
bool plus : 2;
bool space : 3;
bool hash : 4;
bool uppercase : 5;
bool precision : 6;
bool adapt_exp : 7;
}
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(512);
}
toformat_functions.set($Type.typeid, (ToFormatFunction)&$Type.to_format);
$else:
if (!tostring_functions.table.len)
{
tostring_functions.init(512);
}
tostring_functions.set($Type.typeid, (ToStringFunction)&$Type.to_string);
$endif;
}
static initialize @priority(101)
{
if (!toformat_functions.table.len)
{
toformat_functions.init(512);
}
if (!tostring_functions.table.len)
{
tostring_functions.init(512);
}
}
private fn void! Formatter.out(Formatter* this, char c)
{
this.out_fn(c, this.data)?;
}
macro bool! Formatter.print_with_function(Formatter* this, variant arg)
{
if (try to_format = toformat_functions.get(arg.type))
{
PrintFlags old = this.flags;
uint old_width = this.width;
uint old_prec = this.prec;
defer
{
this.flags = old;
this.width = old_width;
this.prec = old_prec;
}
to_format(arg.ptr, this)?;
return true;
}
if (try to_string = tostring_functions.get(arg.type))
{
PrintFlags old = this.flags;
uint old_width = this.width;
uint old_prec = this.prec;
defer
{
this.flags = old;
this.width = old_width;
this.prec = old_prec;
}
@pool()
{
this.out_substr(to_string(arg.ptr, mem::temp_allocator()))?;
return true;
};
}
return false;
}
private fn void! Formatter.out_str(Formatter* this, variant arg)
{
switch (arg.type.kindof)
{
case TYPEID:
return this.out_substr("<typeid>");
case VOID:
return this.out_substr("void");
case ANYERR:
case FAULT:
return this.out_substr((*(anyerr*)arg.ptr).nameof);
case VARIANT:
return this.out_substr("<variant>");
case ENUM:
if (this.print_with_function(arg)?) return;
return this.out_substr(arg.type.names[types::variant_to_int(arg, usz)!!]);
case STRUCT:
if (this.print_with_function(arg)?) return;
return this.out_substr("<struct>");
case UNION:
if (this.print_with_function(arg)?) return;
return this.out_substr("<union>");
case BITSTRUCT:
if (this.print_with_function(arg)?) return;
return this.out_substr("<bitstruct>");
case FUNC:
if (this.print_with_function(arg)?) return;
return this.out_substr("<function>");
case OPTIONAL:
unreachable();
case DISTINCT:
if (this.print_with_function(arg)?) return;
if (arg.type == String.typeid)
{
return this.out_substr(((String*)arg).str());
}
return this.out_str(variant { arg.ptr, arg.type.inner });
case POINTER:
if (this.print_with_function(arg)?) return;
typeid inner = arg.type.inner;
if (inner.kindof == TypeKind.ARRAY && inner.inner == char.typeid)
{
char *ptr = *(char**)arg.ptr;
return this.out_substr(ptr[:inner.len]);
}
return this.ntoa_variant(arg, 16);
case SIGNED_INT:
case UNSIGNED_INT:
return this.ntoa_variant(arg, 10);
case FLOAT:
return this.ftoa(float_from_variant(arg));
case ARRAY:
if (this.print_with_function(arg)?) return;
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz len = arg.type.len;
// Pretend this is a char[]
void* ptr = (void*)arg.ptr;
this.out('[')?;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")?;
this.out_str(variant { ptr, inner })?;
ptr += size;
}
return this.out(']');
case VECTOR:
if (this.print_with_function(arg)?) return;
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz len = arg.type.len;
// Pretend this is a char[]
void* ptr = (void*)arg.ptr;
this.out_substr("[<")?;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")?;
this.out_str(variant { ptr, inner })?;
ptr += size;
}
return this.out_substr(">]");
case SUBARRAY:
if (this.print_with_function(arg)?) return;
// this is SomeType[] so grab the "SomeType"
typeid inner = arg.type.inner;
if (inner == char.typeid)
{
return this.out_substr(*(char[]*)arg);
}
usz size = inner.sizeof;
// Pretend this is a char[]
char[]* temp = (void*)arg.ptr;
void* ptr = (void*)temp.ptr;
usz len = temp.len;
this.out('[')?;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")?;
this.out_str(variant { ptr, inner })?;
ptr += size;
}
this.out(']')?;
case BOOL:
if (*(bool*)arg.ptr)
{
return this.out_substr("true");
}
else
{
return this.out_substr("false");
}
default:
if (this.print_with_function(arg)?) return;
return this.out_substr("Invalid type");
}
}
fault FormattingFault
{
UNTERMINATED_FORMAT,
MISSING_ARG,
INVALID_WIDTH_ARG,
INVALID_FORMAT_TYPE,
}
private fn void! out_buffer_fn(char c, void *data)
{
BufferData *buffer_data = data;
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED!;
buffer_data.buffer[buffer_data.written++] = c;
}
private fn void! out_null_fn(char c @unused, void* data @unused)
{
}
private fn void! out_putchar_fn(char c, void* data @unused)
{
libc::putchar(c);
}
private fn void! out_fputchar_fn(char c, void* data)
{
File* f = data;
f.putc(c)?;
}
private fn void! out_string_append_fn(char c, void* data)
{
String* s = data;
s.append_char(c);
}
fn usz! printf(char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
return formatter.vprintf(format, args);
}
fn usz! printfln(char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
usz len = formatter.vprintf(format, args)?;
putchar('\n');
return len + 1;
}
fn usz! String.printf(String* str, char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
return formatter.vprintf(format, args);
}
fn usz! String.printfln(String* str, char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
usz len = formatter.vprintf(format, args)?;
str.append('\n');
return len + 1;
}
private struct BufferData
{
char[] buffer;
usz written;
}
fn char[]! bprintf(char[] buffer, char[] format, args...) @maydiscard
{
Formatter formatter;
BufferData data = { .buffer = buffer };
formatter.init(&out_buffer_fn, &data);
usz size = formatter.vprintf(format, args)?;
return buffer[:size];
}
fn usz! File.printf(File file, char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn, &file);
return formatter.vprintf(format, args)?;
}
fn usz! File.printfln(File file, char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn, &file);
usz len = formatter.vprintf(format, args)?;
file.putc('\n')?;
file.flush();
return len + 1;
}
fn usz! Formatter.printf(Formatter* this, char[] format, args...)
{
return this.vprintf(format, args) @inline;
}
fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
{
if (!this.out_fn)
{
// use null output function
this.out_fn = &out_null_fn;
}
usz format_len = format.len;
usz variant_index = 0;
for (usz i = 0; i < format_len; i++)
{
// format specifier? %[flags][width][.precision][length]
char c = format[i];
if (c != '%')
{
// no
this.out(c)?;
continue;
}
i++;
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
c = format[i];
if (c == '%')
{
this.out(c)?;
continue;
}
// evaluate flags
this.flags = {};
while FLAG_EVAL: (true)
{
switch (c)
{
case '0': this.flags.zeropad = true;
case '-': this.flags.left = true;
case '+': this.flags.plus = true;
case ' ': this.flags.space = true;
case '#': this.flags.hash = true;
default: break FLAG_EVAL;
}
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
c = format[i];
}
// evaluate width field
int w = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
c = format[i];
if (w < 0)
{
this.flags.left = true;
w = -w;
}
this.width = w;
// evaluate precision field
this.prec = 0;
if (c == '.')
{
this.flags.precision = true;
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
int prec = printf_parse_format_field(variants.ptr, variants.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++];
switch (c)
{
case 'd':
base = 10;
this.flags.hash = false;
case 'X' :
this.flags.uppercase = true;
nextcase;
case 'x' :
base = 16;
case 'O':
this.flags.uppercase = true;
nextcase;
case 'o' :
base = 8;
case 'B':
this.flags.uppercase = true;
nextcase;
case 'b' :
base = 2;
case 'F' :
this.flags.uppercase = true;
nextcase;
case 'f':
this.ftoa(float_from_variant(current))?;
continue;
case 'E':
this.flags.uppercase = true;
nextcase;
case 'e':
this.etoa(float_from_variant(current))?;
continue;
case 'G':
this.flags.uppercase = true;
nextcase;
case 'g':
this.flags.adapt_exp = true;
this.etoa(float_from_variant(current))?;
continue;
case 'c':
this.out_char(current)?;
continue;
case 's':
this.out_str(current)?;
continue;
case 'p':
this.flags.zeropad = true;
this.flags.hash = true;
base = 16;
default:
return PrintFault.INVALID_FORMAT_STRING!;
}
if (base != 10)
{
this.flags.plus = false;
this.flags.space = false;
}
// ignore '0' flag when precision is given
if (this.flags.precision) this.flags.zeropad = false;
bool is_neg;
uint128 v = int_from_variant(current, &is_neg);
this.ntoa(v, is_neg, base)?;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return this.idx;
}

View File

@@ -1,47 +0,0 @@
module std::io::os;
import libc;
$if (env::OS_TYPE == OsType.WIN32):
extern fn Char16* _wgetcwd(Char16* buffer, int maxlen);
extern fn usz wcslen(Char16* str);
macro char[]! getcwd(Allocator* allocator = mem::default_allocator())
{
const DEFAULT_BUFFER = 256;
Char16[DEFAULT_BUFFER] buffer;
Char16 *res = _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);
free = true;
}
Char16[] str16 = res[:wcslen(res)];
return str::utf16to8(str16, allocator);
}
$else:
extern fn ZString _getcwd(char* pwd, usz len) @extname("getcwd");
macro char[]! getcwd(Allocator* allocator = mem::default_allocator())
{
const usz DEFAULT_BUFFER = 256;
char[DEFAULT_BUFFER] buffer;
ZString res = _getcwd(&buffer, DEFAULT_BUFFER);
bool free = false;
if (!res)
{
// Improve error
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR!;
res = _getcwd(null, 0);
free = true;
}
defer if (free) libc::free((void*)res);
char[] copy = str::copyz(res.as_str(), allocator);
return copy;
}
$endif;

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

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

View File

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

View File

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

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::array::linkedlist<Type>;
import std::mem;
private struct Node
{
@@ -12,7 +13,7 @@ private struct Node
struct LinkedList
{
usz size;
usize size;
Node *first;
Node *last;
}
@@ -25,7 +26,7 @@ fn void LinkedList.push(LinkedList *list, Type value)
private fn void LinkedList.linkFirst(LinkedList *list, Type value)
{
Node *first = list.first;
Node *new_node = mem::alloc(Node);
Node *new_node = @mem::malloc(Node);
*new_node = { .next = first, .value = value };
list.first = new_node;
if (!first)
@@ -42,7 +43,7 @@ private fn void LinkedList.linkFirst(LinkedList *list, Type value)
private fn void LinkedList.linkLast(LinkedList *list, Type value)
{
Node *last = list.last;
Node *new_node = mem::alloc(Node);
Node *new_node = mem::alloc(Node.sizeof);
*new_node = { .prev = last, .value = value };
list.last = new_node;
if (!last)
@@ -61,7 +62,7 @@ fn void LinkedList.free(LinkedList *list)
for (Node* node = list.first; node != null;)
{
Node* next = node.next;
free(node);
mem::free(node);
node = next;
}
list.first = null;
@@ -69,12 +70,12 @@ fn void LinkedList.free(LinkedList *list)
list.size = 0;
}
fn usz LinkedList.len(LinkedList* list) @inline
fn usize LinkedList.len(LinkedList* list) @inline
{
return list.size;
}
fn Type LinkedList.get(LinkedList* list, usz index)
fn Type LinkedList.get(LinkedList* list, usize index)
{
Node* node = list.first;
while (index--)
@@ -89,7 +90,7 @@ fn Type LinkedList.get(LinkedList* list, usz index)
private fn void LinkedList.linkBefore(LinkedList *list, Node *succ, Type value)
{
Node* pred = succ.prev;
Node* new_node = mem::alloc(Node);
Node* new_node = @mem::malloc(Node);
*new_node = { .prev = pred, .next = succ, .value = value };
succ.prev = new_node;
if (!pred)
@@ -109,7 +110,7 @@ private fn void LinkedList.linkBefore(LinkedList *list, Node *succ, Type value)
private fn void unlinkFirst(LinkedList* list, Node* f)
{
Node* next = f.next;
free(f);
mem::free(f);
list.first = next;
if (!next)
{
@@ -129,7 +130,7 @@ private fn void LinkedList.unlinkLast(LinkedList *list, Node* l)
{
Node* prev = l.prev;
list.last = prev;
free(l);
mem::free(l);
if (!prev)
{
list.first = null;
@@ -164,6 +165,6 @@ private fn void LinkedList.unlink(LinkedList* list, Node* x)
{
next.prev = prev;
}
free(x);
mem::free(x);
list.size--;
}

View File

@@ -2,92 +2,71 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::array::list<Type>;
import std::math;
import std::mem;
struct List
{
usz size;
usz capacity;
Allocator *allocator;
usize size;
usize capacity;
Type *entries;
}
/**
* @require allocator != null "A valid allocator must be provided"
**/
fn void List.init(List* list, usz initial_capacity = 16, Allocator* allocator = mem::current_allocator())
private fn void List.ensureCapacity(List *list) @inline
{
list.allocator = allocator;
list.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
list.entries = allocator.alloc_aligned(Type.sizeof * initial_capacity, Type[1].alignof)!!;
}
else
{
list.entries = null;
}
list.capacity = initial_capacity;
if (list.capacity == list.size)
{
list.capacity = list.capacity ? 2 * list.capacity : 16;
list.entries = mem::realloc(list.entries, Type.sizeof * list.capacity);
}
}
fn void List.tinit(List* list, usz initial_capacity = 16)
{
list.init(initial_capacity, mem::temp_allocator()) @inline;
}
fn void List.push(List* list, Type element) @inline
fn void List.push(List *list, Type element) @inline
{
list.append(element);
}
fn void List.append(List* list, Type element)
fn void List.append(List *list, Type element)
{
list.ensure_capacity();
list.ensureCapacity();
list.entries[list.size++] = element;
}
/**
* @require list.size > 0
*/
fn Type List.pop(List* list)
fn Type List.pop(List *list)
{
return list.entries[--list.size];
}
fn void List.clear(List* list)
{
list.size = 0;
}
/**
* @require list.size > 0
*/
fn Type List.pop_first(List* list)
fn Type List.popFirst(List *list)
{
Type value = list.entries[0];
list.remove_at(0);
list.removeAt(0);
return value;
}
fn void List.remove_at(List* list, usz index)
fn void List.removeAt(List *list, usize index)
{
for (usz i = index + 1; i < list.size; i++)
for (usize i = index + 1; i < list.size; i++)
{
list.entries[i - 1] = list.entries[i];
}
list.size--;
}
fn void List.push_front(List* list, Type type) @inline
fn void List.pushFront(List *list, Type type) @inline
{
list.insert_at(0, type);
list.insertAt(0, type);
}
fn void List.insert_at(List* list, usz index, Type type)
fn void List.insertAt(List *list, usize index, Type type)
{
list.ensure_capacity();
for (usz i = list.size; i > index; i--)
list.ensureCapacity();
for (usize i = list.size; i > index; i--)
{
list.entries[i] = list.entries[i - 1];
}
@@ -95,83 +74,55 @@ fn void List.insert_at(List* list, usz index, Type type)
list.entries[index] = type;
}
fn void List.remove_last(List* list)
fn void List.removeLast(List *list)
{
list.size--;
}
fn void List.remove_first(List* list)
fn void List.removeFirst(List *list)
{
list.remove_at(0);
list.removeAt(0);
}
fn Type* List.first(List* list)
fn Type* List.first(List *list)
{
return list.size ? &list.entries[0] : null;
}
fn Type* List.last(List* list)
fn Type* List.last(List *list)
{
return list.size ? &list.entries[list.size - 1] : null;
}
fn bool List.is_empty(List* list)
{
return !list.size;
}
fn usz List.len(List* list) @operator(len)
fn bool List.isEmpty(List *list)
{
return list.size;
}
fn Type List.get(List* list, usz index)
fn usize List.len(List *list) @operator(len)
{
return list.size;
}
fn Type List.get(List *list, usize index)
{
return list.entries[index];
}
fn void List.free(List* list)
fn void List.free(List *list)
{
if (!list.allocator) return;
list.allocator.free_aligned(list.entries)!!;
mem::free(list.entries);
list.capacity = 0;
list.size = 0;
list.entries = null;
}
fn void List.swap(List* list, usz i, usz j)
{
@swap(list.entries[i], list.entries[j]);
}
/**
* Reserve at least min_capacity
**/
fn void List.reserve(List* list, usz min_capacity)
{
if (!min_capacity) return;
if (list.capacity >= min_capacity) return;
if (!list.allocator) list.allocator = mem::temp_allocator();
min_capacity = math::next_power_of_2(min_capacity);
list.entries = list.allocator.realloc_aligned(list.entries, Type.sizeof * min_capacity, Type[1].alignof) ?? null;
list.capacity = min_capacity;
}
macro Type List.@item_at(List &list, usz index) @operator([])
macro Type List.item_at(List &list, usize index) @operator(elementat)
{
return list.entries[index];
}
fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
macro Type* List.item_ref(List &list, usize index) @operator(elementref)
{
return &list.entries[index];
}
private fn void List.ensure_capacity(List* list) @inline
{
if (list.capacity == list.size)
{
list.reserve(list.capacity ? 2 * list.capacity : 16);
}
}

View File

@@ -1,341 +0,0 @@
module std::map<Key, Value>;
import std::math;
const uint DEFAULT_INITIAL_CAPACITY = 16;
const uint MAXIMUM_CAPACITY = 1u << 31;
const float DEFAULT_LOAD_FACTOR = 0.75;
private struct Entry
{
uint hash;
Key key;
Value value;
Entry* next;
}
struct HashMap
{
Entry*[] table;
Allocator* allocator;
uint count; // Number of elements
uint threshold; // Resize limit
float load_factor;
}
/**
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
* @require allocator != null "The allocator must be non-null"
**/
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = mem::current_allocator())
{
capacity = math::next_power_of_2(capacity);
map.allocator = allocator;
map.load_factor = load_factor;
map.threshold = (uint)(capacity * load_factor);
map.table = array::make(Entry*, capacity, allocator);
}
/**
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn void HashMap.tinit(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
map.init(capacity, load_factor, mem::temp_allocator());
}
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* allocator = mem::current_allocator())
{
map.init(other_map.table.len, other_map.load_factor, allocator);
map.put_all_for_create(other_map);
}
fn void HashMap.tinit_from_map(HashMap* map, HashMap* other_map)
{
map.init_from_map(other_map, mem::temp_allocator()) @inline;
}
fn bool HashMap.is_empty(HashMap* map) @inline
{
return !map.count;
}
fn Value*! HashMap.get_ref(HashMap* map, Key key)
{
if (!map.count) return SearchResult.MISSING!;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return SearchResult.MISSING!;
}
/**
* Get the value or update and
**/
macro Value HashMap.@get_or_set(HashMap* map, Key key, Value #expr)
{
if (!map.count)
{
Value val = #expr;
map.set(key, val);
return val;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e.value;
}
Value val = #expr;
map.add_entry(hash, key, val, index);
return val;
}
fn Value! HashMap.get(HashMap* map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
fn bool HashMap.has_key(HashMap* map, Key key)
{
return try(map.get_ref(key));
}
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!map.allocator)
{
map.init();
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return true;
}
}
map.add_entry(hash, key, value, index);
return false;
}
fn void! HashMap.remove(HashMap* map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING!;
}
fn void HashMap.clear(HashMap* map)
{
if (!map.count) return;
foreach (Entry** &entry_ref : map.table)
{
Entry* entry = *entry_ref;
if (!entry) continue;
map.free(entry);
*entry_ref = null;
}
map.count = 0;
}
fn void HashMap.destroy(HashMap* map)
{
if (!map.allocator) return;
map.clear();
map.free(map.table.ptr);
map.table = Entry*[] {};
}
fn Key[] HashMap.key_tlist(HashMap* map)
{
return map.key_list(mem::temp_allocator()) @inline;
}
fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = mem::current_allocator())
{
if (!map.count) return Key[] {};
Key[] list = array::make(Key, map.count, allocator);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.key;
entry = entry.next;
}
}
return list;
}
fn Value[] HashMap.value_tlist(HashMap* map)
{
return map.value_list(mem::temp_allocator()) @inline;
}
fn Value[] HashMap.value_list(HashMap* map, Allocator* allocator = mem::current_allocator())
{
if (!map.count) return Value[] {};
Value[] list = array::make(Value, map.count, allocator);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.value;
entry = entry.next;
}
}
return list;
}
$if (types::is_equatable(Value)):
fn bool HashMap.has_value(HashMap* map, Value v)
{
if (!map.count) return false;
foreach (Entry* entry : map.table)
{
while (entry)
{
if (equals(v, entry.value)) return true;
entry = entry.next;
}
}
return false;
}
$endif;
// --- private methods
private fn void HashMap.add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index)
{
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
}
}
private fn void HashMap.resize(HashMap* map, uint new_capacity)
{
Entry*[] old_table = map.table;
uint old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
map.threshold = uint.max;
return;
}
Entry*[] new_table = array::make(Entry*, new_capacity, map.allocator);
map.transfer(new_table);
map.table = new_table;
map.free(old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
private fn uint rehash(uint hash) @inline
{
hash ^= (hash >> 20) ^ (hash >> 12);
return hash ^ ((hash >> 7) ^ (hash >> 4));
}
private macro uint index_for(uint hash, uint capacity)
{
return hash & (capacity - 1);
}
private fn void HashMap.transfer(HashMap* map, Entry*[] new_table)
{
Entry*[] src = map.table;
uint new_capacity = new_table.len;
foreach (uint j, Entry *e : src)
{
if (!e) continue;
do
{
Entry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
private fn void HashMap.put_all_for_create(HashMap* map, HashMap* other_map)
{
if (!other_map.count) return;
foreach (Entry *e : other_map.table)
{
if (!e) continue;
map.put_for_create(e.key, e.value);
}
}
private fn void HashMap.put_for_create(HashMap* map, Key key, Value value)
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
for (Entry *e = map.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return;
}
}
map.create_entry(hash, key, value, i);
}
private fn void HashMap.free(HashMap* map, void* ptr)
{
map.allocator.free(ptr)!!;
}
private fn bool HashMap.remove_entry_for_key(HashMap* map, Key key)
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
Entry* prev = map.table[i];
Entry* e = prev;
while (e)
{
Entry *next = e.next;
if (e.hash == hash && equals(key, e.key))
{
map.count--;
if (prev == e)
{
map.table[i] = next;
}
else
{
prev.next = next;
}
map.free(e);
return true;
}
prev = e;
e = next;
}
return false;
}
private fn void HashMap.create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index)
{
Entry *e = map.table[bucket_index];
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
map.table[bucket_index] = entry;
map.count++;
}

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::math;
import std::math::complex;
// TODO Define these using quad precision.
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
@@ -54,15 +53,13 @@ const DOUBLE_MIN_10_EXP = -307;
const DOUBLE_MAX_EXP = 1024;
const DOUBLE_MIN_EXP = -1021;
const DOUBLE_EPSILON = 2.22044604925031308085e-16;
const QUAD_MANT_DIG = 113;
/*
const QUAD_MAX = 1.18973149535723176508575932662800702e+4932;
const QUAD_MIN = 3.36210314311209350626267781732175260e-4932;
const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966;
const QUAD_DIG = 33;
const QUAD_DEC_DIGITS = 36;
const QUAD_MANT_DIG = 113;
const QUAD_MAX_10_EXP = 4932;
const QUAD_MIN_10_EXP = -4931;
const QUAD_MAX_EXP = 16384;
@@ -70,396 +67,65 @@ const QUAD_MIN_EXP = -16481;
const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34;
*/
enum RoundingMode : int
macro max(x, y) @autoimport
{
TOWARD_ZERO,
TO_NEAREST,
TOWARD_INFINITY,
TOWARD_NEG_INFINITY
return x > y ? x : y;
}
define Complex32 = Complex<float>;
define Complex64 = Complex<double>;
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
**/
macro abs(x) = $$abs(x);
/**
* @require values::@is_int(x) `The input must be an integer`
**/
macro sign(x)
macro min(x, y) @autoimport
{
if (!x) return ($typeof(x))0;
return ($typeof(x))(x < 0 ? -1 : 1);
return x < y ? x : y;
}
$if (env::COMPILER_LIBC_AVAILABLE):
extern fn double _atan(double x) @extname("atan");
extern fn float _atanf(float x) @extname("atanf");
extern fn double _atan2(double x) @extname("atan2");
extern fn float _atan2f(float x) @extname("atan2f");
macro atan(x)
fn double log10(double x) @inline
{
$if ($typeof(x).typeid == float.typeid):
return _atanf(x);
$else:
return _atan(x);
$endif;
return $$log10(x);
}
macro atan2(x, y)
fn double log2(double x) @inline
{
$if ($typeof(x).typeid == float.typeid && $typeof(y).typeid == float.typeid):
return _atan2f(x);
$else:
return _atan2(x);
$endif;
return $$log2(x);
}
$endif;
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro ceil(x) = $$ceil(x);
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
* @require types::@has_same(x, lower, upper) `The input types must be equal`
**/
macro clamp(x, lower, upper) = $$max(lower, $$min(x, upper));
/**
* @require values::@is_floatlike(mag) `The input must be a floating point value or float vector`
* @require types::is_same($typeof(mag), $typeof(sgn)) `The input types must be equal`
**/
macro copysign(mag, sgn) = $$copysign(mag, sgn);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro cos(x) = $$cos(x);
macro cosec(x) = 1 / sin(x);
macro cosech(x) = 2 / (exp(x) - exp(-x));
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro cosh(x) = (exp(x) + exp(-x)) / 2.0;
macro cotan(x) = cos(x) / sin(x);
macro cotanh(x) = (exp(2.0 * x) + 1.0) / (exp(2.0 * x) - 1.0);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro exp(x) = $$exp(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro exp2(x) = $$exp2(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro floor(x) = $$floor(x);
/**
* @require values::@is_floatlike(a) `The input must be a floating point value or float vector`
* @require types::@has_same(a, b, c) `The input types must be equal`
**/
macro fma(a, b, c) = $$fma(a, b, c);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
* @require values::@is_floatlike(y) `The input must be a floating point value or float vector`
**/
macro hypot(x, y) = sqrt(sqr(x) + sqr(y));
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro log(x) = $$log(x);
/**
* @require values::is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro log2(x) = $$log2(x);
/**
* @require values::is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro log10(x) = $$log10(x);
/**
* @require types::is_numerical($typeof(x)) `The input must be a floating point value or float vector`
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
**/
macro max(x, y, ...)
fn double log(double x) @inline
{
$if ($vacount == 0):
return $$max(x, y);
$else:
var m = $$max(x, y);
$for (var $i = 0; $i < $vacount; $i++):
m = $$max(m, $vaarg($i));
$endfor;
return m;
$endif;
return $$log(x);
}
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
**/
macro min(x, y, ...)
fn double cos(double x) @inline
{
$if ($vacount == 0):
return $$min(x, y);
$else:
var m = $$min(x, y);
$for (var $i = 0; $i < $vacount; $i++):
m = $$min(m, $vaarg($i));
$endfor;
return m;
$endif;
return $$cos(x);
}
/**
* @require types::@is_float(a) `The input must be a floating point value`
* @require types::@has_same(a, b, c) `The input types must be equal`
**/
macro muladd(a, b, c) = $$fmuladd(a, b, c);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro nearbyint(x) = $$nearbyint(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
* @require values::@convertable_to(exp, x) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
**/
macro pow(x, exp)
fn double sin(double x) @inline
{
$if (types::is_floatlike($typeof(exp))):
return $$pow(x, ($typeof(x))exp);
$else:
return $$pow_int(x, exp);
$endif;
return $$sin(x);
}
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro rint(x) = $$rint(x);
fn double exp(double x) @inline
{
return $$exp(x);
}
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro round(x) = $$round(x);
fn double pow(double x, double y) @inline
{
return $$pow(x, y);
}
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro roundeven(x) = $$roundeven(x);
fn double fabs(double x) @inline
{
return $$fabs(x);
}
macro sec(x) = 1 / cos(x);
fn double trunc(double x) @inline
{
return $$trunc(x);
}
macro sech(x) = 2 / (exp(x) + exp(-x));
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro sin(x) = $$sin(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro sinh(x) = (exp(x) - exp(-x)) / 2.0;
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro sqr(x) = x * x;
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro sqrt(x) = $$sqrt(x);
macro tan(x) = sin(x) / cos(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro tanh(x) = (exp(2.0 * x) - 1.0) / (exp(2.0 * x) + 1.0);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro trunc(x) = $$trunc(x);
macro float float.ceil(float x) = $$ceil(x);
macro float float.clamp(float x, float lower, float upper) = $$max(lower, $$min(x, upper));
macro float float.copysign(float mag, float sgn) = $$copysign(mag, sgn);
macro float float.floor(float x) = $$floor(x);
macro float float.fma(float a, float b, float c) = $$fma(a, b, c);
macro float float.muladd(float a, float b, float c) = $$fmuladd(a, b, c);
macro float float.nearbyint(float x) = $$nearbyint(x);
macro float float.pow(float x, exp) = pow(x, exp);
macro float float.rint(float x) = $$rint(x);
macro float float.round(float x) = $$round(x);
macro float float.roundeven(float x) = $$roundeven(x);
macro float float.trunc(float x) = $$trunc(x);
macro float float[<*>].sum(float[<*>] x, float start = 0.0) = $$reduce_fadd(x, start);
macro float float[<*>].product(float[<*>] x, float start = 1.0) = $$reduce_fmul(x, start);
macro float float[<*>].max(float[<*>] x) = $$reduce_max(x);
macro float float[<*>].min(float[<*>] x) = $$reduce_min(x);
macro float[<*>] float[<*>].ceil(float[<*>] x) = $$ceil(x);
macro float[<*>] float[<*>].clamp(float[<*>] x, float[<*>] lower, float[<*>] upper) = $$max(lower, $$min(x, upper));
macro float[<*>] float[<*>].copysign(float[<*>] mag, float[<*>] sgn) = $$copysign(mag, sgn);
macro float[<*>] float[<*>].fma(float[<*>] a, float[<*>] b, float[<*>] c) = $$fma(a, b, c);
macro float[<*>] float[<*>].floor(float[<*>] x) = $$floor(x);
macro float[<*>] float[<*>].nearbyint(float[<*>] x) = $$nearbyint(x);
macro float[<*>] float[<*>].pow(float[<*>] x, exp) = pow(x, exp);
macro float[<*>] float[<*>].rint(float[<*>] x) = $$rint(x);
macro float[<*>] float[<*>].round(float[<*>] x) = $$round(x);
macro float[<*>] float[<*>].roundeven(float[<*>] x) = $$roundeven(x);
macro float[<*>] float[<*>].trunc(float[<*>] x) = $$trunc(x);
macro double double.ceil(double x) = $$ceil(x);
macro double double.clamp(double x, double lower, double upper) = $$max(lower, $$min(x, upper));
macro double double.copysign(double mag, double sgn) = $$copysign(mag, sgn);
macro double double.floor(double x) = $$floor(x);
macro double double.fma(double a, double b, double c) = $$fma(a, b, c);
macro double double.muladd(double a, double b, double c) = $$fmuladd(a, b, c);
macro double double.nearbyint(double x) = $$nearbyint(x);
macro double double.pow(double x, exp) = pow(x, exp);
macro double double.rint(double x) = $$rint(x);
macro double double.round(double x) = $$round(x);
macro double double.roundeven(double x) = $$roundeven(x);
macro double double.trunc(double x) = $$trunc(x);
macro double double[<*>].sum(double[<*>] x, double start = 0.0) = $$reduce_add(x, start);
macro double double[<*>].product(double[<*>] x, double start = 1.0) = $$reduce_mul(x, start);
macro double double[<*>].max(double[<*>] x) = $$reduce_fmax(x);
macro double double[<*>].min(double[<*>] x) = $$reduce_fmin(x);
macro double[<*>] double[<*>].ceil(double[<*>] x) = $$ceil(x);
macro double[<*>] double[<*>].clamp(double[<*>] x, double[<*>] lower, double[<*>] upper) = $$max(lower, $$min(x, upper));
macro double[<*>] double[<*>].copysign(double[<*>] mag, double[<*>] sgn) = $$copysign(mag, sgn);
macro double[<*>] double[<*>].floor(double[<*>] x) = $$floor(x);
macro double[<*>] double[<*>].fma(double[<*>] a, double[<*>] b, double[<*>] c) = $$fma(a, b, c);
macro double[<*>] double[<*>].nearbyint(double[<*>] x) = $$nearbyint(x);
macro double[<*>] double[<*>].pow(double[<*>] x, exp) = pow(x, exp);
macro double[<*>] double[<*>].rint(double[<*>] x) = $$rint(x);
macro double[<*>] double[<*>].round(double[<*>] x) = $$round(x);
macro double[<*>] double[<*>].roundeven(double[<*>] x) = $$roundeven(x);
macro double[<*>] double[<*>].trunc(double[<*>] x) = $$trunc(x);
macro ichar ichar[<*>].sum(ichar[<*>] x) = $$reduce_add(x);
macro ichar ichar[<*>].product(ichar[<*>] x) = $$reduce_mul(x);
macro ichar ichar[<*>].and(ichar[<*>] x) = $$reduce_and(x);
macro ichar ichar[<*>].or(ichar[<*>] x) = $$reduce_or(x);
macro ichar ichar[<*>].xor(ichar[<*>] x) = $$reduce_xor(x);
macro ichar ichar[<*>].max(ichar[<*>] x) = $$reduce_max(x);
macro ichar ichar[<*>].min(ichar[<*>] x) = $$reduce_min(x);
macro short short[<*>].sum(short[<*>] x) = $$reduce_add(x);
macro short short[<*>].product(short[<*>] x) = $$reduce_mul(x);
macro short short[<*>].and(short[<*>] x) = $$reduce_and(x);
macro short short[<*>].or(short[<*>] x) = $$reduce_or(x);
macro short short[<*>].xor(short[<*>] x) = $$reduce_xor(x);
macro short short[<*>].max(short[<*>] x) = $$reduce_max(x);
macro short short[<*>].min(short[<*>] x) = $$reduce_min(x);
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) = $$veccomplt(x, y);
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) = $$veccomple(x, y);
macro bool[<*>] int[<*>].comp_eq(int[<*>] x, int[<*>] y) = $$veccompeq(x, y);
macro bool[<*>] int[<*>].comp_gt(int[<*>] x, int[<*>] y) = $$veccompgt(x, y);
macro bool[<*>] int[<*>].comp_ge(int[<*>] x, int[<*>] y) = $$veccompge(x, y);
macro bool[<*>] int[<*>].comp_ne(int[<*>] x, int[<*>] y) = $$veccompne(x, y);
macro int int[<*>].sum(int[<*>] x) = $$reduce_add(x);
macro int int[<*>].product(int[<*>] x) = $$reduce_mul(x);
macro int int[<*>].and(int[<*>] x) = $$reduce_and(x);
macro int int[<*>].or(int[<*>] x) = $$reduce_or(x);
macro int int[<*>].xor(int[<*>] x) = $$reduce_xor(x);
macro int int[<*>].max(int[<*>] x) = $$reduce_max(x);
macro int int[<*>].min(int[<*>] x) = $$reduce_min(x);
macro long long[<*>].sum(long[<*>] x) = $$reduce_add(x);
macro long long[<*>].product(long[<*>] x) = $$reduce_mul(x);
macro long long[<*>].and(long[<*>] x) = $$reduce_and(x);
macro long long[<*>].or(long[<*>] x) = $$reduce_or(x);
macro long long[<*>].xor(long[<*>] x) = $$reduce_xor(x);
macro long long[<*>].max(long[<*>] x) = $$reduce_max(x);
macro long long[<*>].min(long[<*>] x) = $$reduce_min(x);
macro int128 int128[<*>].sum(int128[<*>] x) = $$reduce_add(x);
macro int128 int128[<*>].product(int128[<*>] x) = $$reduce_mul(x);
macro int128 int128[<*>].and(int128[<*>] x) = $$reduce_and(x);
macro int128 int128[<*>].or(int128[<*>] x) = $$reduce_or(x);
macro int128 int128[<*>].xor(int128[<*>] x) = $$reduce_xor(x);
macro int128 int128[<*>].max(int128[<*>] x) = $$reduce_max(x);
macro int128 int128[<*>].min(int128[<*>] x) = $$reduce_min(x);
macro bool bool[<*>].sum(bool[<*>] x) = $$reduce_add(x);
macro bool bool[<*>].product(bool[<*>] x) = $$reduce_mul(x);
macro bool bool[<*>].and(bool[<*>] x) = $$reduce_and(x);
macro bool bool[<*>].or(bool[<*>] x) = $$reduce_or(x);
macro bool bool[<*>].xor(bool[<*>] x) = $$reduce_xor(x);
macro bool bool[<*>].max(bool[<*>] x) = $$reduce_max(x);
macro bool bool[<*>].min(bool[<*>] x) = $$reduce_min(x);
macro char char[<*>].sum(char[<*>] x) = $$reduce_add(x);
macro char char[<*>].product(char[<*>] x) = $$reduce_mul(x);
macro char char[<*>].and(char[<*>] x) = $$reduce_and(x);
macro char char[<*>].or(char[<*>] x) = $$reduce_or(x);
macro char char[<*>].xor(char[<*>] x) = $$reduce_xor(x);
macro char char[<*>].max(char[<*>] x) = $$reduce_max(x);
macro char char[<*>].min(char[<*>] x) = $$reduce_min(x);
macro ushort ushort[<*>].sum(ushort[<*>] x) = $$reduce_add(x);
macro ushort ushort[<*>].product(ushort[<*>] x) = $$reduce_mul(x);
macro ushort ushort[<*>].and(ushort[<*>] x) = $$reduce_and(x);
macro ushort ushort[<*>].or(ushort[<*>] x) = $$reduce_or(x);
macro ushort ushort[<*>].xor(ushort[<*>] x) = $$reduce_xor(x);
macro ushort ushort[<*>].max(ushort[<*>] x) = $$reduce_max(x);
macro ushort ushort[<*>].min(ushort[<*>] x) = $$reduce_min(x);
macro uint uint[<*>].sum(uint[<*>] x) = $$reduce_add(x);
macro uint uint[<*>].product(uint[<*>] x) = $$reduce_mul(x);
macro uint uint[<*>].and(uint[<*>] x) = $$reduce_and(x);
macro uint uint[<*>].or(uint[<*>] x) = $$reduce_or(x);
macro uint uint[<*>].xor(uint[<*>] x) = $$reduce_xor(x);
macro uint uint[<*>].max(uint[<*>] x) = $$reduce_max(x);
macro uint uint[<*>].min(uint[<*>] x) = $$reduce_min(x);
macro ulong ulong[<*>].sum(ulong[<*>] x) = $$reduce_add(x);
macro ulong ulong[<*>].product(ulong[<*>] x) = $$reduce_mul(x);
macro ulong ulong[<*>].and(ulong[<*>] x) = $$reduce_and(x);
macro ulong ulong[<*>].or(ulong[<*>] x) = $$reduce_or(x);
macro ulong ulong[<*>].xor(ulong[<*>] x) = $$reduce_xor(x);
macro ulong ulong[<*>].max(ulong[<*>] x) = $$reduce_max(x);
macro ulong ulong[<*>].min(ulong[<*>] x) = $$reduce_min(x);
macro uint128 uint128[<*>].sum(uint128[<*>] x) = $$reduce_add(x);
macro uint128 uint128[<*>].product(uint128[<*>] x) = $$reduce_mul(x);
macro uint128 uint128[<*>].and(uint128[<*>] x) = $$reduce_and(x);
macro uint128 uint128[<*>].or(uint128[<*>] x) = $$reduce_or(x);
macro uint128 uint128[<*>].xor(uint128[<*>] x) = $$reduce_xor(x);
macro uint128 uint128[<*>].max(uint128[<*>] x) = $$reduce_max(x);
macro uint128 uint128[<*>].min(uint128[<*>] x) = $$reduce_min(x);
fn double ceil(double x) @inline
{
return $$ceil(x);
}
/**
* @checked x & 1
@@ -469,9 +135,5 @@ macro bool is_power_of_2(x)
return x != 0 && (x & (x - 1)) == 0;
}
macro next_power_of_2(x)
{
$typeof(x) y = 1;
while (y < x) y += y;
return y;
}

View File

@@ -1,31 +0,0 @@
module std::math::complex<Type>;
union Complex
{
struct
{
Type r, c;
}
Type[<2>] v;
}
macro Complex Complex.add(Complex a, Complex b)
{
return Complex { .v = a.v + b.v };
}
macro Complex Complex.sub(Complex a, Complex b)
{
return Complex { .v = a.v - b.v };
}
macro Complex Complex.mult(Complex a, Complex b)
{
return Complex { .r = a.r * b.r - a.c * b.c, .c = a.r * b.c + b.r * a.c };
}
fn Complex Complex.div(Complex a, Complex b)
{
Type div = b.r * b.r + b.c * b.c;
return Complex { .r = (a.r * b.r + a.c * b.c) / div, .c = (a.c * b.r - a.r * b.c) / div };
}

View File

@@ -1,431 +0,0 @@
module std::math::matrix;
fault MatrixError
{
MATRIX_INVERSE_DOESNT_EXIST,
}
struct Matrix2x2
{
union
{
struct
{
float m00, m01;
float m10, m11;
}
float[4] m;
}
}
struct Matrix3x3
{
union
{
struct
{
float m00; float m01; float m02;
float m10; float m11; float m12;
float m20; float m21; float m22;
}
float[9] m;
}
}
struct Matrix4x4
{
union
{
struct
{
float m00, m01, m02, m03;
float m10, m11, m12, m13;
float m20, m21, m22, m23;
float m30, m31, m32, m33;
}
float[16] m;
}
}
fn float[<2>] Matrix2x2.apply(Matrix2x2* mat, float[<2>] vec)
{
return float[<2>] {
mat.m00 * vec[0] + mat.m01 * vec[1],
mat.m10 * vec[0] + mat.m11 * vec[1],
};
}
fn float[<3>] Matrix3x3.apply(Matrix3x3* mat, float[<3>] vec)
{
return float[<3>] {
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2],
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2],
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2],
};
}
fn float[<4>] Matrix4x4.apply(Matrix4x4* mat, float[<4>] vec)
{
return float[<4>] {
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2] + mat.m03 * vec[3],
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2] + mat.m13 * vec[3],
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2] + mat.m23 * vec[3],
mat.m30 * vec[0] + mat.m31 * vec[1] + mat.m32 * vec[2] + mat.m33 * vec[3],
};
}
fn Matrix2x2 Matrix2x2.mul(Matrix2x2* a, Matrix2x2 b)
{
return Matrix2x2 {
a.m00 * b.m00 + a.m01 * b.m10, a.m00 * b.m01 + a.m01 * b.m11,
a.m10 * b.m01 + a.m11 * b.m11, a.m10 * b.m01 + a.m11 * b.m11,
};
}
fn Matrix3x3 Matrix3x3.mul(Matrix3x3* a, Matrix3x3 b)
{
return Matrix3x3 {
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20,
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21,
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22,
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20,
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21,
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22,
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20,
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21,
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22,
};
}
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* a, Matrix4x4 b)
{
return Matrix4x4 {
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30,
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31,
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32,
a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33,
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30,
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31,
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32,
a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33,
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30,
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31,
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32,
a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33,
a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30,
a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31,
a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32,
a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33,
};
}
fn Matrix2x2 Matrix2x2.component_mul(Matrix2x2* mat, float s)
{
return Matrix2x2 {
mat.m00 * s, mat.m01 * s,
mat.m10 * s, mat.m11 * s,
};
}
fn Matrix3x3 Matrix3x3.component_mul(Matrix3x3* mat, float s)
{
return Matrix3x3 {
mat.m00 * s, mat.m01 * s, mat.m02 * s,
mat.m10 * s, mat.m11 * s, mat.m12 * s,
mat.m20 * s, mat.m21 * s, mat.m22 * s,
};
}
fn Matrix4x4 Matrix4x4.component_mul(Matrix4x4* mat, float s)
{
return Matrix4x4 {
mat.m00 * s, mat.m01 * s, mat.m02 * s, mat.m03 * s,
mat.m10 * s, mat.m11 * s, mat.m12 * s, mat.m13 * s,
mat.m20 * s, mat.m21 * s, mat.m22 * s, mat.m23 * s,
mat.m30 * s, mat.m31 * s, mat.m32 * s, mat.m33 * s,
};
}
fn Matrix2x2 Matrix2x2.transpose(Matrix2x2* mat)
{
return Matrix2x2 {
mat.m00, mat.m10,
mat.m01, mat.m11
};
}
fn Matrix3x3 Matrix3x3.transpose(Matrix3x3* mat)
{
return Matrix3x3 {
mat.m00, mat.m10, mat.m20,
mat.m01, mat.m11, mat.m21,
mat.m02, mat.m12, mat.m22,
};
}
fn Matrix4x4 Matrix4x4.transpose(Matrix4x4* mat)
{
return Matrix4x4 {
mat.m00, mat.m10, mat.m20, mat.m30,
mat.m01, mat.m11, mat.m21, mat.m31,
mat.m02, mat.m12, mat.m22, mat.m32,
mat.m03, mat.m13, mat.m23, mat.m33,
};
}
fn float Matrix2x2.determinant(Matrix2x2* mat)
{
return mat.m00 * mat.m11 - mat.m01 * mat.m10;
}
fn float Matrix3x3.determinant(Matrix3x3* mat)
{
return
mat.m00 * (mat.m11 * mat.m22 - mat.m21 * mat.m12) -
mat.m01 * (mat.m10 * mat.m22 - mat.m20 * mat.m12) +
mat.m02 * (mat.m10 * mat.m21 - mat.m20 * mat.m11);
}
fn float Matrix4x4.determinant(Matrix4x4* mat)
{
return
mat.m00 * (mat.m11 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m12 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
mat.m13 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) ) -
mat.m01 * (mat.m10 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m12 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m13 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) ) +
mat.m02 * (mat.m10 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
mat.m11 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m13 * (mat.m20 * mat.m31 - mat.m30 * mat.m21) ) -
mat.m03 * (mat.m10 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
mat.m11 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
mat.m12 * (mat.m20 * mat.m31 - mat.m30 * mat.m21) );
}
fn Matrix2x2 Matrix2x2.adjoint(Matrix2x2* mat)
{
return Matrix2x2 { mat.m00, -mat.m01, -mat.m10, mat.m11 };
}
fn Matrix3x3 Matrix3x3.adjoint(Matrix3x3* mat)
{
return Matrix3x3 {
(mat.m11 * mat.m22 - mat.m21 * mat.m12),
-(mat.m10 * mat.m22 - mat.m20 * mat.m12),
(mat.m10 * mat.m21 - mat.m20 * mat.m11),
-(mat.m01 * mat.m22 - mat.m21 * mat.m02),
(mat.m00 * mat.m22 - mat.m20 * mat.m02),
-(mat.m00 * mat.m21 - mat.m20 * mat.m01),
(mat.m01 * mat.m12 - mat.m11 * mat.m02),
-(mat.m00 * mat.m12 - mat.m10 * mat.m02),
(mat.m00 * mat.m11 - mat.m10 * mat.m01),
};
}
fn Matrix4x4 Matrix4x4.adjoint(Matrix4x4* mat)
{
return Matrix4x4 {
(mat.m11 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m12 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
mat.m13 * (mat.m21 * mat.m32 - mat.m31 * mat.m22)),
-(mat.m10 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m12 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m13 * (mat.m20 * mat.m32 - mat.m30 * mat.m22)),
(mat.m10 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
mat.m11 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m13 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
-(mat.m10 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
mat.m11 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
mat.m12 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
-(mat.m01 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m02 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
mat.m03 * (mat.m21 * mat.m32 - mat.m31 * mat.m22)),
(mat.m00 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
mat.m02 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m03 * (mat.m20 * mat.m32 - mat.m30 * mat.m22)),
-(mat.m00 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
mat.m01 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
mat.m03 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
(mat.m00 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
mat.m01 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
mat.m02 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
(mat.m01 * (mat.m12 * mat.m33 - mat.m32 * mat.m13) -
mat.m02 * (mat.m11 * mat.m33 - mat.m31 * mat.m13) +
mat.m03 * (mat.m11 * mat.m32 - mat.m31 * mat.m12)),
-(mat.m00 * (mat.m12 * mat.m33 - mat.m32 * mat.m13) -
mat.m02 * (mat.m10 * mat.m33 - mat.m30 * mat.m13) +
mat.m03 * (mat.m10 * mat.m32 - mat.m30 * mat.m12)),
(mat.m00 * (mat.m11 * mat.m33 - mat.m31 * mat.m13) -
mat.m01 * (mat.m10 * mat.m33 - mat.m30 * mat.m13) +
mat.m03 * (mat.m10 * mat.m31 - mat.m30 * mat.m11)),
-(mat.m00 * (mat.m11 * mat.m32 - mat.m31 * mat.m12) -
mat.m01 * (mat.m10 * mat.m32 - mat.m30 * mat.m12) +
mat.m02 * (mat.m10 * mat.m31 - mat.m30 * mat.m11)),
-(mat.m01 * (mat.m12 * mat.m23 - mat.m22 * mat.m13) -
mat.m02 * (mat.m11 * mat.m23 - mat.m21 * mat.m13) +
mat.m03 * (mat.m11 * mat.m22 - mat.m21 * mat.m12)),
(mat.m00 * (mat.m12 * mat.m23 - mat.m22 * mat.m13) -
mat.m02 * (mat.m10 * mat.m23 - mat.m20 * mat.m13) +
mat.m03 * (mat.m10 * mat.m22 - mat.m20 * mat.m12)),
-(mat.m00 * (mat.m11 * mat.m23 - mat.m21 * mat.m13) -
mat.m01 * (mat.m10 * mat.m23 - mat.m20 * mat.m13) +
mat.m03 * (mat.m10 * mat.m21 - mat.m20 * mat.m11)),
(mat.m00 * (mat.m11 * mat.m22 - mat.m21 * mat.m12) -
mat.m01 * (mat.m10 * mat.m22 - mat.m20 * mat.m12) +
mat.m02 * (mat.m10 * mat.m21 - mat.m20 * mat.m11)),
};
}
fn Matrix2x2! Matrix2x2.inverse(Matrix2x2* m)
{
float det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
Matrix2x2 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
fn Matrix3x3! Matrix3x3.inverse(Matrix3x3* m)
{
float det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
Matrix3x3 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
fn Matrix4x4! Matrix4x4.inverse(Matrix4x4* m)
{
float det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
Matrix4x4 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
fn Matrix3x3 Matrix3x3.translate(Matrix3x3* m, float[<2>] v)
{
return m.mul(Matrix3x3 {
1, 0, v[0],
0, 1, v[1],
0, 0, 1,
});
}
fn Matrix4x4 Matrix4x4.translate(Matrix4x4* m, float[<3>] v)
{
return m.mul(Matrix4x4 {
1, 0, 0, v[0],
0, 1, 0, v[1],
0, 0, 1, v[2],
0, 0, 0, 1,
});
}
// r in radians
fn Matrix3x3 Matrix3x3.rotate(Matrix3x3* m, float r)
{
return m.mul(Matrix3x3 {
math::cos(r), -math::sin(r), 0,
math::sin(r), math::cos(r), 0,
0, 0, 1,
});
}
// r in radians
fn Matrix4x4 Matrix4x4.rotate_z(Matrix4x4* m, float r)
{
return m.mul(Matrix4x4 {
math::cos(r), -math::sin(r), 0, 0,
math::sin(r), math::cos(r), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
});
}
// r in radians
fn Matrix4x4 Matrix4x4.rotate_y(Matrix4x4* m, float r)
{
return m.mul(Matrix4x4 {
math::cos(r), 0, -math::sin(r), 0,
0, 1, 0, 0,
math::sin(r), 0, math::cos(r), 0,
0, 0, 0, 1,
});
}
// r in radians
fn Matrix4x4 Matrix4x4.rotate_x(Matrix4x4* m, float r)
{
return m.mul(Matrix4x4 {
1, 0, 0, 0,
0, math::cos(r), -math::sin(r), 0,
0, math::sin(r), math::cos(r), 0,
0, 0, 0, 1,
});
}
fn Matrix3x3 Matrix3x3.scale(Matrix3x3* m, float[<2>] v)
{
return m.mul(Matrix3x3 {
v[0], 0, 0,
0, v[1], 0,
0, 0, 1,
});
}
fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, float[<3>] v)
{
return m.mul(Matrix4x4 {
v[0], 0, 0, 0,
0, v[1], 0, 0,
0, 0, v[2], 0,
0, 0, 0, 1,
});
}
fn Matrix4x4 ortho(float left, float right, float top, float bottom, float near, float far)
{
float width = right - left;
float height = top - bottom;
float depth = far - near;
return Matrix4x4 {
2 / width, 0, 0, 0,
0, 2 / height, 0, 0,
0, 0, -2 / depth, 0,
-(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1,
};
}
// fov in radians
fn Matrix4x4 perspective(float fov, float aspect_ratio, float near, float far)
{
float top = ((float)math::sin(fov / 2) / (float)math::cos(fov / 2)) * near;
float right = top * aspect_ratio;
float depth = far - near;
return Matrix4x4 {
1 / right, 0, 0, 0,
0, 1 / top, 0, 0,
0, 0, -2 / depth, 0,
0, 0, - (far + near) / depth, 1,
};
}

View File

@@ -5,7 +5,7 @@ struct SimpleRandom
long seed;
}
private const long SIMPLE_RANDOM_MULTIPLIER = 0x5DEECE66D;
private const long SIMPLE_RANDOM_MULTIPLIER = 0x5DEECE66Di64;
private const long SIMPLE_RANDOM_ADDEND = 0xB;
private const long SIMPLE_RANDOM_MASK = (1i64 << 48) - 1;

View File

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

209
lib/std/mem.c3 Normal file
View File

@@ -0,0 +1,209 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::mem;
macro volatile_load(&x)
{
return $$volatile_load(&x);
}
macro volatile_store(&x, y)
{
return $$volatile_store(&x, y);
}
/**
* @require @math::is_power_of_2(alignment)
**/
fn usize aligned_offset(usize offset, usize alignment)
{
return alignment * ((offset + alignment - 1) / alignment);
}
/**
* @require @math::is_power_of_2(alignment)
**/
fn bool ptr_is_aligned(void* ptr, usize alignment) @inline
{
return (uptr)ptr & ((uptr)alignment - 1) == 0;
}
fn void copy(char* dst, char* src, usize size) @inline
{
@memcpy(dst, src, size);
}
macro void memcpy(void* dst, void* src, usize size, bool $is_volatile = false, usize $dst_align = 0, usize $src_align = 0)
{
$$memcpy(dst, src, size, $is_volatile, $dst_align, $src_align);
}
fn void set(void* dst, char val, usize bytes) @inline
{
@memset(dst, val, bytes);
}
macro void memset(void* dst, char val, usize bytes, bool $is_volatile = false, usize $dst_align = 0)
{
$$memset(dst, val, bytes, $is_volatile, $dst_align);
}
macro bitcast(expr, $Type)
{
var $size = (usize)($sizeof(expr));
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
$Type x = void;
@memcpy(&x, &expr, $size, false, $alignof($Type), $alignof(expr));
return x;
}
enum AllocationKind
{
ALLOC,
CALLOC,
REALLOC,
FREE,
RESET,
}
fault AllocationFailure
{
OUT_OF_MEMORY,
UNSUPPORTED_OPERATION,
}
private tlocal Allocator thread_allocator = { null, SYSTEM_ALLOCATOR };
struct Allocator
{
void* data;
AllocatorFunction function;
}
macro malloc($Type)
{
return ($Type*)(mem::alloc($Type.sizeof));
}
fn char[] alloc_bytes(usize bytes) @inline
{
return ((char*)thread_allocator.alloc(bytes, 1))[0..bytes - 1]!!;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void* alloc(usize size, usize alignment = 0)
{
return thread_allocator.alloc(size, alignment)!!;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! alloc_checked(usize size, usize alignment = 0)
{
return thread_allocator.alloc(size, alignment);
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void* calloc(usize size, usize alignment = 0)
{
return thread_allocator.calloc(size, alignment)!!;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! calloc_checked(usize size, usize alignment = 0)
{
return thread_allocator.calloc(size, alignment);
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void* realloc(void *ptr, usize new_size, usize alignment = 0)
{
return thread_allocator.realloc(ptr, new_size, alignment)!!;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! realloc_checked(void *ptr, usize new_size, usize alignment = 0)
{
return thread_allocator.realloc(ptr, new_size, alignment);
}
fn void free(void* ptr)
{
return thread_allocator.free(ptr)!!;
}
/**
* Run with a specific allocator inside of the macro body.
**/
macro void with_allocator(Allocator allocator; @body())
{
Allocator old_allocator = thread_allocator;
thread_allocator = allocator;
defer thread_allocator = old_allocator;
@body();
}
fn void*! talloc(usize size)
{
return temp_allocator.alloc(size);
}
struct MemoryArena
{
void* memory;
usize total;
usize used;
}
/**
* @require alignment > 0 `alignment must be non zero`
* @require @math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
* @require this != null
**/
fn void*! MemoryArena.alloc(MemoryArena* this, usize size, usize alignment, usize prefixed_bytes = 0)
{
void* start_mem = this.memory;
void* unaligned_pointer = start_mem + this.used + prefixed_bytes;
if ((uptr)unaligned_pointer < (uptr)start_mem) return AllocationFailure.OUT_OF_MEMORY!;
usize offset_start = aligned_offset((usize)(uptr)unaligned_pointer, alignment) - (usize)(uptr)start_mem;
usize end = offset_start + size;
if (end > this.total || end < offset_start) return AllocationFailure.OUT_OF_MEMORY!;
this.used = end;
return start_mem + offset_start;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void MemoryArena.init(MemoryArena* this, char[] data)
{
this.memory = data.ptr;
this.total = data.len;
this.used = 0;
}
/**
* @require this != null
**/
fn void MemoryArena.reset(MemoryArena* this)
{
this.used = 0;
}

355
lib/std/mem_allocator.c3 Normal file
View File

@@ -0,0 +1,355 @@
module std::mem;
define AllocatorFunction = fn void*!(void* alloc_data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind);
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = $alignof(void*) * 2;
const DEFAULT_SIZE_PREFIX = usize.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = $alignof(usize);
Allocator main_allocator = { null, SYSTEM_ALLOCATOR };
const AllocatorFunction NULL_ALLOCATOR = &null_allocator_fn;
const AllocatorFunction SYSTEM_ALLOCATOR = &libc_allocator_fn;
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! Allocator.alloc(Allocator *allocator, usize size, usize alignment = 0) @inline
{
return allocator.function(allocator.data, size, alignment, null, ALLOC);
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! Allocator.realloc(Allocator *allocator, void* old_pointer, usize size, usize alignment = 0) @inline
{
return allocator.function(allocator.data, size, alignment, old_pointer, REALLOC);
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! Allocator.calloc(Allocator *allocator, usize size, usize alignment = 0) @inline
{
return allocator.function(allocator.data, size, alignment, null, CALLOC);
}
fn void! Allocator.free(Allocator *allocator, void* old_pointer) @inline
{
allocator.function(allocator.data, 0, 0, old_pointer, FREE)?;
}
fn void Allocator.reset(Allocator *allocator)
{
allocator.function(allocator.data, 0, 0, null, RESET)!!;
}
fn Allocator arena_allocator(MemoryArena *arena)
{
return { arena, &arena_allocator_function };
}
fn Allocator dynamic_arena_allocator(DynamicArenaAllocator* allocator)
{
return { allocator, &dynamic_arena_allocator_function };
}
private fn usize alignment_for_allocation(usize alignment) @inline
{
if (alignment < DEFAULT_MEM_ALIGNMENT)
{
alignment = DEFAULT_SIZE_PREFIX_ALIGNMENT;
}
return alignment;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! arena_allocator_function(void* data, usize size, usize alignment, void* old_pointer, AllocationKind kind)
{
MemoryArena* arena = data;
switch (kind)
{
case CALLOC:
case ALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
alignment = alignment_for_allocation(alignment);
void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?;
*(usize*)(mem - DEFAULT_SIZE_PREFIX) = size;
if (kind == AllocationKind.CALLOC) mem::set(mem, 0, size);
return mem;
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator.");
if (size > arena.total) return AllocationFailure.OUT_OF_MEMORY!;
alignment = alignment_for_allocation(alignment);
usize* old_size_ptr = (usize*)(old_pointer - DEFAULT_SIZE_PREFIX);
usize old_size = *old_size_ptr;
// Do last allocation and alignment match?
if (arena.memory + arena.used == old_pointer + old_size && ptr_is_aligned(old_pointer, alignment))
{
if (old_size >= size)
{
*old_size_ptr = size;
arena.used -= old_size - size;
return old_pointer;
}
usize new_used = arena.used + size - old_size;
if (new_used > arena.total) return AllocationFailure.OUT_OF_MEMORY!;
arena.used = new_used;
*old_size_ptr = size;
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?;
*(usize*)(mem - DEFAULT_SIZE_PREFIX) = size;
copy(mem, old_pointer, old_size);
return mem;
case FREE:
if (!old_pointer) return null;
assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator.");
usize old_size = *(usize*)(old_pointer - DEFAULT_SIZE_PREFIX);
if (old_pointer + old_size == arena.memory + arena.used)
{
arena.used -= old_size;
}
return null;
case RESET:
arena.used = 0;
return null;
}
@unreachable();
}
struct DynamicArenaAllocator
{
Allocator backing_allocator;
DynamicArenaPage* page;
DynamicArenaPage* unused_page;
usize page_size;
}
private struct DynamicArenaPage
{
void* memory;
void* prev_arena;
usize total;
usize used;
void* last_ptr;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! dynamic_arena_allocator_function(void* data, usize size, usize alignment, void* old_pointer, AllocationKind kind)
{
DynamicArenaAllocator* allocator = data;
switch (kind)
{
case CALLOC:
assert(!old_pointer, "Unexpected no old pointer for calloc.");
if (!size) return null;
void* mem = allocator.alloc(size, alignment)?;
set(mem, 0, size);
return mem;
case ALLOC:
assert(!old_pointer, "Unexpected no old pointer for alloc.");
if (!size) return null;
return allocator.alloc(size, alignment);
case REALLOC:
if (!size)
{
if (!old_pointer) return null;
allocator.free(old_pointer);
return null;
}
if (!old_pointer) return allocator.alloc(size, alignment);
void* mem = allocator.realloc(old_pointer, size, alignment)?;
return mem;
case FREE:
if (!old_pointer) return null;
allocator.free(old_pointer);
return null;
case RESET:
allocator.reset();
return null;
}
@unreachable();
}
/**
* @require page_size >= 128
* @require this != null
**/
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator* backing_allocator = null)
{
this.page = null;
this.unused_page = null;
this.page_size = page_size;
this.backing_allocator = backing_allocator ? *backing_allocator : main_allocator;
}
/**
* @require this != null
**/
fn void DynamicArenaAllocator.destroy(DynamicArenaAllocator* this)
{
DynamicArenaPage* page = this.page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
this.backing_allocator.free(page);
page = next_page;
}
page = this.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
this.backing_allocator.free(page);
page = next_page;
}
this.page = null;
this.unused_page = null;
}
/**
* @require ptr && this
* @require this.page `tried to free pointer on invalid allocator`
*/
private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* ptr)
{
DynamicArenaPage* current_page = this.page;
if (ptr == current_page.last_ptr)
{
current_page.used = (usize)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory);
}
current_page.last_ptr = null;
}
/**
* @require old_pointer && size > 0
* @require this.page `tried to realloc pointer on invalid allocator`
*/
private fn void*! DynamicArenaAllocator.realloc(DynamicArenaAllocator* this, void* old_pointer, usize size, usize alignment)
{
DynamicArenaPage* current_page = this.page;
alignment = alignment_for_allocation(alignment);
usize* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
usize old_size = *old_size_ptr;
// We have the old pointer and it's correctly aligned.
if (old_size >= size && ptr_is_aligned(old_pointer, alignment))
{
*old_size_ptr = size;
if (current_page.last_ptr == old_pointer)
{
current_page.used = (usize)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory);
}
return old_pointer;
}
if REUSE: (current_page.last_ptr == old_pointer && ptr_is_aligned(old_pointer, alignment))
{
assert(size > old_size);
usize add_size = size - old_size;
if (add_size + current_page.used > current_page.total) break REUSE;
*old_size_ptr = size;
current_page.used += add_size;
return old_pointer;
}
void* new_mem = this.alloc(size, alignment)?;
copy(new_mem, old_pointer, old_size);
return new_mem;
}
private fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this)
{
DynamicArenaPage* page = this.page;
DynamicArenaPage** unused_page_ptr = &this.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
page.used = 0;
DynamicArenaPage* prev_unused = *unused_page_ptr;
*unused_page_ptr = page;
page.prev_arena = prev_unused;
page = next_page;
}
this.page = page;
}
/**
* @require @math::is_power_of_2(alignment)
* @require size > 0
*/
private fn void*! DynamicArenaAllocator.alloc_new(DynamicArenaAllocator* this, usize size, usize alignment)
{
usize page_size = @max(this.page_size, size + DEFAULT_SIZE_PREFIX + alignment);
void* mem = this.backing_allocator.alloc(page_size)?;
DynamicArenaPage*! page = this.backing_allocator.alloc(DynamicArenaPage.sizeof);
if (catch err = page)
{
this.backing_allocator.free(mem);
return err!;
}
page.memory = mem;
usize offset = aligned_offset((usize)mem + DEFAULT_SIZE_PREFIX, alignment) - (usize)mem;
usize* size_ptr = mem + offset - DEFAULT_SIZE_PREFIX;
*size_ptr = size;
page.prev_arena = this.page;
page.total = page_size;
page.used = size + offset;
this.page = page;
return page.last_ptr = page.memory + offset;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
* @require size > 0
* @require this
*/
private fn void*! DynamicArenaAllocator.alloc(DynamicArenaAllocator* this, usize size, usize alignment)
{
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = this.page;
if (!page && this.unused_page)
{
this.page = page = this.unused_page;
this.unused_page = page.prev_arena;
page.prev_arena = null;
}
if (!page) return this.alloc_new(size, alignment);
usize start = aligned_offset((uptr)page.memory + page.used + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory;
usize new_used = start + size;
if ALLOCATE_NEW: (new_used > page.total)
{
if ((page = this.unused_page))
{
start = aligned_offset((uptr)page.memory + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory;
new_used = start + size;
if (page.total >= new_used)
{
this.unused_page = page.prev_arena;
page.prev_arena = this.page;
this.page = page;
break ALLOCATE_NEW;
}
}
return this.alloc_new(size, alignment);
}
page.used = new_used;
void* mem = page.memory + start;
usize* size_offset = mem - DEFAULT_SIZE_PREFIX;
*size_offset = size;
return mem;
}

View File

@@ -0,0 +1,64 @@
module std::mem;
private fn void*! null_allocator_fn(void *data, usize bytes, usize alignment, void* old_pointer, AllocationKind kind)
{
switch (kind)
{
case ALLOC:
case CALLOC:
case REALLOC:
return AllocationFailure.OUT_OF_MEMORY!;
default:
return null;
}
}
fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* old_pointer, AllocationKind kind) @inline
{
if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT;
assert(@math::is_power_of_2(alignment), "Alignment was not a power of 2");
void* data;
switch (kind)
{
case ALLOC:
if (alignment > DEFAULT_MEM_ALIGNMENT)
{
data = (void*)aligned_offset((iptr)libc::malloc(bytes + alignment), alignment);
}
else
{
data = libc::malloc(bytes);
}
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
return data;
case CALLOC:
if (alignment > DEFAULT_MEM_ALIGNMENT)
{
data = (void*)aligned_offset((iptr)libc::calloc(bytes + alignment, 1), alignment);
}
else
{
data = libc::malloc(bytes);
}
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
return data;
case REALLOC:
if (alignment > DEFAULT_MEM_ALIGNMENT)
{
data = (void*)aligned_offset((iptr)libc::realloc(old_pointer, bytes + alignment), alignment);
}
else
{
data = libc::realloc(old_pointer, bytes);
}
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
return data;
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION!;
case FREE:
libc::free(old_pointer);
return null;
}
@unreachable();
}

View File

@@ -0,0 +1,106 @@
module std::mem;
const TEMP_BLOCK_SIZE = 1024;
const TEMP_PAGES = 64;
private char[TEMP_BLOCK_SIZE * TEMP_PAGES] allocator_static_storage;
private void*[TEMP_PAGES] allocator_static_page_storage;
SlotAllocator temp_allocator = {
.pages = &allocator_static_storage,
.page_size = TEMP_BLOCK_SIZE,
.page_count = TEMP_PAGES,
.bitmask = TEMP_PAGES - 1,
.current_page = 0,
};
struct SlotAllocator
{
void* pages;
usize page_size;
usize page_count;
usize bitmask;
usize current_page;
}
fn void*! SlotAllocator.alloc(SlotAllocator *allocator, usize size)
{
void* active_page = (char*)(allocator.pages) + allocator.current_page * allocator.page_size;
void** page_pointer = (void**)(active_page);
if (*page_pointer)
{
// TODO fix
main_allocator.free(*page_pointer)?;
*page_pointer = null;
}
if (size > allocator.page_size - $sizeof(page_pointer))
{
void* mem = main_allocator.alloc(size)?;
*page_pointer = mem;
allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask);
return mem;
}
allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask);
return &page_pointer[1];
}
struct RingAllocator
{
char *data;
usize size;
usize offset;
}
fn void* RingAllocator.alloc(RingAllocator *allocator, usize size)
{
if (size > allocator.size) return null;
// Wraparound? If so, start at the beginning.
if (allocator.offset + size > allocator.size)
{
allocator.offset = size;
return allocator.data;
}
void* data = allocator.offset + allocator.data;
allocator.offset = (allocator.offset + size) & allocator.size;
return data;
}
fn void* RingAllocator.realloc(RingAllocator *allocator, void* ptr, usize size)
{
if (size > allocator.size) return null;
assert(allocator.data >= ptr && ptr < allocator.data + size, "Realloc on other allocator.");
// 1. The pointer is before the allocator
if (allocator.data + allocator.offset > ptr)
{
if (allocator.data + allocator.size < ptr + size)
{
// 1a. There is not enough space, we need to copy to the start.
usize pointer_offset = ptr - allocator.data;
usize copy_len = pointer_offset + size > allocator.offset ? allocator.offset - pointer_offset : size;
//memcpy(allocator.data, ptr, copy_len);
allocator.offset = size;
return allocator.data;
}
// 1b. There is enough space, so we just change the offset:
allocator.offset = ptr - allocator.data + size;
return ptr;
}
// 2. The pointer is after the allocator
// 2a. Is there sufficient space?
if (ptr + size <= allocator.data + allocator.size)
{
// Good, if so we simply change the offset and return the pointer.
allocator.offset = ptr - allocator.data + size;
return ptr;
}
// 2b. Not sufficient space, we copy to the beginning.
usize pointer_offset = ptr - allocator.data;
usize copy_len = allocator.size - (ptr - allocator.data);
if (copy_len > size) copy_len = size;
//memcpy(allocator.data, ptr, copy_len);
allocator.offset = size;
return allocator.data;
}

18
lib/std/os/linux.c3 Normal file
View File

@@ -0,0 +1,18 @@
module std::os::linux;
import std::env;
$if (env::OS_TYPE == OsType.LINUX):
extern fn int* __errno_location();
fn int errno() @inline
{
return *__errno_location();
}
fn void errno_set(int err)
{
*(__errno_location()) = err;
}
$endif;

16
lib/std/os/macos.c3 Normal file
View File

@@ -0,0 +1,16 @@
module std::os::macos;
import std::env;
$if (env::OS_TYPE == OsType.MACOSX):
extern fn int* __error();
fn int errno() @inline
{
return *__error();
}
fn void errno_set(int err)
{
*(__error()) = err;
}
$endif;

View File

@@ -1,21 +0,0 @@
module std::os::macos::cf;
$if (env::OS_TYPE == OsType.MACOSX):
define CFAllocatorRef = distinct void*;
define CFAllocatorContextRef = distinct void*;
define CFOptionFlags = usz;
macro CFAllocatorRef default_allocator() = _macos_CFAllocatorGetDefault();
macro void CFAllocatorRef.dealloc(CFAllocatorRef allocator, void* ptr) = _macos_CFAllocatorDeallocate(allocator, ptr);
macro void* CFAllocatorRef.alloc(CFAllocatorRef allocator, usz size) = _macos_CFAllocatorAllocate(allocator, size, 0);
macro usz CFAllocatorRef.get_preferred_size(CFAllocatorRef allocator, usz req_size) = _macos_CFAllocatorGetPreferredSizeForSize(allocator, req_size, 0);
macro void CFAllocatorRef.set_default(CFAllocatorRef allocator) = _macos_CFAllocatorSetDefault(allocator);
extern fn CFAllocatorRef _macos_CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContextRef context) @extname("CFAllocatorCreate");
extern fn void _macos_CFAllocatorDeallocate(CFAllocatorRef allocator, void* ptr) @extname("CFAllocatorDeallocate");
extern fn CFAllocatorRef _macos_CFAllocatorGetDefault() @extname("CFAllocatorGetDefault");
extern fn void _macos_CFAllocatorSetDefault(CFAllocatorRef allocator) @extname("CFAllocatorSetDefault");
extern fn void* _macos_CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extname("CFAllocatorAllocate");
extern fn CFIndex _macos_CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extname("CFAllocatorGetPreferredSizeForSize");
$endif;

View File

@@ -1,14 +0,0 @@
module std::os::macos::cf;
$if (env::OS_TYPE == OsType.MACOSX):
define CFArrayRef = distinct void*;
define CFArrayCallBacksRef = distinct void*;
define CFMutableArrayRef = distinct void*;
extern fn CFArrayRef _macos_CFArrayCreate(CFAllocatorRef allocator, void** values, CFIndex num_values, CFArrayCallBacksRef callBacks) @extname("CFArrayCreate");
extern fn CFArrayRef _macos_CFArrayCopy(CFAllocatorRef allocator, CFArrayRef array) @extname("CFArrayCopy");
extern fn CFIndex _macos_CFArrayGetCount(CFArrayRef array) @extname("CFArrayGetCount");
extern fn void _macos_CFArrayAppendArray(CFMutableArrayRef theArray, CFArrayRef otherArray, CFRange otherRange) @extname("CFArrayAppendArray");
extern fn CFMutableArrayRef _macos_CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, CFArrayCallBacksRef callBacks) @extname("CFArrayCreateMutable");
extern fn void _macos_CFArrayAppendValue(CFMutableArrayRef theArray, void *value) @extname("CFArrayAppendValue");
$endif;

View File

@@ -1,16 +0,0 @@
module std::os::macos::cf;
$if (env::OS_TYPE == OsType.MACOSX):
define CFTypeRef = distinct void*;
define CFIndex = isz;
struct CFRange
{
CFIndex location;
CFIndex length;
}
extern fn CFTypeRef _macos_CFRetain(CFTypeRef cf) @extname("CFRetain");
extern fn void _macos_CFRelease(CFTypeRef cf) @extname("CFRelease");
$endif;

View File

@@ -1,49 +0,0 @@
module std::os::macos::objc;
$if (env::OS_TYPE == OsType.MACOSX):
define Class = distinct void*;
define Method = distinct void*;
define Ivar = distinct void*;
define Selector = distinct void*;
fault ObjcFailure
{
CLASS_NOT_FOUND
}
macro char* Class.name(Class cls) = _macos_class_getName(cls);
macro Class Class.superclass(Class cls) = _macos_class_getSuperclass(cls);
macro bool Class.responds_to(Class cls, Selector sel) = _macos_class_respondsToSelector(cls, sel);
macro Method Class.method(Class cls, Selector name) = _macos_class_getClassMethod(cls, name);
macro bool Selector.equals(Selector a, Selector b) = a == b;
macro bool Class.equals(Class a, Class b) = a == b;
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!;
return cls;
}
macro Class[] class_get_list(Allocator *allocator = mem::current_allocator())
{
int num_classes = _macos_objc_getClassList(null, 0);
if (!num_classes) return {};
Class[] entries = array::make(Class, num_classes, allocator);
_macos_objc_getClassList(entries.ptr, entries.len);
return entries;
}
extern fn Class _macos_objc_getClass(char* name) @extname("objc_getClass");
extern fn int _macos_objc_getClassList(Class* buffer, int buffer_count) @extname("objc_getClassList");
extern fn char* _macos_class_getName(Class cls) @extname("class_getName");
extern fn Class _macos_class_getSuperclass(Class cls) @extname("class_getSuperclass");
extern fn Method _macos_class_getClassMethod(Class cls, Selector name) @extname("class_getClassMethod");
extern fn bool _macos_class_respondsToSelector(Class cls, Selector name) @extname("class_respondsToSelector");
extern fn Selector _macos_sel_registerName(char* str) @extname("sel_registerName");
extern fn Class _macos_objc_lookUpClass(char* name) @extname("objc_lookUpClass");
$endif;

View File

@@ -1,12 +0,0 @@
module std::os::win32::files;
$if (env::OS_TYPE == OsType.WIN32):
/*
private extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extname("GetCurrentDirectoryW");
private extern bool _win32_CreateSymbolicLinkW(Char16* symlink_file, Char16* target_file, ulong flags) @extname("CreateSymbolicLinkW");
private extern bool _win32_CreateDirectoryW(Char16* path_name, void* security_attributes) @extname("CreateDirectoryW");
private extern bool _win32_DeleteFileW(Char16* file) @extname("DeleteFileW");
private extern bool _win32_CopyFileW(Char16* from_file, Char16* to_file, bool no_overwrite) @extname("CopyFileW");
private extern ulong _win32_GetFullPathNameW(Char16* file_name, ulong buffer_len, Char16* buffer, Char16** file_part) @extname("GetFullPathNameW");
*/
$endif;

11
lib/std/os/windows.c3 Normal file
View File

@@ -0,0 +1,11 @@
module std::os::windows;
import std::env;
$if (env::OS_TYPE == OsType.WIN32):
extern fn int getLastError() @stdcall @extname("GetLastError");
fn int errno() @inline
{
return getLastError();
}
$endif;

View File

@@ -1,113 +0,0 @@
// priorityqueue.c3
// A priority queue using a classic binary heap for C3.
//
// Copyright (c) 2022 David Kopec
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
module std::priorityqueue<Type>;
import std::array::list;
define Heap = List<Type>;
struct PriorityQueue
{
Heap heap;
bool max; // true if max-heap, false if min-heap
}
fn void PriorityQueue.push(PriorityQueue* pq, Type element)
{
pq.heap.push(element);
usz i = pq.heap.len() - 1;
while (i > 0)
{
usz parent = (i - 1) / 2;
if ((pq.max && greater(pq.heap.get(i), pq.heap.get(parent))) || (!pq.max && less(pq.heap.get(i), pq.heap.get(parent))))
{
pq.heap.swap(i, parent);
i = parent;
continue;
}
break;
}
}
/**
* @require pq != null
*/
fn Type! PriorityQueue.pop(PriorityQueue* pq)
{
usz i = 0;
usz len = pq.heap.len() @inline;
if (!len) return IteratorResult.NO_MORE_ELEMENT!;
usz newCount = len - 1;
pq.heap.swap(0, newCount);
while ((2 * i + 1) < newCount)
{
usz j = 2 * i + 1;
if (((j + 1) < newCount) &&
((pq.max && greater(pq.heap.get(j + 1), pq.heap[j]))
|| (!pq.max && less(pq.heap.get(j + 1), pq.heap.get(j)))))
{
j++;
}
if ((pq.max && less(pq.heap.get(i), pq.heap.get(j))) || (!pq.max && greater(pq.heap.get(i), pq.heap.get(j))))
{
pq.heap.swap(i, j);
i = j;
continue;
}
break;
}
return pq.heap.pop();
}
/**
* @require pq != null
*/
fn Type! PriorityQueue.peek(PriorityQueue* pq)
{
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT!;
return pq.heap.get(0);
}
/**
* @require pq != null
*/
fn void PriorityQueue.free(PriorityQueue* pq)
{
pq.heap.free();
}
/**
* @require pq != null
*/
fn usz PriorityQueue.len(PriorityQueue* pq) @operator(len)
{
return pq.heap.len();
}
/**
* @require pq != null, index < pq.len()
*/
fn Type PriorityQueue.peek_at(PriorityQueue* pq, usz index) @operator([])
{
return pq.heap[index];
}

View File

@@ -18,82 +18,13 @@ struct VirtualContainer
struct SubArrayContainer
{
void* ptr;
usz len;
usize len;
}
struct VarArrayHeader
{
usz size;
usz capacity;
usize size;
usize capacity;
void *allocator;
}
define TestFn = fn void!();
struct TestRunner
{
char[][] test_names;
TestFn[] test_fns;
JmpBuf buf;
}
fn TestRunner test_runner_create()
{
return TestRunner {
.test_fns = $$TEST_FNS,
.test_names = $$TEST_NAMES,
};
}
import libc;
private TestRunner* current_runner;
fn void test_panic(char[] message, char[] file, char[] function, uint line)
{
io::println("[error]");
io::printfln("\n Error: %s", message);
io::printfln(" - in %s %s:%s.\n", function, file, line);
libc::longjmp(&current_runner.buf, 1);
}
fn bool TestRunner.run(TestRunner* runner)
{
current_runner = runner;
PanicFn old_panic = builtin::panic;
defer builtin::panic = old_panic;
builtin::panic = &test_panic;
int tests_passed = 0;
int tests = runner.test_names.len;
io::println("----- TESTS -----");
foreach(i, char[] name : runner.test_names)
{
io::printf("Testing %s ... ", name);
if (libc::setjmp(&runner.buf) == 0)
{
if (catch err = runner.test_fns[i]())
{
io::println("[failed]");
continue;
}
io::println("[ok]");
tests_passed++;
}
}
io::printfln("\n%d test(s) run.\n", tests);
io::print("Test Result: ");
if (tests_passed < tests)
{
io::print("FAILED");
}
else
{
io::print("ok");
}
io::printfln(". %d passed, %d failed.", tests_passed, tests - tests_passed);
return tests == tests_passed;
}
fn bool __run_default_test_runner()
{
return test_runner_create().run();
}

59
lib/std/str.c3 Normal file
View File

@@ -0,0 +1,59 @@
module std::str;
import std::mem;
define ZString = char*;
define String = char[];
fn ZString copy_zstring(String s)
{
usize len = s.len;
char* str = mem::alloc(len + 1);
mem::copy(str, s.ptr, len);
str[len] = 0;
return str;
}
fn ZString tcopy_zstring(String s)
{
usize len = s.len;
char* str = mem::talloc(len + 1)!!;
mem::copy(str, s.ptr, len);
str[len] = 0;
return str;
}
fn String copy(String s)
{
usize len = s.len;
ZString str_copy = copy_zstring(s) @inline;
return str_copy[..len];
}
fn String tcopy(String s)
{
usize len = s.len;
ZString str_copy = tcopy_zstring(s) @inline;
return str_copy[..len];
}
fn String tconcat(String s1, String s2)
{
usize full_len = s1.len + s2.len;
char* str = mem::talloc(full_len + 1)!!;
usize 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 str[..full_len];
}
fn String concat(String s1, String s2)
{
usize full_len = s1.len + s2.len;
char* str = mem::alloc(full_len + 1);
usize 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 str[..full_len];
}

View File

@@ -1,243 +0,0 @@
#!/usr/bin/env python3
# Script based on https://gist.github.com/mmozeiko/7f3162ec2988e81e56d5c4e22cde9977
# by Mārtiņš Možeiko.
# Changes and additions to the gist are licensed under the CC0 License.
import platform
import io
import os
import sys
import json
import shutil
import hashlib
import zipfile
import tempfile
import argparse
import subprocess
import urllib.request
import re
from pathlib import Path
OUTPUT = Path("msvc_temp") # output folder
SDK_OUTPUT = Path("msvc_sdk")
MANIFEST_URL = "https://aka.ms/vs/17/release/channel"
def download(url):
with urllib.request.urlopen(url) as res:
return res.read()
def download_progress(url, check, name, f):
data = io.BytesIO()
with urllib.request.urlopen(url) as res:
total = int(res.headers["Content-Length"])
size = 0
while True:
block = res.read(1<<20)
if not block:
break
f.write(block)
data.write(block)
size += len(block)
perc = size * 100 // total
print(f"\r{name} ... {perc}%", end="")
print()
data = data.getvalue()
digest = hashlib.sha256(data).hexdigest()
if check.lower() != digest:
exit(f"Hash mismatch for f{pkg}")
return data
# super crappy msi format parser just to find required .cab files
def get_msi_cabs(msi):
index = 0
while True:
index = msi.find(b".cab", index+4)
if index < 0:
return
yield msi[index-32:index+4].decode("ascii")
def first(items, cond):
return next(item for item in items if cond(item))
### parse command-line arguments
ap = argparse.ArgumentParser()
ap.add_argument("--show-versions", const=True, action="store_const", help="Show available MSVC and Windows SDK versions")
ap.add_argument("--accept-license", const=True, action="store_const", help="Automatically accept license")
ap.add_argument("--msvc-version", help="Get specific MSVC version")
ap.add_argument("--sdk-version", help="Get specific Windows SDK version")
args = ap.parse_args()
### get main manifest
manifest = json.loads(download(MANIFEST_URL))
### download VS manifest
vs = first(manifest["channelItems"], lambda x: x["id"] == "Microsoft.VisualStudio.Manifests.VisualStudio")
payload = vs["payloads"][0]["url"]
vsmanifest = json.loads(download(payload))
### find MSVC & WinSDK versions
packages = {}
for p in vsmanifest["packages"]:
packages.setdefault(p["id"].lower(), []).append(p)
msvc = {}
sdk = {}
for pid,p in packages.items():
if pid.startswith("Microsoft.VisualStudio.Component.VC.".lower()) and pid.endswith(".x86.x64".lower()):
pver = ".".join(pid.split(".")[4:6])
if pver[0].isnumeric():
msvc[pver] = pid
elif pid.startswith("Microsoft.VisualStudio.Component.Windows10SDK.".lower()) or \
pid.startswith("Microsoft.VisualStudio.Component.Windows11SDK.".lower()):
pver = pid.split(".")[-1]
if pver.isnumeric():
sdk[pver] = pid
if args.show_versions:
print("MSVC versions:", " ".join(sorted(msvc.keys())))
print("Windows SDK versions:", " ".join(sorted(sdk.keys())))
exit(0)
msvc_ver = args.msvc_version or max(sorted(msvc.keys()))
sdk_ver = args.sdk_version or max(sorted(sdk.keys()))
if msvc_ver in msvc:
msvc_pid = msvc[msvc_ver]
msvc_ver = ".".join(msvc_pid.split(".")[4:-2])
else:
exit(f"Unknown MSVC version: f{args.msvc_version}")
if sdk_ver in sdk:
sdk_pid = sdk[sdk_ver]
else:
exit(f"Unknown Windows SDK version: f{args.sdk_version}")
print(f"Downloading MSVC v{msvc_ver} and Windows SDK v{sdk_ver}")
### agree to license
tools = first(manifest["channelItems"], lambda x: x["id"] == "Microsoft.VisualStudio.Product.BuildTools")
resource = first(tools["localizedResources"], lambda x: x["language"] == "en-us")
license = resource["license"]
if not args.accept_license:
accept = input(f"Do you accept Visual Studio license at {license}, and also confirm that you have a valid license Visual Studio license allowing you to download the VS Build Tools [Y/N] ?")
if not accept or accept[0].lower() != "y":
exit(0)
shutil.rmtree(OUTPUT, ignore_errors = True)
shutil.rmtree(SDK_OUTPUT, ignore_errors = True)
OUTPUT.mkdir()
total_download = 0
### download Windows SDK
archs = [
#"arm",
#"arm64",
"x64",
#"x86"
]
msvc_packages = [
f"microsoft.vc.{msvc_ver}.asan.headers.base",
]
for arch in archs:
msvc_packages.append(f"microsoft.vc.{msvc_ver}.crt.{arch}.desktop.base")
msvc_packages.append(f"microsoft.vc.{msvc_ver}.crt.{arch}.store.base")
msvc_packages.append(f"microsoft.vc.{msvc_ver}.asan.{arch}.base")
for pkg in msvc_packages:
p = first(packages[pkg], lambda p: p.get("language") in (None, "en-US"))
for payload in p["payloads"]:
with tempfile.TemporaryFile() as f:
data = download_progress(payload["url"], payload["sha256"], pkg, f)
total_download += len(data)
with zipfile.ZipFile(f) as z:
for name in z.namelist():
if name.startswith("Contents/"):
out = OUTPUT / Path(name).relative_to("Contents")
out.parent.mkdir(parents=True, exist_ok=True)
out.write_bytes(z.read(name))
sdk_packages = [
# Windows SDK libs
f"Windows SDK for Windows Store Apps Libs-x86_en-us.msi",
f"Windows SDK Desktop Libs x64-x86_en-us.msi",
# CRT headers & libs
f"Universal CRT Headers Libraries and Sources-x86_en-us.msi",
]
with tempfile.TemporaryDirectory() as d:
dst = Path(d)
sdk_pkg = packages[sdk_pid][0]
sdk_pkg = packages[first(sdk_pkg["dependencies"], lambda x: True).lower()][0]
msi = []
cabs = []
# download msi files
for pkg in sdk_packages:
payload = first(sdk_pkg["payloads"], lambda p: p["fileName"] == f"Installers\\{pkg}")
msi.append(dst / pkg)
with open(dst / pkg, "wb") as f:
data = download_progress(payload["url"], payload["sha256"], pkg, f)
total_download += len(data)
cabs += list(get_msi_cabs(data))
# download .cab files
for pkg in cabs:
payload = first(sdk_pkg["payloads"], lambda p: p["fileName"] == f"Installers\\{pkg}")
with open(dst / pkg, "wb") as f:
download_progress(payload["url"], payload["sha256"], pkg, f)
print("Unpacking msi files...")
# run msi installers
for m in msi:
if (platform.system() == "Windows"):
subprocess.check_call(["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"])
else:
subprocess.check_call(["msiextract", m, '-C', OUTPUT.resolve()])
### versions
if (platform.system() == "Windows"):
window_kit = OUTPUT / "Windows Kits/"
else:
window_kit = OUTPUT / "Program Files/Windows Kits/"
ucrt = list(window_kit.glob("*/Lib/*/ucrt"))[0]
um = list(window_kit.glob("*/Lib/*/um"))[0]
lib = list((OUTPUT / "VC/Tools/MSVC/").glob("*/lib"))[0]
SDK_OUTPUT.mkdir(exist_ok=True)
for arch in archs:
out_dir = SDK_OUTPUT / arch
shutil.copytree(ucrt / arch, out_dir, dirs_exist_ok=True)
shutil.copytree(um / arch, out_dir, dirs_exist_ok=True)
shutil.copytree(lib / arch, out_dir, dirs_exist_ok=True)
### cleanup
shutil.rmtree(OUTPUT, ignore_errors=True)

View File

@@ -1,42 +0,0 @@
Release Notes
0.4.0 Change List
- Compatibility with LLVM 16.
- Dropped LLVM 12 support.
- Updated memory allocators. Added @scoped and @pool macros.
- Various bug fixes.
- Constant pointers may be compile time evaluated.
- Added many new builtins.
- Emit asm using --emit-asm.
- Added --nostdlib and --nolibc.
- Fixes to adding libraries at link time.
- Various improved error messages.
- Windows debug info fixes.
- Add of `foreach_r`.
- Script downloading the MSVC SDK to cross compile to windows.
- Many standard library additions.
- Extension methods may be added for built-in types.
- Macros may take vector and array arguments generic over length.
- Macro varargs with $vaarg, $vacount etc.
- Many vector builtins added as dot methods.
- in / out / inout doc parameters checked.
- Initial inline asm support for aarch64 and x64.
- Single line short function declaration.
- Added `$checks` builtin.
- Optional single module compilation.
- Static initialization / finalization to have code running at start/end.
- C3 custom printf function in the stdlib.
- `[]=` overload now works correctly.
- More compile time reflection added and general cleanup done.
- usize/isize/iptrdiff/uptrdiff replaced by usz/isz.
- Add `var` to allow type inference on regular variables.
- LLVM codegen optimizations.
- `??` now allows chaining another optional.
- int128 support on all platforms.
- `import` is now allowed anywhere at the top level.
- `project.c3p` renamed `project.json`
- Update to project properties, e.g. "libs" -> "dependencies" etc.
- $$TIME, $$DATE and $$FUNCTION builtin defines added.
- Improvements to untyped lists.
- Various builtins added: $$prefetch, $$reverse, $$shufflevector etc.

View File

@@ -61,7 +61,7 @@ fn void encode(char[] in, char *out)
}
// move back
usz last = in.len - 1;
usize last = in.len - 1;
// check the last and add padding
switch (last % 3)
{

View File

@@ -1,25 +0,0 @@
module binarydigits;
import std::math;
import std::io;
fn void main()
{
for (int i = 0; i < 20; i++)
{
String s = bin(i);
defer s.destroy();
io::printf("%s\n", s);
}
}
fn String bin(int x)
{
int bits = 1 + (int)(x == 0 ? 0 : math::log10((double)(x)) / math::log10(2));
String str;
str.append_repeat('0', bits);
for (int i = 0; i < bits; i++)
{
str.set((usz)(bits - i - 1), x & 1 ? '1' : '0');
x >>= 1;
}
return str;
}

View File

@@ -1,86 +0,0 @@
// #target: macos-x64
module brainfk;
import std::io;
const BF_MEM = 30000;
char[BF_MEM] memory;
fault InterpretError
{
INTEPRET_FAILED
}
fn void! print_error(usz pos, char[] err)
{
io::printfln("Error at %s: %s", pos, err);
return InterpretError.INTEPRET_FAILED!;
}
fn void! brainf(char[] program)
{
usz sp = 0;
usz mem = 0;
while (sp < program.len)
{
char c = program[sp++];
switch (c)
{
case '<':
if (!mem) return print_error(sp, "Memory underflow");
mem--;
case '>':
if (mem == BF_MEM - 1) return print_error(sp, "Memory overflow");
mem++;
case '+':
memory[mem]++;
case '-':
memory[mem]--;
case '.':
io::putchar(memory[mem]);
io::stdout().flush();
case ',':
memory[mem] = io::stdin().getc()!!;
case '[':
usz indent = 1;
if (memory[mem]) continue;
usz start = sp - 1;
while (indent)
{
if (sp == program.len) return print_error(start, "No matching ']'");
switch (program[sp++])
{
case ']': indent--;
case '[': indent++;
default: break;
}
}
case ']':
if (!memory[mem]) continue;
usz start = sp--;
usz indent = 1;
while (indent)
{
if (!sp) return print_error(start, "No matching '['");
switch (program[--sp])
{
case ']': indent++;
case '[': indent--;
default: break;
}
}
sp++;
default:
break;
}
}
}
fn void! main()
{
char[] program = `
++++[>+++++<-]>[<+++++>-]+<+[
>[>+>+<<-]++>>[<<+>>-]>>>[-]++>[-]+
>>>+[[-]++++++>>>]<<<[[<++++++++<++>>-]+<.<[>----<-]<]
<<[>>>>>[>>>[-]+++++++++<[>-<-]+++++++++>[-[<->-]+[<<<]]<[>+<-]>]<<-]<<-
]`;
brainf(program)?;
}

View File

@@ -1,141 +0,0 @@
module test;
import libc;
import std::io;
struct Doc { Head *head; }
struct Head { String* title; }
struct Summary
{
String* title;
bool ok;
}
private struct StringData
{
Allocator allocator;
usz len;
usz capacity;
char[*] chars;
}
fn void Summary.print(Summary *s, File out)
{
char[] title = s.title ? s.title.str() : "missing";
out.printf("Summary({ .title = %s, .ok = %s})", title, s.ok);
}
fn bool contains(char[] haystack, char[] needle)
{
usz len = haystack.len;
usz needle_len = needle.len;
if (len < needle_len) return false;
if (!needle_len) return true;
len -= needle_len - 1;
for (usz i = 0; i < len; i++)
{
if (mem::equals(haystack[i..], needle))
{
return true;
}
}
return false;
}
macro @dupe(value)
{
$typeof(&value) temp = malloc_checked($sizeof(value))?;
*temp = value;
return temp;
}
fault ReadError
{
BAD_READ,
}
fn Doc! readDoc(char[] url)
{
if (contains(url, "fail")) return ReadError.BAD_READ!;
if (contains(url, "head-missing")) return { .head = null };
if (contains(url, "title-missing")) return { @dupe(Head { .title = null }) };
if (contains(url, "title-empty")) return { @dupe(Head { .title = @dupe((String)null) }) };
String str;
str.printf("Title of %s", url);
return { @dupe(Head { .title = @dupe(str) }) };
}
fn Summary buildSummary(Doc doc)
{
return Summary {
.title = doc.head ? doc.head.title : null,
.ok = true,
};
}
fn Summary readAndBuildSummary(char[] url)
{
return buildSummary(readDoc(url)) ?? Summary { .title = null, .ok = false };
/*
// or
Summary summary = buildSummary(readDoc(url));
if (catch summary) return Summary { .title = null, .ok = false };
return summary;
// or
Summary summary = buildSummary(readDoc(url));
if (try summary) return summary;
return Summary { .title = null, .ok = false };
*/
}
fault TitleResult
{
TITLE_MISSING
}
fn bool! isTitleNonEmpty(Doc doc)
{
if (!doc.head) return TitleResult.TITLE_MISSING!;
String* head = doc.head.title;
if (!head) return TitleResult.TITLE_MISSING!;
return head.len() > 0;
}
fn bool! readWhetherTitleNonEmpty(char[] url)
{
return isTitleNonEmpty(readDoc(url));
}
fn char[] bool_to_string(bool b)
{
return b ? "true" : "false";
}
fn void main()
{
const char[][] URLS = { "good", "title-empty", "title-missing", "head-missing", "fail" };
DynamicArenaAllocator dynamic_arena;
dynamic_arena.init(1024);
foreach (char[] url : URLS)
{
mem::@scoped(&dynamic_arena)
{
io::printf(`Checking "https://%s/":` "\n", url);
Summary summary = readAndBuildSummary(url);
io::printf(" Summary: ");
summary.print(io::stdout());
io::println("");
char[] title_sure = summary.title ? summary.title.str() : "";
io::printf(" Title: %s\n", title_sure);
bool! has_title = readWhetherTitleNonEmpty(url);
// This looks a bit less than elegant, but as you see it's mostly due to having to
// use printf here.
io::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? catch(has_title).nameof, has_title ?? false);
};
dynamic_arena.reset();
}
dynamic_arena.destroy();
}

View File

@@ -1,32 +0,0 @@
module foo;
import std::io;
tlocal char[] context_user = "safe";
macro long reallyPerform(task)
{
io::printfln("%s: %s", context_user, task);
return task.len;
}
macro long perform(task)
{
return reallyPerform(task);
}
macro @with_mode(char[] user, #action, ...)
{
@scope(context_user)
{
context_user = user;
return #action($vasplat());
};
}
fn void main()
{
long val1 = perform("something");
long val2 = @with_mode("faster", perform, "reliable");
long val3 = perform(char[][] {"something"});
io::printfln("%d %d %d", val1, val2, val3);
}

View File

@@ -1,27 +0,0 @@
module test;
import std::io;
fn void main()
{
/*
Here's a comment.
/*
And a nested comment.
*/
*/
char[] text = `
function hello() {
console.log("name`"\t"`age");
}
hello();
`;
io::println(text);
// Binary
const DATA = x"4749463839610100010080"
x"0100ffffff00000021f904"
x"010a0001002c0000000001"
x"0001000002024c01003b";
io::printf("%d\n", DATA.len);
}

View File

@@ -3,7 +3,7 @@ macro int factorial($n)
$if ($n == 0):
return 1;
$else:
return $n * factorial($n - 1);
return $n * @factorial($n - 1);
$endif;
}
@@ -11,6 +11,6 @@ extern fn void printf(char *fmt, ...);
fn void main()
{
int x = factorial(12);
int x = @factorial(12);
printf("12! = %d\n", x);
}

View File

@@ -1,14 +1,18 @@
module fannkuch;
import std::array;
import std::io;
import std::math;
import libc;
import std::mem;
macro int max(int a, int b)
{
return a > b ? a : b;
}
fn int fannkuchredux(int n)
{
int* perm = array::alloc(int, n);
int* perm1 = array::alloc(int, n);
int* count = array::alloc(int, n);
int* perm = @array::make(int, n);
int* perm1 = @array::make(int, n);
int* count = @array::make(int, n);
int max_flips_count;
int perm_count;
int checksum;
@@ -38,7 +42,7 @@ fn int fannkuchredux(int n)
flips_count++;
}
max_flips_count = max(max_flips_count, flips_count);
max_flips_count = @max(max_flips_count, flips_count);
checksum += perm_count % 2 == 0 ? flips_count : -flips_count;
/* Use incremental change to generate another permutation */
@@ -46,7 +50,7 @@ fn int fannkuchredux(int n)
{
if (r == n)
{
io::printf("%d\n", checksum);
libc::printf("%d\n", checksum);
return max_flips_count;
}
@@ -71,6 +75,6 @@ fn int fannkuchredux(int n)
fn int main(int argc, char** argv)
{
int n = argc > 1 ? libc::atoi(argv[1]) : 7;
io::printf("Pfannkuchen(%d) = %d\n", n, fannkuchredux(n));
libc::printf("Pfannkuchen(%d) = %d\n", n, fannkuchredux(n));
return 0;
}

View File

@@ -1,5 +1,4 @@
module fasta;
import std::io;
import libc;
const IM = 139968;
@@ -56,14 +55,14 @@ const LINELEN = 60;
// slowest character-at-a-time output
fn void repeat_fasta(char[] seq, int n)
{
usz len = seq.len;
usize len = seq.len;
int i = void;
for (i = 0; i < n; i++)
{
io::putchar(seq[i % len]);
if (i % LINELEN == LINELEN - 1) io::putchar('\n');
libc::putchar(seq[i % len]);
if (i % LINELEN == LINELEN - 1) libc::putchar('\n');
}
if (i % LINELEN != 0) io::putchar('\n');
if (i % LINELEN != 0) libc::putchar('\n');
}
fn void random_fasta(char[] symb, double[] probability, int n)
@@ -81,10 +80,10 @@ fn void random_fasta(char[] symb, double[] probability, int n)
v -= probability[j];
if (v < 0) break;
}
io::putchar(symb[j]);
if (i % LINELEN == LINELEN - 1) io::putchar('\n');
libc::putchar(symb[j]);
if (i % LINELEN == LINELEN - 1) libc::putchar('\n');
}
if (i % LINELEN != 0) io::putchar('\n');
if (i % LINELEN != 0) libc::putchar('\n');
}
fn void main(int argc, char **argv)
@@ -92,13 +91,13 @@ fn void main(int argc, char **argv)
int n = 1000;
if (argc > 1) n = libc::atoi(argv[1]);
io::printf(">ONE Homo sapiens alu\n");
libc::printf(">ONE Homo sapiens alu\n");
repeat_fasta(alu, n * 2);
io::printf(">TWO IUB ambiguity codes\n");
libc::printf(">TWO IUB ambiguity codes\n");
random_fasta(iub, iub_p, n * 3);
io::printf(">THREE Homo sapiens frequency\n");
libc::printf(">THREE Homo sapiens frequency\n");
random_fasta(homosapiens, homosapiens_p, n * 5);
}

View File

@@ -1,7 +1,11 @@
module game_of_life;
import std::io;
import libc;
extern fn void printf(char *fmt, ...);
extern fn int atoi(char *val);
extern void *__stdoutp;
extern fn void fflush(void *std);
extern fn int rand();
extern fn void* malloc(usize size);
extern fn void usleep(int time);
@@ -16,18 +20,18 @@ struct GameBoard
fn void GameBoard.show(GameBoard *board)
{
io::printf("\e[H");
printf("\e[H");
char* current = board.world;
for (int y = 0; y < board.h; y++)
{
for (int x = 0; x < board.w; x++)
{
io::printf(*current ? "\e[07m \e[m" : " ");
printf(*current ? "\e[07m \e[m" : " ");
current++;
}
io::printf("\e[E");
printf("\e[E");
}
libc::fflush(libc::stdout());
fflush(__stdoutp);
}
fn void GameBoard.evolve(GameBoard *board)
@@ -61,8 +65,8 @@ fn int main(int c, char** v)
{
int w = 0;
int h = 0;
if (c > 1) w = libc::atoi(v[1]);
if (c > 2) h = libc::atoi(v[2]);
if (c > 1) w = atoi(v[1]);
if (c > 2) h = atoi(v[2]);
if (w <= 0) w = 30;
if (h <= 0) h = 30;
@@ -74,7 +78,7 @@ fn int main(int c, char** v)
for (int i = h * w - 1; i >= 0; i--)
{
board.world[i] = libc::rand() % 10 == 0 ? 1 : 0;
board.world[i] = rand() % 10 == 0 ? 1 : 0;
}
for (int j = 0; j < 1000; j++)
{

View File

@@ -1,8 +1,10 @@
module guess_number;
import std::mem;
import std::io;
import libc;
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
extern fn void printf(char*, ...);
extern fn isize getline(char** linep, usize* linecapp, CFile stream);
struct Game
{
@@ -22,7 +24,7 @@ int err_count = 0;
fn int! askGuess(int high)
{
libc::printf("Guess a number between 1 and %d: ", high);
printf("Guess a number between 1 and %d: ", high);
char[] text = readLine()?;
char* end = null;
int value = (int)libc::strtol(text.ptr, &end, 10);
@@ -32,8 +34,8 @@ fn int! askGuess(int high)
fn char[]! readLine()
{
char* chars = tmalloc(1024)?;
isz loaded = getline(&chars, &&(usz)1023, libc::stdin());
char* chars = mem::talloc(1024)?;
isize loaded = getline(&chars, &&(usize)1023, @libc::stdin());
if (loaded < 0) return InputResult.FAILED_TO_READ!;
chars[loaded] = 0;
return chars[0..(loaded - 1)];
@@ -46,13 +48,13 @@ fn int! askGuessMulti(int high)
int! result = askGuess(high);
if (catch(result) == InputResult.NOT_AN_INT)
{
libc::printf("I didn't understand that.\n");
printf("I didn't understand that.\n");
err_count++;
continue;
}
return result;
}
unreachable();
@unreachable();
}
fn void! Game.play(Game *game)
@@ -72,7 +74,7 @@ fn void Game.report(Game *game, int guess)
if (guess > game.answer) return "too high";
return "the answer";
|};
libc::printf("%d is %.*s.\n", guess, (int)desc.len, desc.ptr);
printf("%d is %.*s.\n", guess, (int)desc.len, desc.ptr);
}
fn void Game.update(Game *game, int guess)
@@ -88,6 +90,6 @@ fn void! main()
int answer = libc::rand() % high + 1;
Game game = { .answer = answer, .high = high };
game.play();
libc::printf("Finished in %d guesses.\n", game.guesses);
libc::printf("Total input errors: %d.\n", err_count);
printf("Finished in %d guesses.\n", game.guesses);
printf("Total input errors: %d.\n", err_count);
}

View File

@@ -1,4 +1,4 @@
module std::hash;
module hash;
import libc;
// Code adapted from Odin's hash.odin

View File

@@ -1,19 +0,0 @@
import std::io;
fn void main()
{
char[][] greetings = {
"Hello, world!",
"¡Hola Mundo!",
"Γειά σου Κόσμε!",
"Привет, мир!",
"こんにちは世界!",
"你好世界!",
"नमस्ते दुनिया!",
"👋🌎!"
};
foreach (greeting : greetings)
{
io::println(greeting);
}
}

View File

@@ -22,5 +22,5 @@ fn int levenshtein(char[] s, char[] t)
int b = levenshtein(s, t[0..^2]);
int c = levenshtein(s[0..^2], t);
return min(min(a, b), c) + 1;
return @min(@min(a, b), c) + 1;
}

View File

@@ -1,97 +0,0 @@
module std::container::map <Key, Type>;
import std::core::builtin;
import std::io;
fault MapResult
{
KEY_NOT_FOUND
}
struct Entry
{
Key key;
Type value;
usz hash;
Entry* next;
bool used;
}
struct Map
{
usz size;
Entry* map;
uint mod;
}
/**
* @require map != null
**/
fn void Map.init(Map *map, uint capacity = 128)
{
if (capacity < 16) capacity = 4;
capacity = math::next_power_of_2(capacity);
map.map = calloc(Entry.sizeof * capacity);
map.mod = capacity - 1;
}
fn Type! Map.valueForKey(Map *map, Key key)
{
if (!map.map) return MapResult.KEY_NOT_FOUND!;
uint hash = key.hash();
usz pos = hash & map.mod;
Entry* entry = &map.map[pos];
if (!entry) return MapResult.KEY_NOT_FOUND!;
while (entry)
{
if (entry.hash == hash && entry.key == key) return entry.value;
entry = entry.next;
}
return MapResult.KEY_NOT_FOUND!;
}
fn Type! Map.set(Map *map, Key key, Type value) @maydiscard
{
if (!map.map)
{
map.map = calloc(Entry.sizeof * 16);
map.mod = 0x0F;
}
uint hash = key.hash();
uint pos = hash & map.mod;
Entry *entry = &map.map[pos];
while (1)
{
if (!entry.used)
{
entry.used = true;
entry.value = value;
entry.hash = hash;
entry.key = key;
return MapResult.KEY_NOT_FOUND!;
}
if (entry.hash == hash && entry.key == key)
{
Type old = entry.value;
entry.value = value;
return old;
}
if (entry.next)
{
entry = entry.next;
continue;
}
Entry* new = mem::calloc(Entry.sizeof);
new.hash = hash;
new.key = key;
new.value = value;
new.next = null;
new.used = true;
entry.next = new;
return MapResult.KEY_NOT_FOUND!;
}
}
fn usz Map.size(Map* map)
{
return map.size;
}

View File

@@ -15,10 +15,10 @@ struct Planet
fn void advance(Planet[] bodies) @noinline
{
usz nbodies = bodies.len;
usize nbodies = bodies.len;
foreach (i, Planet* &b : bodies)
{
for (usz j = i + 1; j < nbodies; j++)
for (usize j = i + 1; j < nbodies; j++)
{
Planet* b2 = &bodies[j];
double dx = b.x - b2.x;
@@ -45,12 +45,12 @@ fn void advance(Planet[] bodies) @noinline
fn double energy(Planet[] bodies)
{
double e;
usz nbodies = bodies.len;
usize nbodies = bodies.len;
foreach (i, Planet* &b : bodies)
{
e += 0.5 * b.mass * (b.vx * b.vx + b.vy * b.vy + b.vz * b.vz);
for (usz j = i + 1; j < nbodies; j++)
for (usize j = i + 1; j < nbodies; j++)
{
Planet* b2 = &bodies[j];
double dx = b.x - b2.x;

View File

@@ -16,14 +16,14 @@ macro assert_exp($c, $e)
*/
/** A signed integer, whose size matches Value */
typedef isz Aint;
typedef isize Aint;
/** An unsigned integer, whose size matches Value */
typedef usz Auint;
typedef usize Auint;
/** A float, whose size matches Value (see avm_env.h) */
$assert(usz.size == 8 || usz.size == 4)
$if (usz.size == 8)
$assert(usize.size == 8 || usize.size == 4)
$if (usize.size == 8)
{
typedef double as Afloat;
}

View File

@@ -0,0 +1,21 @@
module binarydigits;
fn int main()
{
fot (int i = 0; i < 20; i++)
{
printf("%s\n", bin(i));
}
}
fn string bin(int x)
{
int bits = (x == 0) ? 1 : log10((double)(x)) / log10(2);
string ret = str.make_repeat('0' as bits);
for (int i = 0; i < bits; i++)
{
ret[bits - i - 1] = x & 1 ? '1' : '0';
x >>= 1;
}
return ret;
}

View File

@@ -14,7 +14,7 @@ fn void main()
Regex r;
r.initWithOptions("<.+?>", RegexOpt.GLOBAL) else unreachable;
r.initWithOptions("<.+?>", RegexOpt.GLOBAL) else @unreachable;
defer r.destroy();
foreach (RegexMatch* match : r.match(story))

View File

@@ -0,0 +1,107 @@
module map(Key, Type);
public struct Entry
{
Key key;
Type* value;
usize hash;
Entry* next;
}
public struct Map
{
usize size;
void* map;
uint mod;
}
public fn Map* Map.init(Map *map)
{
*map = { };
return map;
}
public fn Type* Map.valueForKey(Map *map, Key key)
{
if (!map.map) return nil;
usize hash = key.hash();
usize pos = hash & map.mod;
Entry* entry = &map.map[pop];
if () return nil;
while (entry)
{
if (entry.hash == hash && entry.key == key) return entry.value;
entry = entry.next;
}
return nil;
}
public fn Type *Map.setValueForKey(Map *map, Key key, Type *value)
{
if (!map.map)
{
map.map = @calloc(Entry, 16);
map.mod = 0x0F;
}
usize hash = key.hash();
usize pos = hash & map.mod;
Entry *entry = &map.map[pop];
while (1)
{
if (!entry.value)
{
entry.value = value;
entry.hash = hash;
entry.key = key;
return nil;
}
if (entry.hash == hash && entry.key == key)
{
Type *old = entry.value;
entry.value = value;
return old;
}
if (entry.next)
{
entry = entry.next;
}
entry.next = @malloc(Entry);
entry = entry.next;
}
}
public fn usize Map.size(Vector *vector)
{
return vector.array.size;
}
public fn void Map.removeLast(Vector *vector)
{
vector.array.pop();
}
public macro Vector.foreach(Vector *vector, macro void(Type value) body)
{
for (usize i = 0, i < vector.array.size; i++)
{
@body(vector.array[i]);
}
}
test
{
define IntVector = Vector(int);
IntVector vector = vector.init();
vector.add(1);
vector.add(2);
for (int i : vector)
{
printDigit(i);
}
@vector.foreach(int i)
{
printDigit(i);
}
vector.destroy();
}

View File

@@ -0,0 +1,19 @@
module test;
public macro retry(#function, int retries = 3)
{
error e;
while (1)
{
auto! result = #function;
try (result) return result;
catch (e = result);
} while (retries-- > 0)
return e!;
}
fn void main()
{
int! result = @retry(eventually_succeed());
}

View File

@@ -0,0 +1,21 @@
public test;
/**
* @require parse(a = b), parse(b = a)
*/
public macro void swap(&a, &b)
{
typeof(a) temp = a;
a = b;
b = temp;
}
/**
* @require parse(a = b), parse(b = a)
*/
public macro void swap2(auto &a, auto &b)
{
auto temp = a;
a = b;
b = temp;
}

View File

@@ -118,8 +118,8 @@ fn void! Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks)
p.errorMsg[0] = 0;
p.blocks = blocks;
mem::set(p.parents, 0, sizeof(Node*)*MaxDepth);
mem::set(p.lastChild, 0, sizeof(Node*)*MaxDepth);
memset(p.parents, 0, sizeof(Node*)*MaxDepth);
memset(p.lastChild, 0, sizeof(Node*)*MaxDepth);
p.numParents = 0;
p.topParent = nil;
@@ -438,13 +438,13 @@ fn const Node* Reader.findNode(const Reader* r, const char* key)
switch (*cp) {
case 0:
len = cast<u32>(cp - start);
mem::copy(name, start, len);
memcpy(name, start, len);
name[len] = 0;
node = r.blocks.findNode(name, node);
return node;
case '.':
len = cast<u32>(cp - start);
mem::copy(name, start, len);
memcpy(name, start, len);
name[len] = 0;
start = cp + 1;
node = r.blocks.findNode(name, node);
@@ -644,7 +644,7 @@ public type Blocks struct {
} @(opaque)
fn void Blocks.init(Blocks* b) {
mem::set(b, 0, sizeof(Blocks));
memset(b, 0, sizeof(Blocks));
b.nodes = calloc(MaxNodes, sizeof(Node));
b.namesSize = MaxNames;
@@ -659,7 +659,7 @@ fn void Blocks.init(Blocks* b) {
b.lastCache = 0;
//memset(b.namesCache, 0, sizeof(b.namesCache)); // sizeof(struct member) not supported yet
mem::set(b.namesCache, 0, sizeof(u32)*NamesCacheSize);
memset(b.namesCache, 0, sizeof(u32)*NamesCacheSize);
}
fn void Blocks.destroy(Blocks* b) {
@@ -702,7 +702,7 @@ fn u32 Blocks.addNode(Blocks* b, const char* name, NodeKind k) {
nameOffset = b.namesOffset;
node.nameOffset = nameOffset;
char* newname = &b.names[nameOffset];
mem::copy(newname, name, len);
memcpy(newname, name, len);
b.namesCache[b.lastCache] = nameOffset;
b.lastCache = (b.lastCache + 1) % NamesCacheSize;
b.namesOffset += len;
@@ -716,7 +716,7 @@ fn u32 Blocks.addValue(Blocks* b, const char* value) {
if (value[0] == 0) return 0;
u32 off = b.valuesOffset;
u32 len = cast<u32>(strlen(value)) + 1;
mem::copy(&b.values[off], value, len);
memcpy(&b.values[off], value, len);
b.valuesOffset += len;
return off;
}

View File

@@ -269,7 +269,7 @@ fn void Tokenizer.parseText(Tokenizer* t, Token* result)
uint len = (uint)(t.current - start);
// assert(len < MaxText);
mem::copy(t.text as start, len);
memcpy(t.text as start, len);
t.text[len] = 0;
result.kind = TokenKind.Text;
result.text = t.text;
@@ -310,7 +310,7 @@ fn void! Tokenizer.parseMultiText(Tokenizer* t, Token* result)
uint len = uint(t.current - start);
// assert(len < MaxText);
mem::copy(t.text, start, len);
memcpy(t.text, start, len);
t.text[len] = 0;
result.kind = TokenKind.Text;
result.text = t.text;
@@ -349,7 +349,7 @@ fn void Tokenizer.parseKey(Tokenizer* t, Token* result)
uint len = (uint)(t.current - start);
// assert(len < MaxText);
mem::copy(t.text, start, len);
memcpy(t.text, start, len);
t.text[len] = 0;
result.kind = TokenKind.Word;
result.text = t.text;

View File

@@ -1,84 +0,0 @@
module test;
import std::io;
import libc;
fault TokenResult
{
NO_MORE_TOKENS
}
// While we could have written this with libc
// the C way, let's showcase some features added to C3.
fn void main(char[][] args)
{
// Grab a string from stdin
String s = io::stdin().getline();
// Delete it at scope end [defer]
defer s.destroy();
// Grab the string as a slice.
char[] numbers = s.str();
// Track our current value
int val = 0;
// Is the current state an add?
bool add = true;
while (try char[] token = read_next(&numbers))
{
// We're assuming well formed input here
// so just use atoi.
int i = libc::atoi(token.ptr);
// This value is either added or subtracted from the sum.
val = add ? val + i : val - i;
// Read an optional token.
char[]! op = read_next(&numbers);
// If it's an error, then we're done.
if (catch op) break;
// Let's process the operator
switch (op)
{
case "+":
add = true;
case "-":
add = false;
default:
io::println("Failed to parse expression.");
return;
}
}
io::printfln("%d", val);
}
fn char[]! read_next(char[]* remaining)
{
while (remaining.len > 0 && (*remaining)[0] == ' ')
{
// Make the slice smaller
*remaining = (*remaining)[1..];
}
// Store the beginning of the parse
char* ptr_start = remaining.ptr;
usz len = 0;
while (remaining.len > 0 && (*remaining)[0] != ' ')
{
// Increase length
len++;
// And make the slice smaller
*remaining = (*remaining)[1..];
}
// If it's a zero length token, return an optional result.
if (!len) return TokenResult.NO_MORE_TOKENS!;
// Otherwise create a slice from the pointer start and length.
return ptr_start[:len];
}

View File

@@ -1,313 +0,0 @@
module arkanoid;
/**
*
* raylib - classic game: arkanoid
*
* Sample game developed by Marc Palau and Ramon Santamaria
* converted to C3 by Christoffer Lerno
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
*/
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 450;
//----------------------------------------------------------------------------------
// Some Defines
//----------------------------------------------------------------------------------
const PLAYER_MAX_LIFE = 5;
const LINES_OF_BRICKS = 5;
const BRICKS_PER_LINE = 20;
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
enum GameScreen
{
LOGO,
TITLE,
GAMEPLAY,
ENDING
}
struct Player
{
Vector2 position;
Vector2 size;
int life;
}
struct Ball
{
Vector2 position;
Vector2 speed;
int radius;
bool active;
}
struct Brick
{
Vector2 position;
bool active;
}
//------------------------------------------------------------------------------------
// Global Variables Declaration
//------------------------------------------------------------------------------------
bool game_over = false;
bool pause = false;
Player player;
Ball ball;
Brick[BRICKS_PER_LINE][LINES_OF_BRICKS] brick;
Vector2 brick_size;
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fn void main()
{
// Initialization (Note windowTitle is unused on Android)
//---------------------------------------------------------
raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: arkanoid");
init_game();
raylib::set_target_fps(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!raylib::window_should_close()) // Detect window close button or ESC key
{
// Update and Draw
//----------------------------------------------------------------------------------
update_draw_frame();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
unload_game(); // Unload loaded data (textures, sounds, models...)
raylib::close_window(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
//------------------------------------------------------------------------------------
// Module Functions Definitions (local)
//------------------------------------------------------------------------------------
// Initialize game variables
fn void init_game()
{
brick_size = { raylib::get_screen_width() / BRICKS_PER_LINE, 40 };
// Initialize player
player.position = { SCREEN_WIDTH/2, SCREEN_HEIGHT * 7 / 8 };
player.size = { SCREEN_WIDTH / 10, 20 };
player.life = PLAYER_MAX_LIFE;
// Initialize ball
ball.position = { SCREEN_WIDTH / 2, SCREEN_HEIGHT * 7 / 8 - 30 };
ball.speed = { 0, 0 };
ball.radius = 7;
ball.active = false;
// Initialize bricks
int initial_down_position = 50;
for (int i = 0; i < LINES_OF_BRICKS; i++)
{
for (int j = 0; j < BRICKS_PER_LINE; j++)
{
brick[i][j].position = { j * brick_size.x + brick_size.x / 2, i * brick_size.y + initial_down_position };
brick[i][j].active = true;
}
}
}
// Update game (one frame)
fn void update_game()
{
if (game_over)
{
if (raylib::is_key_pressed(keyboard::ENTER))
{
init_game();
game_over = false;
}
}
if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause;
if (pause) return;
// Player movement logic
if (raylib::is_key_down(keyboard::LEFT)) player.position.x -= 5;
if ((player.position.x - player.size.x/2) <= 0) player.position.x = player.size.x/2;
if (raylib::is_key_down(keyboard::RIGHT)) player.position.x += 5;
if ((player.position.x + player.size.x/2) >= SCREEN_WIDTH) player.position.x = SCREEN_WIDTH - player.size.x/2;
// Ball launching logic
if (!ball.active)
{
if (raylib::is_key_pressed(keyboard::SPACE))
{
ball.active = true;
ball.speed = { 0, -5 };
}
}
// Ball movement logic
if (ball.active)
{
ball.position.x += ball.speed.x;
ball.position.y += ball.speed.y;
}
else
{
ball.position = { player.position.x, SCREEN_HEIGHT * 7 / 8 - 30 };
}
// Collision logic: ball vs walls
if (((ball.position.x + ball.radius) >= SCREEN_WIDTH) || ((ball.position.x - ball.radius) <= 0)) ball.speed.x *= -1;
if ((ball.position.y - ball.radius) <= 0) ball.speed.y *= -1;
if ((ball.position.y + ball.radius) >= SCREEN_HEIGHT)
{
ball.speed = { 0, 0 };
ball.active = false;
player.life--;
}
// Collision logic: ball vs player
if (raylib::check_collision_circle_rec(ball.position, ball.radius,
Rectangle{ player.position.x - player.size.x / 2, player.position.y - player.size.y / 2, player.size.x, player.size.y}))
{
if (ball.speed.y > 0)
{
ball.speed.y *= -1;
ball.speed.x = (ball.position.x - player.position.x) / (player.size.x / 2) * 5;
}
}
// Collision logic: ball vs bricks
for (int i = 0; i < LINES_OF_BRICKS; i++)
{
for (int j = 0; j < BRICKS_PER_LINE; j++)
{
if (brick[i][j].active)
{
// Hit below
if (((ball.position.y - ball.radius) <= (brick[i][j].position.y + brick_size.y / 2)) &&
((ball.position.y - ball.radius) > (brick[i][j].position.y + brick_size.y / 2 + ball.speed.y)) &&
((math::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x / 2 + ball.radius * 2 / 3)) && (ball.speed.y < 0))
{
brick[i][j].active = false;
ball.speed.y *= -1;
}
// Hit above
else if (((ball.position.y + ball.radius) >= (brick[i][j].position.y - brick_size.y/2)) &&
((ball.position.y + ball.radius) < (brick[i][j].position.y - brick_size.y/2 + ball.speed.y)) &&
((math::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x/2 + ball.radius*2/3)) && (ball.speed.y > 0))
{
brick[i][j].active = false;
ball.speed.y *= -1;
}
// Hit left
else if (((ball.position.x + ball.radius) >= (brick[i][j].position.x - brick_size.x/2)) &&
((ball.position.x + ball.radius) < (brick[i][j].position.x - brick_size.x/2 + ball.speed.x)) &&
((math::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x > 0))
{
brick[i][j].active = false;
ball.speed.x *= -1;
}
// Hit right
else if (((ball.position.x - ball.radius) <= (brick[i][j].position.x + brick_size.x/2)) &&
((ball.position.x - ball.radius) > (brick[i][j].position.x + brick_size.x/2 + ball.speed.x)) &&
((math::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x < 0))
{
brick[i][j].active = false;
ball.speed.x *= -1;
}
}
}
}
// Game over logic
if (player.life <= 0)
{
game_over = true;
}
else
{
game_over = true;
for (int i = 0; i < LINES_OF_BRICKS; i++)
{
for (int j = 0; j < BRICKS_PER_LINE; j++)
{
if (brick[i][j].active) game_over = false;
}
}
}
}
// Draw game (one frame)
fn void draw_game()
{
raylib::begin_drawing();
raylib::clear_background(raylib::RAYWHITE);
if (!game_over)
{
// Draw player bar
raylib::draw_rectangle((int)(player.position.x - player.size.x/2), (int)(player.position.y - player.size.y/2), (int)player.size.x, (int)player.size.y, raylib::BLACK);
// Draw player lives
for (int i = 0; i < player.life; i++) raylib::draw_rectangle(20 + 40*i, SCREEN_HEIGHT - 30, 35, 10, raylib::LIGHTGRAY);
// Draw ball
raylib::draw_circle_v(ball.position, ball.radius, raylib::MAROON);
// Draw bricks
for (int i = 0; i < LINES_OF_BRICKS; i++)
{
for (int j = 0; j < BRICKS_PER_LINE; j++)
{
if (brick[i][j].active)
{
if ((i + j) % 2 == 0)
{
raylib::draw_rectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, raylib::GRAY);
}
else
{
raylib::draw_rectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, raylib::DARKGRAY);
}
}
}
}
if (pause) raylib::draw_text("GAME PAUSED", SCREEN_WIDTH/2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, raylib::GRAY);
}
else
{
raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width()/2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib::get_screen_height()/2 - 50, 20, raylib::GRAY);
}
raylib::end_drawing();
}
// Unload game variables
fn void unload_game()
{
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
}
// Update and Draw (one frame)
fn void update_draw_frame()
{
update_game();
draw_game();
}

View File

@@ -1,251 +0,0 @@
module snake;
/**
*
* raylib - classic game: snake
*
* Sample game developed by Ian Eito, Albert Martos and Ramon Santamaria,
* converted to C3 and modified by Christoffer Lerno
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
*
*/
const SNAKE_LENGTH = 256;
const SQUARE_SIZE = 32;
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 450;
enum SnakeDirection
{
RIGHT,
DOWN,
LEFT,
UP
}
struct Snake
{
Vector2 position;
Vector2 size;
Color color;
}
struct Food
{
Vector2 position;
Vector2 size;
bool active;
Color color;
}
int frames_counter = 0;
bool game_over = false;
bool pause = false;
Food fruit;
SnakeDirection snake_direction;
Snake[SNAKE_LENGTH] snake;
Vector2[SNAKE_LENGTH] snake_position;
bool allow_move = false;
Vector2 offset;
int counter_tail = 0;
fn void main()
{
raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: snake");
init_game();
raylib::set_target_fps(60);
while (!raylib::window_should_close()) // Detect window close button or ESC key
{
update_draw_frame();
}
unload_game();
raylib::close_window();
}
// Initialize game variables
fn void init_game()
{
frames_counter = 0;
game_over = false;
pause = false;
counter_tail = 1;
allow_move = false;
snake_direction = SnakeDirection.RIGHT;
offset.x = SCREEN_WIDTH % SQUARE_SIZE;
offset.y = SCREEN_HEIGHT % SQUARE_SIZE;
for (int i = 0; i < SNAKE_LENGTH; i++)
{
snake[i].position = { offset.x / 2, offset.y / 2 };
snake[i].size = { SQUARE_SIZE, SQUARE_SIZE };
if (i == 0)
{
snake[i].color = raylib::DARKBLUE;
}
else
{
snake[i].color = raylib::BLUE;
}
}
for (int i = 0; i < SNAKE_LENGTH; i++)
{
snake_position[i] = { 0.0f, 0.0f };
}
fruit.size = { SQUARE_SIZE, SQUARE_SIZE };
fruit.color = raylib::SKYBLUE;
fruit.active = false;
}
fn void update_game()
{
if (game_over)
{
if (raylib::is_key_pressed(keyboard::ENTER))
{
init_game();
game_over = false;
}
return;
}
if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause;
if (pause) return;
if (raylib::is_key_pressed(keyboard::RIGHT) && allow_move)
{
snake_direction = (SnakeDirection)((snake_direction + 1) % 4);
allow_move = false;
}
if (raylib::is_key_pressed(keyboard::LEFT) && allow_move)
{
snake_direction = (SnakeDirection)((snake_direction + 3) % 4);
allow_move = false;
}
// Snake movement
for (int i = 0; i < counter_tail; i++) snake_position[i] = snake[i].position;
if (frames_counter++ % 5 != 0) return;
allow_move = true;
switch (snake_direction)
{
case RIGHT:
snake[0].position.x += SQUARE_SIZE;
snake[0].position.y += 0;
case UP:
snake[0].position.x += 0;
snake[0].position.y += -SQUARE_SIZE;
case DOWN:
snake[0].position.x += 0;
snake[0].position.y += SQUARE_SIZE;
case LEFT:
snake[0].position.x += -SQUARE_SIZE;
snake[0].position.y += 0;
default:
unreachable();
}
for (int i = 1; i < counter_tail; i++)
{
snake[i].position = snake_position[i - 1];
}
// Wall behaviour
if (((snake[0].position.x) > (SCREEN_WIDTH - offset.x)) ||
((snake[0].position.y) > (SCREEN_HEIGHT - offset.y)) ||
(snake[0].position.x < 0) || (snake[0].position.y < 0))
{
game_over = true;
}
// Collision with yourself
for (int i = 1; i < counter_tail; i++)
{
if ((snake[0].position.x == snake[i].position.x) && (snake[0].position.y == snake[i].position.y)) game_over = true;
}
// Fruit position calculation
if (!fruit.active)
{
fruit.active = true;
fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
for (int i = 0; i < counter_tail; i++)
{
while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y))
{
fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
i = 0;
}
}
}
// Collision
if ((snake[0].position.x < (fruit.position.x + fruit.size.x) && (snake[0].position.x + snake[0].size.x) > fruit.position.x) &&
(snake[0].position.y < (fruit.position.y + fruit.size.y) && (snake[0].position.y + snake[0].size.y) > fruit.position.y))
{
snake[counter_tail].position = snake_position[counter_tail - 1];
counter_tail += 1;
fruit.active = false;
}
}
// Draw game (one frame)
fn void draw_game()
{
raylib::begin_drawing();
raylib::clear_background(raylib::RAYWHITE);
if (!game_over)
{
// Draw grid lines
for (int i = 0; i < SCREEN_WIDTH / SQUARE_SIZE + 1; i++)
{
raylib::draw_line_v({SQUARE_SIZE * i + offset.x/2, offset.y/2}, {SQUARE_SIZE * i + offset.x/2, SCREEN_HEIGHT - offset.y/2}, raylib::LIGHTGRAY);
}
for (int i = 0; i < SCREEN_HEIGHT/SQUARE_SIZE + 1; i++)
{
raylib::draw_line_v({offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, { SCREEN_WIDTH - offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, raylib::LIGHTGRAY);
}
// Draw snake
for (int i = 0; i < counter_tail; i++) raylib::draw_rectangle_v(snake[i].position, snake[i].size, snake[i].color);
// Draw fruit to pick
raylib::draw_rectangle_v(fruit.position, fruit.size, fruit.color);
if (pause) raylib::draw_text("GAME PAUSED", SCREEN_WIDTH/2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT / 2 - 40, 40, raylib::GRAY);
}
else
{
raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width()/2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib::get_screen_height()/2 - 50, 20, raylib::GRAY);
}
raylib::end_drawing();
}
// Unload game variables
fn void unload_game()
{
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
}
// Update and Draw (one frame)
fn void update_draw_frame()
{
update_game();
draw_game();
}

View File

@@ -1,793 +0,0 @@
module tetris;
/**
* raylib - classic game: tetris
*
* Sample game developed by Marc Palau and Ramon Santamaria,
* converted to C3 by Christoffer Lerno.
*
* This game has been created using raylib v1.3 (www.raylib.com)
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
*/
//----------------------------------------------------------------------------------
// Some Defines
//----------------------------------------------------------------------------------
const SQUARE_SIZE = 20;
const GRID_HORIZONTAL_SIZE = 12;
const GRID_VERTICAL_SIZE = 20;
const LATERAL_SPEED = 10;
const TURNING_SPEED = 12;
const FAST_FALL_AWAIT_COUNTER = 30;
const FADING_TIME = 33;
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
enum GridSquare { EMPTY, MOVING, FULL, BLOCK, FADING }
//------------------------------------------------------------------------------------
// Global Variables Declaration
//------------------------------------------------------------------------------------
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 450;
bool game_over = false;
bool pause = false;
// Matrices
GridSquare[GRID_VERTICAL_SIZE][GRID_HORIZONTAL_SIZE] grid;
GridSquare[4][4] piece;
GridSquare[4][4] incoming_piece;
struct IntVec
{
int x;
int y;
}
// These variables keep track of the active piece position
int piece_position_x = 0;
int piece_position_y = 0;
// Game parameters
Color fading_color;
//int fallingSpeed; // In frames
bool begin_play = true; // This var is only true at the begining of the game, used for the first matrix creations
bool piece_active = false;
bool detection = false;
bool line_to_delete = false;
// Statistics
int level = 1;
int lines = 0;
// Counters
int gravity_movement_counter = 0;
int lateral_movement_counter = 0;
int turn_movement_counter = 0;
int fast_fall_movement_counter = 0;
int fade_line_counter = 0;
// Based on level
int gravity_speed = 30;
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fn void main()
{
// Initialization (Note windowTitle is unused on Android)
//---------------------------------------------------------
raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: tetris");
init_game();
raylib::set_target_fps(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!raylib::window_should_close()) // Detect window close button or ESC key
{
// Update and Draw
//----------------------------------------------------------------------------------
update_draw_frame();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
unload_game(); // Unload loaded data (textures, sounds, models...)
raylib::close_window(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
//--------------------------------------------------------------------------------------
// Game Module Functions Definition
//--------------------------------------------------------------------------------------
// Initialize game variables
fn void init_game()
{
// Initialize game statistics
level = 1;
lines = 0;
fading_color = raylib::GRAY;
piece_position_x = 0;
piece_position_y = 0;
pause = false;
begin_play = true;
piece_active = false;
detection = false;
line_to_delete = false;
// Counters
gravity_movement_counter = 0;
lateral_movement_counter = 0;
turn_movement_counter = 0;
fast_fall_movement_counter = 0;
fade_line_counter = 0;
gravity_speed = 30;
// Initialize grid matrices
for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++)
{
for (int j = 0; j < GRID_VERTICAL_SIZE; j++)
{
if ((j == GRID_VERTICAL_SIZE - 1) || (i == 0) || (i == GRID_HORIZONTAL_SIZE - 1))
{
grid[i][j] = BLOCK;
}
else
{
grid[i][j] = EMPTY;
}
}
}
// Initialize incoming piece matrices
for (int i = 0; i < 4; i++)
{
for (int j = 0; j< 4; j++)
{
incoming_piece[i][j] = EMPTY;
}
}
}
// Update game (one frame)
fn void update_game()
{
if (game_over)
{
if (raylib::is_key_pressed(keyboard::ENTER))
{
init_game();
game_over = false;
}
return;
}
if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause;
if (pause) return;
if (line_to_delete)
{
// Animation when deleting lines
fade_line_counter++;
fading_color = fade_line_counter % 8 < 4 ? raylib::MAROON : raylib::GRAY;
if (fade_line_counter >= FADING_TIME)
{
lines += delete_complete_lines();
fade_line_counter = 0;
line_to_delete = false;
}
return;
}
if (!piece_active)
{
// Get another piece
piece_active = create_piece();
// We leave a little time before starting the fast falling down
fast_fall_movement_counter = 0;
}
else // Piece falling
{
// Counters update
fast_fall_movement_counter++;
gravity_movement_counter++;
lateral_movement_counter++;
turn_movement_counter++;
// We make sure to move if we've pressed the key this frame
if (raylib::is_key_pressed(keyboard::LEFT) || raylib::is_key_pressed(keyboard::RIGHT)) lateral_movement_counter = LATERAL_SPEED;
if (raylib::is_key_pressed(keyboard::UP)) turn_movement_counter = TURNING_SPEED;
// Fall down
if (raylib::is_key_down(keyboard::DOWN) && (fast_fall_movement_counter >= FAST_FALL_AWAIT_COUNTER))
{
// We make sure the piece is going to fall this frame
gravity_movement_counter += gravity_speed;
}
if (gravity_movement_counter >= gravity_speed)
{
// Basic falling movement
if (check_detection()) detection = true;
// Check if the piece has collided with another piece or with the boundings
resolve_falling_movement(&detection, &piece_active);
// Check if we fullfilled a line and if so, erase the line and pull down the the lines above
check_completion(&line_to_delete);
gravity_movement_counter = 0;
}
// Move laterally at player's will
if (lateral_movement_counter >= LATERAL_SPEED)
{
// Update the lateral movement and if success, reset the lateral counter
if (!resolve_lateral_movement()) lateral_movement_counter = 0;
}
// Turn the piece at player's will
if (turn_movement_counter >= TURNING_SPEED)
{
// Update the turning movement and reset the turning counter
if (resolve_turn_movement()) turn_movement_counter = 0;
}
}
// Game over logic
for (int j = 0; j < 2; j++)
{
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
{
if (grid[i][j] == GridSquare.FULL)
{
game_over = true;
}
}
}
}
// Draw game (one frame)
fn void draw_game()
{
raylib::begin_drawing();
raylib::clear_background(raylib::RAYWHITE);
if (game_over)
{
raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width() / 2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20) / 2, raylib::get_screen_height() / 2 - 50, 20, raylib::GRAY);
raylib::end_drawing();
return;
}
// Draw gameplay area
IntVec offset = {
SCREEN_WIDTH / 2 - (GRID_HORIZONTAL_SIZE * SQUARE_SIZE / 2) - 50,
SCREEN_HEIGHT / 2 - ((GRID_VERTICAL_SIZE - 1) * SQUARE_SIZE / 2) + SQUARE_SIZE * 2
};
offset.y -= 50; // NOTE: Harcoded position!
int controller = offset.x;
for (int j = 0; j < GRID_VERTICAL_SIZE; j++)
{
for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++)
{
// Draw each square of the grid
switch (grid[i][j])
{
case EMPTY:
raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY );
raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY );
raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY );
raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY );
offset.x += SQUARE_SIZE;
case FULL:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY);
offset.x += SQUARE_SIZE;
case MOVING:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::DARKGRAY);
offset.x += SQUARE_SIZE;
case BLOCK:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::LIGHTGRAY);
offset.x += SQUARE_SIZE;
case FADING:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, fading_color);
offset.x += SQUARE_SIZE;
default:
}
}
offset.x = controller;
offset.y += SQUARE_SIZE;
}
// Draw incoming piece (hardcoded)
offset.x = 500;
offset.y = 45;
controller = offset.x;
for (int j = 0; j < 4; j++)
{
for (int i = 0; i < 4; i++)
{
switch (incoming_piece[i][j])
{
case EMPTY:
raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY);
raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY);
raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY);
raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY);
offset.x += SQUARE_SIZE;
case MOVING:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY);
offset.x += SQUARE_SIZE;
default:
break;
}
}
offset.x = controller;
offset.y += SQUARE_SIZE;
}
raylib::draw_text("INCOMING:", offset.x, offset.y - 100, 10, raylib::GRAY);
raylib::draw_text(raylib::text_format("LINES: %04i", lines), offset.x, offset.y + 20, 10, raylib::GRAY);
if (pause)
{
raylib::draw_text("GAME PAUSED", SCREEN_WIDTH / 2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, raylib::GRAY);
}
raylib::end_drawing();
}
// Unload game variables
fn void unload_game()
{
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
}
// Update and Draw (one frame)
fn void update_draw_frame()
{
update_game();
draw_game();
}
//--------------------------------------------------------------------------------------
// Additional module functions
//--------------------------------------------------------------------------------------
fn bool create_piece()
{
piece_position_x = (int)((GRID_HORIZONTAL_SIZE - 4)/2);
piece_position_y = 0;
// If the game is starting and you are going to create the first piece, we create an extra one
if (begin_play)
{
get_random_piece();
begin_play = false;
}
// We assign the incoming piece to the actual piece
for (int i = 0; i < 4; i++)
{
for (int j = 0; j< 4; j++)
{
piece[i][j] = incoming_piece[i][j];
}
}
// We assign a random piece to the incoming one
get_random_piece();
// Assign the piece to the grid
for (int i = piece_position_x; i < piece_position_x + 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (piece[i - (int)piece_position_x][j] == GridSquare.MOVING) grid[i][j] = MOVING;
}
}
return true;
}
fn void get_random_piece()
{
int random = raylib::get_random_value(0, 6);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
incoming_piece[i][j] = EMPTY;
}
}
switch (random)
{
case 0:
incoming_piece[1][1] = MOVING;
incoming_piece[2][1] = MOVING;
incoming_piece[1][2] = MOVING;
incoming_piece[2][2] = MOVING; //Cube
case 1:
incoming_piece[1][0] = MOVING;
incoming_piece[1][1] = MOVING;
incoming_piece[1][2] = MOVING;
incoming_piece[2][2] = MOVING; //L
case 2:
incoming_piece[1][2] = MOVING;
incoming_piece[2][0] = MOVING;
incoming_piece[2][1] = MOVING;
incoming_piece[2][2] = MOVING; //L inversa
case 3:
incoming_piece[0][1] = MOVING;
incoming_piece[1][1] = MOVING;
incoming_piece[2][1] = MOVING;
incoming_piece[3][1] = MOVING; //Recta
case 4:
incoming_piece[1][0] = MOVING;
incoming_piece[1][1] = MOVING;
incoming_piece[1][2] = MOVING;
incoming_piece[2][1] = MOVING; //Creu tallada
case 5:
incoming_piece[1][1] = MOVING;
incoming_piece[2][1] = MOVING;
incoming_piece[2][2] = MOVING;
incoming_piece[3][2] = MOVING; //S
case 6:
incoming_piece[1][2] = MOVING;
incoming_piece[2][2] = MOVING;
incoming_piece[2][1] = MOVING;
incoming_piece[3][1] = MOVING; //S inversa
default:
unreachable();
}
}
fn void resolve_falling_movement(bool* detection_ref, bool* piece_active_ref)
{
// If we finished moving this piece, we stop it
if (*detection_ref)
{
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
{
if (grid[i][j] == GridSquare.MOVING)
{
grid[i][j] = FULL;
*detection_ref = false;
*piece_active_ref = false;
}
}
}
}
else // We move down the piece
{
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
{
if (grid[i][j] == GridSquare.MOVING)
{
grid[i][j+1] = MOVING;
grid[i][j] = EMPTY;
}
}
}
piece_position_y++;
}
}
fn bool resolve_lateral_movement()
{
bool collision = false;
// Piece movement
if (raylib::is_key_down(keyboard::LEFT)) // Move left
{
// Check if is possible to move to left
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
{
if (grid[i][j] == GridSquare.MOVING)
{
// Check if we are touching the left wall or we have a full square at the left
if ((i-1 == 0) || (grid[i-1][j] == GridSquare.FULL)) collision = true;
}
}
}
// If able, move left
if (!collision)
{
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) // We check the matrix from left to right
{
// Move everything to the left
if (grid[i][j] == GridSquare.MOVING)
{
grid[i-1][j] = MOVING;
grid[i][j] = EMPTY;
}
}
}
piece_position_x--;
}
}
else if (raylib::is_key_down(keyboard::RIGHT)) // Move right
{
// Check if is possible to move to right
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
{
if (grid[i][j] == GridSquare.MOVING)
{
// Check if we are touching the right wall or we have a full square at the right
if ((i+1 == GRID_HORIZONTAL_SIZE - 1) || (grid[i+1][j] == GridSquare.FULL))
{
collision = true;
}
}
}
}
// If able move right
if (!collision)
{
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
for (int i = GRID_HORIZONTAL_SIZE - 1; i >= 1; i--) // We check the matrix from right to left
{
// Move everything to the right
if (grid[i][j] == GridSquare.MOVING)
{
grid[i+1][j] = MOVING;
grid[i][j] = EMPTY;
}
}
}
piece_position_x++;
}
}
return collision;
}
fn bool resolve_turn_movement()
{
// Input for turning the piece
if (raylib::is_key_down(keyboard::UP))
{
GridSquare aux;
bool checker = false;
// Check all turning possibilities
if ((grid[piece_position_x + 3][piece_position_y] == GridSquare.MOVING) &&
(grid[piece_position_x][piece_position_y] != GridSquare.EMPTY) &&
(grid[piece_position_x][piece_position_y] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 3][piece_position_y + 3] == GridSquare.MOVING) &&
(grid[piece_position_x + 3][piece_position_y] != GridSquare.EMPTY) &&
(grid[piece_position_x + 3][piece_position_y] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x][piece_position_y + 3] == GridSquare.MOVING) &&
(grid[piece_position_x + 3][piece_position_y + 3] != GridSquare.EMPTY) &&
(grid[piece_position_x + 3][piece_position_y + 3] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x][piece_position_y] == GridSquare.MOVING) &&
(grid[piece_position_x][piece_position_y + 3] != GridSquare.EMPTY) &&
(grid[piece_position_x][piece_position_y + 3] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 1][piece_position_y] == GridSquare.MOVING) &&
(grid[piece_position_x][piece_position_y + 2] != GridSquare.EMPTY) &&
(grid[piece_position_x][piece_position_y + 2] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 3][piece_position_y + 1] == GridSquare.MOVING) &&
(grid[piece_position_x + 1][piece_position_y] != GridSquare.EMPTY) &&
(grid[piece_position_x + 1][piece_position_y] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 2][piece_position_y + 3] == GridSquare.MOVING) &&
(grid[piece_position_x + 3][piece_position_y + 1] != GridSquare.EMPTY) &&
(grid[piece_position_x + 3][piece_position_y + 1] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x][piece_position_y + 2] == GridSquare.MOVING) &&
(grid[piece_position_x + 2][piece_position_y + 3] != GridSquare.EMPTY) &&
(grid[piece_position_x + 2][piece_position_y + 3] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 2][piece_position_y] == GridSquare.MOVING) &&
(grid[piece_position_x][piece_position_y + 1] != GridSquare.EMPTY) &&
(grid[piece_position_x][piece_position_y + 1] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 3][piece_position_y + 2] == GridSquare.MOVING) &&
(grid[piece_position_x + 2][piece_position_y] != GridSquare.EMPTY) &&
(grid[piece_position_x + 2][piece_position_y] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 1][piece_position_y + 3] == GridSquare.MOVING) &&
(grid[piece_position_x + 3][piece_position_y + 2] != GridSquare.EMPTY) &&
(grid[piece_position_x + 3][piece_position_y + 2] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x][piece_position_y + 1] == GridSquare.MOVING) &&
(grid[piece_position_x + 1][piece_position_y + 3] != GridSquare.EMPTY) &&
(grid[piece_position_x + 1][piece_position_y + 3] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 1][piece_position_y + 1] == GridSquare.MOVING) &&
(grid[piece_position_x + 1][piece_position_y + 2] != GridSquare.EMPTY) &&
(grid[piece_position_x + 1][piece_position_y + 2] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 2][piece_position_y + 1] == GridSquare.MOVING) &&
(grid[piece_position_x + 1][piece_position_y + 1] != GridSquare.EMPTY) &&
(grid[piece_position_x + 1][piece_position_y + 1] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 2][piece_position_y + 2] == GridSquare.MOVING) &&
(grid[piece_position_x + 2][piece_position_y + 1] != GridSquare.EMPTY) &&
(grid[piece_position_x + 2][piece_position_y + 1] != GridSquare.MOVING)) checker = true;
if ((grid[piece_position_x + 1][piece_position_y + 2] == GridSquare.MOVING) &&
(grid[piece_position_x + 2][piece_position_y + 2] != GridSquare.EMPTY) &&
(grid[piece_position_x + 2][piece_position_y + 2] != GridSquare.MOVING)) checker = true;
if (!checker)
{
aux = piece[0][0];
piece[0][0] = piece[3][0];
piece[3][0] = piece[3][3];
piece[3][3] = piece[0][3];
piece[0][3] = aux;
aux = piece[1][0];
piece[1][0] = piece[3][1];
piece[3][1] = piece[2][3];
piece[2][3] = piece[0][2];
piece[0][2] = aux;
aux = piece[2][0];
piece[2][0] = piece[3][2];
piece[3][2] = piece[1][3];
piece[1][3] = piece[0][1];
piece[0][1] = aux;
aux = piece[1][1];
piece[1][1] = piece[2][1];
piece[2][1] = piece[2][2];
piece[2][2] = piece[1][2];
piece[1][2] = aux;
}
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
{
if (grid[i][j] == GridSquare.MOVING)
{
grid[i][j] = EMPTY;
}
}
}
for (int i = piece_position_x; i < piece_position_x + 4; i++)
{
for (int j = piece_position_y; j < piece_position_y + 4; j++)
{
if (piece[i - piece_position_x][j - piece_position_y] == GridSquare.MOVING)
{
grid[i][j] = MOVING;
}
}
}
return true;
}
return false;
}
fn bool check_detection()
{
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
{
if ((grid[i][j] == GridSquare.MOVING) && ((grid[i][j+1] == GridSquare.FULL)
|| (grid[i][j+1] == GridSquare.BLOCK))) return true;
}
}
return false;
}
fn void check_completion(bool *line_to_delete_ref)
{
int calculator = 0;
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
calculator = 0;
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
{
// Count each square of the line
if (grid[i][j] == GridSquare.FULL)
{
calculator++;
}
// Check if we completed the whole line
if (calculator == GRID_HORIZONTAL_SIZE - 2)
{
*line_to_delete_ref = true;
calculator = 0;
// points++;
// Mark the completed line
for (int z = 1; z < GRID_HORIZONTAL_SIZE - 1; z++)
{
grid[z][j] = FADING;
}
}
}
}
}
fn int delete_complete_lines()
{
int lines_to_erase = 0;
// Erase the completed line
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
{
while (grid[1][j] == GridSquare.FADING)
{
lines_to_erase++;
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
{
grid[i][j] = GridSquare.EMPTY;
}
for (int j2 = j-1; j2 >= 0; j2--)
{
for (int i2 = 1; i2 < GRID_HORIZONTAL_SIZE - 1; i2++)
{
switch (grid[i2][j2])
{
case FULL:
grid[i2][j2+1] = GridSquare.FULL;
grid[i2][j2] = GridSquare.EMPTY;
case FADING:
grid[i2][j2+1] = GridSquare.FADING;
grid[i2][j2] = GridSquare.EMPTY;
default:
}
}
}
}
}
return lines_to_erase;
}

View File

@@ -1,44 +0,0 @@
module test;
import libc;
fault TestErr
{
NOPE
}
fn int! eventually_succeed()
{
static int i = 0;
if (i++ < 3) return TestErr.NOPE!;
return i * 3;
}
macro @retry(#function, int retries = 3)
{
var $Type = $typeof(#function);
anyerr e;
do
{
$Type! result = #function;
if (catch err = result)
{
e = err;
continue;
}
return result;
} while (retries-- > 0);
return e!;
}
fn void main()
{
int! result = @retry(eventually_succeed());
if (try result)
{
libc::printf("Got result: %d\n", result);
}
else
{
libc::printf("Failed :(\n");
}
}

View File

@@ -46,9 +46,9 @@ fn void eval_AtA_times_u(double[] u, double[] atau) @noinline
fn int main(int argc, char **argv)
{
int n = (argc == 2) ? atoi(argv[1]) : 2000;
temparr = array::alloc(double, n);
double[] u = array::alloc(double, n);
double[] v = array::alloc(double, n);
temparr = @array::make(double, n);
double[] u = @array::make(double, n);
double[] v = @array::make(double, n);
foreach(&uval : u) *uval = 1;
for (int i = 0; i < 10; i++)
{

View File

@@ -1,20 +0,0 @@
module test;
import libc;
/**
* @checked a = b, b = a
*/
macro void @swap(&a, &b)
{
$typeof(a) temp = a;
a = b;
b = temp;
}
fn void main()
{
int x = 123;
int y = 456;
@swap(x, y);
libc::printf("x: %d y: %d\n", x, y);
}

View File

@@ -238,8 +238,8 @@ macro_argument_list
;
declaration
: optional_type IDENT '=' initializer
| optional_type IDENT
: failable_type IDENT '=' initializer
| failable_type IDENT
;
param_declaration
@@ -291,7 +291,7 @@ type
| type '[' '+' ']'
;
optional_type
failable_type
: type
| type '!'
;
@@ -419,8 +419,8 @@ expression_statement
if_expr
: optional_type IDENT '=' initializer
| optional_type IDENT NOFAIL_ASSIGN expression
: failable_type IDENT '=' initializer
| failable_type IDENT NOFAIL_ASSIGN expression
| expression
;
@@ -513,7 +513,7 @@ func_name
;
func_declaration
: FUNC optional_type func_name opt_parameter_type_list opt_attributes
: FUNC failable_type func_name opt_parameter_type_list opt_attributes
;
func_definition
@@ -594,7 +594,7 @@ const_declaration
;
func_typedef
: FUNC optional_type opt_parameter_type_list
: FUNC failable_type opt_parameter_type_list
;
typedef_declaration

View File

@@ -1,6 +1,8 @@
module antiprime;
import std::io;
extern fn int printf(char* message, ...);
fn int countDivisors(int n)
{
if (n < 2) return 1;
@@ -23,7 +25,7 @@ fn int main()
int d = countDivisors(n);
if (d > maxDiv)
{
io::printf("%d ", n);
printf("%d ", n);
maxDiv = d;
count++;
}

View File

@@ -1,87 +0,0 @@
// #target: macos-x64
module test;
import std::io;
import libc;
enum Foo
{
ABC
}
fn void print_pages()
{
mem::temp_allocator().print_pages(io::stdout());
}
fn void setstring(char* dst, char[] str)
{
foreach (char c : str)
{
dst++[0] = c;
}
dst[0] = 0;
}
fn void testAllocator(Allocator* a, int val)
{
io::println("Test");
void* data = a.alloc_aligned(val, 128, 16)!!;
io::printf("Aligned with offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
data = a.calloc_aligned(val, 128, 16)!!;
io::printf("Aligned with offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
data = a.realloc_aligned(data, val + 1, 128, 16)!!;
io::printf("Aligned with offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
data = a.realloc_aligned(data, val + 1, 128, 0)!!;
io::printf("No offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
io::printfln("Freeing %p", data);
a.free_aligned(data)!!;
}
fn void main()
{
char* small = tmalloc(128);
setstring(small, "small");
libc::printf("Small1: %p %s\n", small, small);
print_pages();
small = trealloc(small, 129, 1024 * 16);
libc::printf("Small2: %p %s\n", small, small);
print_pages();
small = trealloc(small, 12933);
libc::printf("Small3: %p %s\n", small, small);
print_pages();
char* first_big = tmalloc(9512);
void *big = tmalloc(4095);
io::printf("Big: %p\n", big);
io::printf("Small: %p\n", tmalloc(13));
print_pages();
@pool() {
big = trealloc(big, 5067);
print_pages();
void* hidden = tmalloc(4096);
io::printf("Hidden: %p\n", hidden);
io::printf("Big: %p\n", big);
big = trealloc(big, 4096, 256);
io::printf("Big: %p\n", big);
io::printf("First big: %p\n", first_big);
print_pages();
};
mem::@tscoped()
{
io::printf("Malloc: %p\n", malloc(23));
io::printf("Malloc: %p\n", malloc(23));
};
io::printf("Malloc: %p\n", malloc(23));
@pool()
{
io::printf("Talloc: %p\n", tmalloc(22));
};
testAllocator(mem::temp_allocator(), 126);
testAllocator(mem::temp_allocator(), 12600);
ArenaAllocator aa;
aa.init(&&char[1024] {});
testAllocator(&aa, 126);
io::println("Test dynamic arena");
DynamicArenaAllocator dynamic_arena;
dynamic_arena.init(1024);
testAllocator(&dynamic_arena, 112);
testAllocator(&dynamic_arena, 712);
first_big[3] = 123;
}

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