Compare commits

..

93 Commits

Author SHA1 Message Date
Christoffer Lerno
04695489b4 Bump version to 0.2.27 2022-07-30 02:55:32 +02:00
Christoffer Lerno
331f9b23f8 Attributes correctly checks for recursive definitions now. Added a max bitstruct size. 2022-07-30 02:55:32 +02:00
Christoffer Lerno
9886d381c0 Update allocator and resolution. 2022-07-30 02:55:32 +02:00
Christoffer Lerno
12c17b62cf Allow any expression as default expression. 2022-07-30 02:55:32 +02:00
Christoffer Lerno
2698ba1a94 Fix of expr location in args. 2022-07-27 21:21:36 +02:00
Christoffer Lerno
6f5f5feb97 Fixing distinct, typedef and bitstruct copying. Fix where global constants did not need to be constant. Bump to 0.2.26 2022-07-27 18:12:27 +02:00
Christoffer Lerno
64d883cb99 Fix bug on runtime "nameof" with optional values. Fixed issues with integer to enum casts. 0.2.25. Added enum_by_name. 2022-07-27 00:46:02 +02:00
Christoffer Lerno
1adc8b8264 Moved bitcast to builtin module. 2022-07-26 23:42:03 +02:00
Christoffer Lerno
c02ce5ce2a Better error on all upper parameter names. "Upper case" -> "uppercase" 2022-07-26 22:01:23 +02:00
Christoffer Lerno
e36e4b60e4 Fprintf changes. 2022-07-26 16:12:22 +02:00
Christoffer Lerno
1d808be4b9 Fix stdout defaults. 2022-07-26 15:07:37 +02:00
Christoffer Lerno
7065c28a08 Some fixes to string. Added fprintf and String.printf. Updated boolerr example. 2022-07-26 13:44:08 +02:00
Christoffer Lerno
da4df9d626 Less libc in example. 2022-07-26 02:21:49 +02:00
Christoffer Lerno
a7e4dda360 Added examples. 2022-07-26 02:10:18 +02:00
Christoffer Lerno
4471ccff13 String works with printf. Example cleanup. 2022-07-26 01:15:36 +02:00
Christoffer Lerno
cdff5c3e26 Dev (#500)
Single code path for kind/inner/len/sizeof on type and typeid. Fix of #493. Bump to 0.2.24. Remove ´func´ deprecated keyword. Unify builtin access. Enum and fault name reflection.
2022-07-26 00:56:59 +02:00
Christoffer Lerno
cc1bc58ed0 Allow using enums for indexing. 2022-07-24 15:39:11 +02:00
Christoffer Lerno
812bd8b3d0 Added $converable / $castable. Simplify and corrected if try/catch parsing. Fix bug with { [A] = 1 } 2022-07-24 15:10:04 +02:00
Christoffer Lerno
7e0a29ef40 Fix constant typeid comparisons. Allow methods to use & and * and constants. Improved error messages. Updated String type with generic append. 2022-07-24 15:10:04 +02:00
Christoffer Lerno
c1de3f059e Updated error messages. 2022-07-23 21:08:17 +02:00
Christoffer Lerno
62c1d2ddb5 Slicing a distinct type now returns the distinct type. 2022-07-23 20:46:21 +02:00
Christoffer Lerno
b313bec69d Fix to "typeid.kind". Conversion unsigned int -> enum fixed. @autoimport -> @builtin. Comparison macros. Bump to 0.2.21 2022-07-22 17:09:49 +02:00
Zhang Li Hui
036859c0c8 Added installation info for arch linux 2022-07-22 01:20:30 +02:00
Christoffer Lerno
56a6e0b112 Fix bug preventing implicit & on optionals. Updated priority queue to return optionals. Changed the list API to have snake case on methods. Bump to 0.2.20 2022-07-21 22:21:50 +02:00
Christoffer Lerno
18f7f35e80 More tests. 2022-07-21 21:20:03 +02:00
Christoffer Lerno
1d572f3e7c Allow distinct printthrough in printf. Added tests. 2022-07-21 18:13:11 +02:00
Christoffer Lerno
002ee006c1 More efficient int[] a = {}; Disallow zero length arrays. Bump to 0.2.19 (#489)
More efficient int[] a = {}; Disallow zero length arrays. Bump to 0.2.19. Improve error on mistyped types.
2022-07-20 22:25:03 +02:00
Christoffer Lerno
8afbccd3fe Fix bug in extension methods for generic types and typedefs. 2022-07-20 13:24:55 +02:00
David Kopec
6576725ed8 Add Binary-Heap Based Priority Queue to Standard Library (#481)
Priorityqueue
2022-07-20 12:22:43 +02:00
Christoffer Lerno
d3a053e049 Updated mangling and bump to 0.2.18 2022-07-20 12:22:03 +02:00
Christoffer Lerno
4afec24434 More advanced introspection. 2022-07-20 12:22:03 +02:00
Christoffer Lerno
29edd6e54e Fix to extension methods in other modules. Version 0.2.17 2022-07-19 14:17:00 +02:00
Christoffer Lerno
547d30eb1e Disallow complist as a regular macro parameter. 2022-07-18 01:22:29 +02:00
Christoffer Lerno
6cf3c9f46b Fix in nested block handling. @maydiscard and @nodiscard annotations. If the common type of int[x] and int[y] is int[] 2022-07-17 19:48:24 +02:00
Christoffer Lerno
4beb7eff8f Add support for : slices. Version bumped to 0.2.16 2022-07-17 19:48:24 +02:00
Christoffer Lerno
48a31cfa48 Fix of error where {| |} with value return could have no final return. 2022-07-17 19:48:24 +02:00
Christoffer Lerno
cd1138447e Update math.matrix.c3
Change module to std::math.
2022-07-16 15:33:09 +02:00
PixelRifts
c29ad77cdb Matrix Math Library 2022-07-16 15:33:09 +02:00
Christoffer Lerno
1c15ebe6d2 Fix of bug using ".len" 2022-07-16 12:09:12 +02:00
Christoffer Lerno
a68efec5e8 Added swap and list swap to stdlib. 2022-07-14 14:58:11 +02:00
Christoffer Lerno
3f6b0646b3 An initial printf. Added type.inner and type.len. Bump to 0.2.15 2022-07-14 02:43:53 +02:00
Christoffer Lerno
28a8e17690 Vararg abi fix. Version bumped to 0.2.14 2022-07-13 14:19:09 +02:00
Christoffer Lerno
2a7d46844a Fix "libs" in project creation. 2022-07-13 09:50:51 +02:00
Christoffer Lerno
92542ac1f9 Fix bug with bit struct initialization and zeros. Allow float mod. Add float.max/min. Version bumped to 0.2.13 2022-07-13 00:13:34 +02:00
Christoffer Lerno
59b41f8deb Reduce size of memory pages used. 2022-07-12 13:09:45 +02:00
Christoffer Lerno
abfccb5576 Fix issues with union of const. Bump version 0.2.12 2022-07-11 17:58:11 +02:00
Christoffer Lerno
ea5d7cd2e7 Fixes initialization of anonymous structs. Bump version 0.2.11 2022-07-10 23:29:05 +02:00
Christoffer Lerno
ca21b1daac Allow [in] to be used on subarray types. Added more to "conv" module. 2022-07-09 19:32:39 +02:00
Christoffer Lerno
9fdd66af42 Fix of distinct void* and null. Version bumped to 0.2.10 2022-07-08 17:16:44 +02:00
Christoffer Lerno
d403912ec7 Add linker and linked dir arguments to build files. 2022-07-08 14:52:58 +02:00
Christoffer Lerno
05f222616e Fix of default project creation target format. 2022-07-07 18:17:41 +02:00
Christoffer Lerno
253dbf3603 Remove std::mem 2022-07-07 18:14:36 +02:00
Christoffer Lerno
cfbfc29e84 Fix of $sizeof(Type) => Type.sizeof 2022-07-07 15:51:40 +02:00
Christoffer Lerno
bb020a1752 Add a windows install instruction. 2022-07-07 15:02:48 +02:00
Christoffer Lerno
c8a614e43f LLVM 15 compatibility fixes (#465)
More variant code. Fixes to typekind. Fixes to macro with failable returns. Remove use of LLVMConstInsert etc. Version 0.2.8
2022-07-06 16:41:52 +02:00
Christoffer Lerno
bb28f6e61c Fix stack setting after error return. Some fixes to examples. 2022-07-02 10:54:40 +02:00
Christoffer Lerno
b1d83e2ccd Auto-import std::core. Fix module assignment of declarations. Introspection improvements. Deref null error panics in safe mode. Support for LLVM 15 2022-06-29 21:57:35 +02:00
Christoffer Lerno
df41caabdd Global @align fixed #446. 2022-06-04 23:19:27 +02:00
Christoffer Lerno
2f5d51c92c Attempt to add more native file handling for MSVC (#459)
* Fix clean and update MSVC function calls.
2022-06-04 21:32:35 +02:00
Christoffer Lerno
224390ce5a Make builtins loaded by default. 2022-06-04 01:41:23 +02:00
Christoffer Lerno
09d50ebf6c New import rules. 2022-06-04 01:41:23 +02:00
Christoffer Lerno
2d608a4d51 Change TB dir and do debug printout by default. 2022-05-22 14:54:18 +02:00
Christoffer Lerno
d511f150a7 Add lld linking for FreeBSD. 2022-05-22 14:54:18 +02:00
Christoffer Lerno
f4dc4f64f2 Change TB dir and do debug printout by default. 2022-05-21 20:09:09 +02:00
Christoffer Lerno
6035cb4600 Update TB 2022-05-21 19:57:11 +02:00
Christoffer Lerno
3d1eaad6b9 Update file_utils.c 2022-05-18 22:30:07 +02:00
Christoffer Lerno
ca2bb505b6 Merge pull request #453 from matkuki/patch-1
Update CMakeLists.txt
2022-05-16 15:07:07 +02:00
matkuki
8b4b4273cc Update CMakeLists.txt
Fixes compilation error on Visual Studio 2022
2022-05-16 14:10:28 +02:00
Christoffer Lerno
7c91c56f3d Updated cmake with latest win-llvm (#451)
Updated cmake with latest win-llvm
2022-05-15 19:41:12 +02:00
Christoffer Lerno
5edafc5b2f Test for #149 ct lists. 2022-05-12 19:13:49 +02:00
Christoffer Lerno
dbb0dc302d Add instructions for building on Unix variants. 2022-05-12 09:53:05 +02:00
Christoffer Lerno
e09e5c06d3 User defined attributes. 2022-05-11 20:55:09 +02:00
Christoffer Lerno
b0c55ff777 Support enum associated values. 2022-05-11 20:55:09 +02:00
Christoffer Lerno
60832019bd Merge pull request #445 from c3lang/dev
Updated enums, macro usage / naming
2022-05-09 14:06:34 +02:00
Christoffer Lerno
42b5445225 Fixes enum set with new ordinal based enums. 2022-05-09 12:25:33 +02:00
Christoffer Lerno
9691d50a6f @ is now part of the name of an attribute or a macro. Macros without '@' must be function-like. 2022-05-08 22:16:33 +02:00
Christoffer Lerno
29a9769651 Ordinal based enums. 2022-05-08 21:39:00 +02:00
Christoffer Lerno
15e1db78a7 Remove unused code. 2022-05-02 16:35:14 +02:00
Christoffer Lerno
2f23d56a12 0.2.1 Update
0.2.1 SysV ABI fix for passing certain things by struct. Fix implicit…
2022-04-27 17:06:26 +02:00
Christoffer Lerno
22ee082d00 0.2.1 SysV ABI fix for passing certain things by struct. Fix implicitly converting to float in the case of myfloat *= -1. Prefer inferred constant over global. Allow locals to shadow global variables. 2022-04-27 16:43:25 +02:00
Christoffer Lerno
212bc7d9af Merge pull request #437 from c3lang/dev
0.2.0. Build system improvements. Target changes x64-windows -> windo…
2022-04-26 13:40:43 +02:00
Christoffer Lerno
890c4bc435 0.2.0. Build system improvements. Target changes x64-windows -> windows-x64, x64-darwin -> macos-x64. Improved mac support. LLD linking for Mac, Windows, Linux. Cross linking for Mac, Windows. Clean up string use. Fix of debug handling of multiple compilation units per module. MSVC CI 2022-04-26 13:20:33 +02:00
Christoffer Lerno
7df7dd2933 Merge pull request #436 from data-man/ci_testing
Use Ninja in CI
2022-04-17 11:09:00 +02:00
data-man
ada8652209 Use Ninja in CI 2022-04-16 15:25:48 +05:00
Christoffer Lerno
bf8288ed1c Merge pull request #435 from data-man/fix_typo
Fix typo in sema_expr.c [skip ci]
2022-04-16 11:19:30 +02:00
data-man
9b6e4f9d11 Fix typo in sema_expr.c [skip ci] 2022-04-16 13:12:14 +05:00
Christoffer Lerno
ecdcd8f959 0.1.1: Add -L and -l parameters, also incomplete support of .c3i files. 2022-04-15 18:52:36 +02:00
Dmitry Atamanov
828724f593 Add more vector tests (#430)
* Add more vector tests
* Added .ll output.
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2022-04-14 09:55:11 +02:00
data-man
f6eb20f725 Fix resource leak in check_file function 2022-04-13 10:17:14 +02:00
data-man
ade4065480 Add LLVM 13 to MacOS CI 2022-04-10 09:50:53 +02:00
Christoffer Lerno
b19cef4bc1 Removed some accidental commits. 2022-04-09 20:13:41 +02:00
Dmitry Atamanov
151cbfd706 Semi-implemented foreach for vectors (#423)
Implement foreach for vectors
2022-04-09 20:09:35 +02:00
Christoffer Lerno
b99db4be24 Allocators. Rename of "optenum" to fault. Memcpy and memset added. Cleanup of declaration use. 2022-04-09 20:07:59 +02:00
884 changed files with 44062 additions and 7972 deletions

View File

@@ -7,6 +7,36 @@ on:
branches: [ master ]
jobs:
build-msvc:
runs-on: windows-latest
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [ Release, Debug ]
defaults:
run:
shell: cmd
steps:
- uses: actions/checkout@v3
- 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: Build testproject
run: |
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log run hello_world_win32
- name: run compiler tests
run: |
cd test
python3.exe src/tester.py ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
build-msys2-mingw:
runs-on: windows-latest
strategy:
@@ -25,27 +55,30 @@ jobs:
with:
msystem: MINGW64
update: true
install: git binutils mingw-w64-x86_64-mlir mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-llvm mingw-w64-x86_64-polly mingw-w64-x86_64-python mingw-w64-x86_64-lld
install: git binutils mingw-w64-x86_64-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-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: |
mkdir build && cd build
cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build --debug-log
../../build/c3c run --debug-log
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c.exe test_suite/
build-msys2-clang:
runs-on: windows-latest
#if: ${{ false }}
if: ${{ false }}
strategy:
# Don't abort runners if a single one fails
fail-fast: false
@@ -66,13 +99,12 @@ jobs:
- name: CMake
run: |
mkdir build && cd build
cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build --debug-log
../../build/c3c run --debug-log
- name: run compiler tests
run: |
@@ -92,7 +124,7 @@ jobs:
- uses: actions/checkout@v3
- name: Install common deps
run: |
sudo apt-get install zlib1g zlib1g-dev python3
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build
- name: Install Clang ${{ matrix.llvm_version }}
run: |
@@ -111,19 +143,27 @@ jobs:
- name: CMake
run: |
mkdir build && cd build
cmake .. -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
cmake -B build -G Ninja -DCMAKE_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: Build testproject
run: |
cd resources/testproject
../../build/c3c build --debug-log
../../build/c3c run --debug-log
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --forcelinker
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
if [[ "${{matrix.llvm_version}}" < 15 ]]; then
python3 src/tester.py ../build/c3c test_suite/
else
python3 src/tester.py ../build/c3c test_suite2/
fi
build-mac:
runs-on: macos-latest
@@ -132,26 +172,30 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [12]
llvm_version: [12, 13, 14]
steps:
- uses: actions/checkout@v3
- name: Download LLVM
run: |
brew install llvm@${{ matrix.llvm_version }} botan
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
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
- name: CMake
run: |
mkdir build && cd build
cmake .. -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build .
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c build --debug-log
../../build/c3c run --debug-log
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --forcelinker
- name: run compiler tests
run: |

View File

@@ -1,10 +1,23 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.15)
project(c3c)
include(FetchContent)
include(FeatureSummary)
set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3")
#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")
@@ -23,29 +36,49 @@ if(C3_USE_MIMALLOC)
FetchContent_MakeAvailable(mimalloc)
endif()
if(NOT C3_LLVM_VERSION STREQUAL "auto")
if(${C3_LLVM_VERSION} VERSION_LESS 12 OR ${C3_LLVM_VERSION} VERSION_GREATER 15)
if (NOT C3_LLVM_VERSION STREQUAL "auto")
if (${C3_LLVM_VERSION} VERSION_LESS 12 OR ${C3_LLVM_VERSION} VERSION_GREATER 15)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
endif()
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "14")
endif()
FetchContent_Declare(
LLVM_Windows
URL https://github.com/c3lang/win-llvm/releases/download/llvm-vs22/llvm-14.0.1-windows-amd64-msvc17-libcmt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm-vs22/llvm-14.0.1-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows_debug)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_debug_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
else()
message("Loading Windows LLVM libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
endif()
find_package(LLVM REQUIRED CONFIG)
find_package(LLD REQUIRED CONFIG)
else()
find_package(LLVM REQUIRED CONFIG)
if (NOT C3_LLVM_VERSION STREQUAL "auto")
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
else()
find_package(LLVM REQUIRED CONFIG)
endif()
endif()
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
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")
set(LLVM_LINK_COMPONENTS
AllTargetsAsmParsers
AllTargetsCodeGens
@@ -73,9 +106,9 @@ set(LLVM_LINK_COMPONENTS
Target
TransformUtils
WindowsManifest
)
)
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER 14)
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER 14.1)
set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} WindowsDriver)
endif()
@@ -83,25 +116,31 @@ llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
# These don't seem to be reliable on windows.
if(UNIX)
message(STATUS "using find_library")
# find_library(TB_LIB NAMES tinybackend.a PATHS ${CMAKE_SOURCE_DIR}/resources/tblib)
find_library(LLD_COFF NAMES lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_COMMON NAMES lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_ELF NAMES lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_MACHO NAMES lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_MINGW NAMES lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_WASM NAMES lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS})
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})
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.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_DRIVER NAMES lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_READER_WRITER NAMES lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS})
find_library(LLD_YAML NAMES lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS})
endif()
if (${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}
@@ -113,10 +152,12 @@ if(UNIX)
${LLD_YAML}
${LLD_CORE}
)
message(STATUS "linking to llvm libs ${llvm_libs} ${lld_libs}")
endif()
if (APPLE)
set(lld_libs ${lld_libs} xar)
endif ()
message(STATUS "Found LLD ${lld_libs}")
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
@@ -138,6 +179,7 @@ add_executable(c3c
src/compiler/float.c
src/compiler/headers.c
src/compiler/lexer.c
src/compiler/libraries.c
src/compiler/linker.c
src/compiler/llvm_codegen.c
src/compiler/llvm_codegen_c_abi_aarch64.c
@@ -196,11 +238,13 @@ add_executable(c3c
src/utils/vmem.h
src/utils/whereami.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/tilde_codegen_type.c
src/compiler/windows_support.c)
if(NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC")
message(STATUS "using gcc/clang warning switches")
@@ -219,20 +263,31 @@ target_include_directories(c3c_wrappers PRIVATE
if(UNIX)
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
target_link_libraries(c3c m ${llvm_libs} c3c_wrappers ${lld_libs})
# target_link_libraries(c3c m ${llvm_libs} c3c_wrappers ${TB_LIB} ${lld_libs})
if(C3_USE_MIMALLOC)
target_link_libraries(c3c m mimalloc-static)
endif()
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()
# todo: maybe get this from llvm-config somehow? it should be in LLVM_DIR\..\..\..\bin I think.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -LIBPATH:C:\\llvm\\llvm\\build\\Release\\lib") # needed for lldCommon.lib
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -LIBPATH:${LLVM_LIBPATH}") # This doesn't seem to work for some reason
message(STATUS "${LLVM_LIBPATH}")
target_link_libraries(c3c debug ${llvm_libs} c3c_wrappers lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO lldYAML Advapi32)
target_link_libraries(c3c optimized ${llvm_libs} c3c_wrappers lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO lldYAML Advapi32)
target_compile_definitions(c3c PUBLIC TB_BACKEND=0)
endif()
if(C3_USE_MIMALLOC)
target_link_libraries(c3c mimalloc-static)
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
message("Adding MSVC options")
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
if(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()
endif()
if (WIN32)

View File

@@ -29,7 +29,6 @@ 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
{
@@ -47,7 +46,7 @@ fn void Stack.push(Stack* this, Type element)
if (this.capacity == this.size)
{
this.capacity *= 2;
this.elems = mem::realloc(this.elems, $sizeof(Type) * this.capacity);
this.elems = mem::realloc(this.elems, Type.sizeof * this.capacity);
}
this.elems[this.size++] = element;
}
@@ -129,7 +128,7 @@ fn void test()
### Current status
The current version of the compiler is alpha release 0.1.0.
The current version of the compiler is alpha release 0.2.
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.
@@ -142,8 +141,7 @@ Follow the issues [here](https://github.com/c3lang/c3c/issues).
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
or discuss C3 on its dedicated Discord: [https://discord.gg/qN76R87](https://discord.gg/qN76R87).
The compiler should compile on Linux, Windows (under MSVC, Mingw or MSYS2) and MacOS,
but needs some install documentation for Windows.
The compiler is currently verified to compile on Linux, Windows and MacOS.
@@ -151,7 +149,6 @@ but needs some install documentation for Windows.
- If you wish to contribute with ideas, please file issues or discuss on Discord.
- Interested in contributing to the stdlib? Please get in touch on Discord.
- Are you a Windows dev and know your way around Github CI? Please help us get MSVC CI working!
- Install instructions for other Linux and Unix variants are appreciated.
- 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.
@@ -160,6 +157,29 @@ but needs some install documentation for Windows.
### Installing
#### Installing on Windows
1. Make sure you have Visual Studio 17 2022 installed.
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`
#### 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
```
#### Installing 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.
@@ -169,7 +189,8 @@ but needs some install documentation for Windows.
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 .`
8. Set up CMake build: `cmake ..`
9. Build: `cmake --build .`
You should now have a `c3c` executable.
@@ -203,6 +224,18 @@ A `c3c` executable will be found under `bin/`.
8. Set up CMake build for debug: `cmake ..`
9. Build: `cmake --build .`
#### Installing on other Linux / Unix variants
1. Install CMake.
2. Install or compile LLVM and LLD *libraries* (version 12+ 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 .`
#### Getting started with a "hello world"
Create a `main.c3` file with:
@@ -232,4 +265,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

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

View File

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

View File

@@ -0,0 +1,182 @@
module std::core::mem::allocator;
private struct DynamicArenaPage
{
void* memory;
void* prev_arena;
usize total;
usize used;
void* last_ptr;
}
/**
* @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 && mem::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 && mem::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)?;
mem::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 = mem::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 = mem::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 = mem::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;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
private fn void*! dynamic_arena_allocator_function(Allocator* data, usize size, usize alignment, void* old_pointer, AllocationKind kind)
{
DynamicArenaAllocator* allocator = (DynamicArenaAllocator*)data;
switch (kind)
{
case CALLOC:
assert(!old_pointer, "Unexpected no old pointer for calloc.");
if (!size) return null;
void* mem = allocator.alloc(size, alignment)?;
mem::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();
}

View File

@@ -1,6 +1,10 @@
module std::mem;
module std::core::mem::allocator;
import libc;
private fn void*! null_allocator_fn(void *data, usize bytes, usize alignment, void* old_pointer, AllocationKind kind)
private const Allocator _NULL_ALLOCATOR = { &null_allocator_fn };
private const Allocator _SYSTEM_ALLOCATOR = { &libc_allocator_fn };
private fn void*! null_allocator_fn(Allocator* this, usize bytes, usize alignment, void* old_pointer, AllocationKind kind)
{
switch (kind)
{
@@ -13,10 +17,10 @@ private fn void*! null_allocator_fn(void *data, usize bytes, usize alignment, vo
}
}
fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* old_pointer, AllocationKind kind) @inline
fn void*! libc_allocator_fn(Allocator* 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");
assert(math::is_power_of_2(alignment), "Alignment was not a power of 2");
void* data;
switch (kind)
@@ -24,7 +28,7 @@ fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* ol
case ALLOC:
if (alignment > DEFAULT_MEM_ALIGNMENT)
{
data = (void*)aligned_offset((iptr)libc::malloc(bytes + alignment), alignment);
data = (void*)mem::aligned_offset((iptr)libc::malloc(bytes + alignment), alignment);
}
else
{
@@ -35,7 +39,7 @@ fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* ol
case CALLOC:
if (alignment > DEFAULT_MEM_ALIGNMENT)
{
data = (void*)aligned_offset((iptr)libc::calloc(bytes + alignment, 1), alignment);
data = (void*)mem::aligned_offset((iptr)libc::calloc(bytes + alignment, 1), alignment);
}
else
{
@@ -46,7 +50,7 @@ fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* ol
case REALLOC:
if (alignment > DEFAULT_MEM_ALIGNMENT)
{
data = (void*)aligned_offset((iptr)libc::realloc(old_pointer, bytes + alignment), alignment);
data = (void*)mem::aligned_offset((iptr)libc::realloc(old_pointer, bytes + alignment), alignment);
}
else
{
@@ -54,9 +58,11 @@ fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* ol
}
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
return data;
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION!;
case FREE:
libc::free(old_pointer);
return null;
}
@unreachable();
unreachable();
}

View File

@@ -0,0 +1,82 @@
module std::core::mem::allocator;
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
private fn void*! arena_allocator_function(Allocator* data, usize size, usize alignment, void* old_pointer, AllocationKind kind)
{
MemoryArena* arena = (MemoryArena*)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 && mem::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;
mem::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();
}
/**
* @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
**/
private 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 = mem::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;
}

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

@@ -0,0 +1,115 @@
// 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;
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
{
$typeof(variable) temp = variable;
defer variable = temp;
@body();
}
macro void @swap(&a, &b) @builtin
{
$typeof(a) 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 panic(char* message, char *file, char *function, uint line) @builtin
{
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 void unreachable($string = "Unreachable statement reached.") @builtin @noreturn
{
panic($string, $$FILE, $$FUNC, $$LINE);
$$unreachable();
}
macro bitcast(expr, $Type) @builtin
{
var $size = (usize)($sizeof(expr));
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
$Type x = void;
mem::memcpy(&x, &expr, $size, false, $alignof($Type), $alignof(expr));
return x;
}
/**
* @require $Type.kind == 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!;
}

View File

@@ -0,0 +1,76 @@
// 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 is_comparable_value(a) && 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 is_comparable_value(a) && 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 is_comparable_value(a) && 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 is_comparable_value(a) && 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 is_equatable_value(a) && 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;
}

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::cinterop;
module std::core::cinterop;
const C_INT_SIZE = $$C_INT_SIZE;
const C_LONG_SIZE = $$C_LONG_SIZE;

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

@@ -0,0 +1,404 @@
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 usize! char32_to_utf8(Char32 c, char* output, usize 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;
default:
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;
}
}
/**
* 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, usize *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, usize* size)
{
usize 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;
if (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];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
return uc + c & 0x3F;
}
if (max_size < 4) 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];
if (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 usize utf8_codepoints(char[] utf8)
{
usize 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 usize utf8len_for_utf32(Char32[] utf32)
{
usize 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 usize utf8len_for_utf16(Char16[] utf16)
{
usize len = 0;
usize len16 = utf16.len;
for (usize 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 usize utf16len_for_utf8(char[] utf8)
{
usize len = utf8.len;
usize len16 = 0;
for (usize 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 usize utf16len_for_utf32(Char32[] utf32)
{
usize 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 usize! utf32to8(Char32[] utf32, char[] utf8_buffer)
{
usize len = utf8_buffer.len;
char* ptr = utf8_buffer.ptr;
foreach (Char32 uc : utf32)
{
usize 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 usize! utf8to32(char[] utf8, Char32[] utf32_buffer)
{
usize len = utf8.len;
Char32* ptr = utf32_buffer.ptr;
usize len32 = 0;
usize buf_len = utf32_buffer.len;
for (usize i = 0; i < len;)
{
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED!;
usize 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)
{
usize len16 = utf16.len;
for (usize i = 0; i < len16;)
{
usize 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)
{
usize len = utf8.len;
for (usize i = 0; i < len;)
{
usize 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)
{
usize len = utf8.len;
for (usize i = 0; i < len;)
{
usize 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,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::env;
module std::core::env;
enum CompilerOptLevel
{

View File

@@ -1,20 +1,20 @@
// 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;
module std::core::mem;
macro volatile_load(&x)
macro @volatile_load(&x)
{
return $$volatile_load(&x);
}
macro volatile_store(&x, y)
macro @volatile_store(&x, y)
{
return $$volatile_store(&x, y);
}
/**
* @require @math::is_power_of_2(alignment)
* @require math::is_power_of_2(alignment)
**/
fn usize aligned_offset(usize offset, usize alignment)
{
@@ -23,7 +23,7 @@ fn usize aligned_offset(usize offset, usize alignment)
/**
* @require @math::is_power_of_2(alignment)
* @require math::is_power_of_2(alignment)
**/
fn bool ptr_is_aligned(void* ptr, usize alignment) @inline
{
@@ -32,7 +32,7 @@ fn bool ptr_is_aligned(void* ptr, usize alignment) @inline
fn void copy(char* dst, char* src, usize size) @inline
{
@memcpy(dst, src, size);
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)
@@ -42,7 +42,7 @@ macro void memcpy(void* dst, void* src, usize size, bool $is_volatile = false, u
fn void set(void* dst, char val, usize bytes) @inline
{
@memset(dst, val, bytes);
memset(dst, val, bytes);
}
macro void memset(void* dst, char val, usize bytes, bool $is_volatile = false, usize $dst_align = 0)
@@ -50,44 +50,27 @@ macro void memset(void* dst, char val, usize bytes, bool $is_volatile = false, u
$$memset(dst, val, bytes, $is_volatile, $dst_align);
}
macro bitcast(expr, $Type)
macro @clone(&value) @builtin
{
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;
$typeof(value)* x = malloc($typeof(value));
*x = value;
return x;
}
enum AllocationKind
{
ALLOC,
CALLOC,
REALLOC,
FREE,
}
fault AllocationFailure
{
OUT_OF_MEMORY
}
private tlocal Allocator thread_allocator = { SYSTEM_ALLOCATOR, null };
struct Allocator
{
AllocatorFunction function;
void *data;
}
macro malloc($Type)
macro malloc($Type) @builtin
{
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)
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void* alloc(usize size, usize alignment = 0)
{
@@ -95,7 +78,7 @@ fn void* alloc(usize size, usize alignment = 0)
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! alloc_checked(usize size, usize alignment = 0)
{
@@ -104,7 +87,7 @@ fn void*! alloc_checked(usize size, usize alignment = 0)
/**
* @require !alignment || @math::is_power_of_2(alignment)
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void* calloc(usize size, usize alignment = 0)
{
@@ -112,7 +95,7 @@ fn void* calloc(usize size, usize alignment = 0)
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! calloc_checked(usize size, usize alignment = 0)
{
@@ -120,7 +103,7 @@ fn void*! calloc_checked(usize size, usize alignment = 0)
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void* realloc(void *ptr, usize new_size, usize alignment = 0)
{
@@ -128,21 +111,24 @@ fn void* realloc(void *ptr, usize new_size, usize alignment = 0)
}
/**
* @require !alignment || @math::is_power_of_2(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)
fn void free(void* ptr) @builtin
{
return thread_allocator.free(ptr)!!;
}
macro void with_allocator(Allocator allocator; @body())
/**
* Run with a specific allocator inside of the macro body.
**/
macro void @with_allocator(Allocator* allocator; @body())
{
Allocator old_allocator = thread_allocator;
Allocator* old_allocator = thread_allocator;
thread_allocator = allocator;
defer thread_allocator = old_allocator;
@body();
@@ -150,6 +136,15 @@ macro void with_allocator(Allocator allocator; @body())
fn void*! talloc(usize size)
{
return default_allocator.alloc(size);
return temp_allocator.alloc(size);
}
private tlocal Allocator* thread_allocator = allocator::LIBC_ALLOCATOR;
macro Allocator* current_allocator()
{
return thread_allocator;
}

View File

@@ -0,0 +1,151 @@
module std::core::mem::allocator;
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);
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR;
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR;
define AllocatorFunction = fn void*!(Allocator* allocator, usize new_size, usize alignment, void* old_pointer, AllocationKind kind);
struct Allocator
{
AllocatorFunction function;
}
enum AllocationKind
{
ALLOC,
CALLOC,
REALLOC,
FREE,
RESET,
}
fault AllocationFailure
{
OUT_OF_MEMORY,
UNSUPPORTED_OPERATION,
}
/**
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! Allocator.alloc(Allocator* allocator, usize size, usize alignment = 0) @inline
{
return allocator.function(allocator, 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, 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, size, alignment, null, CALLOC);
}
fn void! Allocator.free(Allocator* allocator, void* old_pointer) @inline
{
allocator.function(allocator, 0, 0, old_pointer, FREE)?;
}
fn void Allocator.reset(Allocator* allocator)
{
allocator.function(allocator, 0, 0, null, RESET)!!;
}
private fn usize alignment_for_allocation(usize alignment) @inline
{
if (alignment < DEFAULT_MEM_ALIGNMENT)
{
alignment = DEFAULT_SIZE_PREFIX_ALIGNMENT;
}
return alignment;
}
struct DynamicArenaAllocator
{
inline Allocator allocator;
Allocator* backing_allocator;
DynamicArenaPage* page;
DynamicArenaPage* unused_page;
usize page_size;
}
/**
* @require page_size >= 128
* @require this != null
**/
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator* backing_allocator = mem::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 MemoryArena
{
inline Allocator allocator;
void* memory;
usize total;
usize used;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void MemoryArena.init(MemoryArena* this, char[] data)
{
this.function = &arena_allocator_function;
this.memory = data.ptr;
this.total = data.len;
this.used = 0;
}
/**
* @require this != null
**/
fn void MemoryArena.reset(MemoryArena* this)
{
this.used = 0;
}

22
lib/std/core/mem_array.c3 Normal file
View File

@@ -0,0 +1,22 @@
// 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 usize.max / elements > $Type.sizeof
**/
macro alloc($Type, usize elements)
{
$Type* ptr = mem::alloc($Type.sizeof * elements, $alignof($Type));
return ptr[:elements];
}
/**
* @require (usize.max / elements > $Type.sizeof)
**/
macro make($Type, usize elements)
{
$Type* ptr = mem::calloc($sizeof($Type) * elements, $alignof($Type));
return ptr[:elements];
}

View File

@@ -1,4 +1,4 @@
module std::mem;
module std::core::mem;
const TEMP_BLOCK_SIZE = 1024;
const TEMP_PAGES = 64;
@@ -6,7 +6,7 @@ const TEMP_PAGES = 64;
private char[TEMP_BLOCK_SIZE * TEMP_PAGES] allocator_static_storage;
private void*[TEMP_PAGES] allocator_static_page_storage;
SlotAllocator default_allocator = {
SlotAllocator temp_allocator = {
.pages = &allocator_static_storage,
.page_size = TEMP_BLOCK_SIZE,
.page_count = TEMP_PAGES,
@@ -30,12 +30,12 @@ fn void*! SlotAllocator.alloc(SlotAllocator *allocator, usize size)
if (*page_pointer)
{
// TODO fix
main_allocator.free(*page_pointer)?;
thread_allocator.free(*page_pointer)?;
*page_pointer = null;
}
if (size > allocator.page_size - $sizeof(page_pointer))
{
void* mem = main_allocator.alloc(size)?;
void* mem = thread_allocator.alloc(size)?;
*page_pointer = mem;
allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask);
return mem;

View File

@@ -1,5 +1,4 @@
module std::os::linux;
import std::env;
module std::core::os::linux;
$if (env::OS_TYPE == OsType.LINUX):

View File

@@ -1,6 +1,4 @@
module std::os::macos;
import std::env;
module std::core::os::macos;
$if (env::OS_TYPE == OsType.MACOSX):
extern fn int* __error();

View File

@@ -1,5 +1,4 @@
module std::os::windows;
import std::env;
module std::core::os::windows;
$if (env::OS_TYPE == OsType.WIN32):

160
lib/std/core/str.c3 Normal file
View File

@@ -0,0 +1,160 @@
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;
fn String join(char[][] s, char[] joiner)
{
if (!s.len) return (String)null;
usize 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;
}
fn ZString copy_zstring(char[] s)
{
usize len = s.len;
char* str = mem::alloc(len + 1);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn ZString tcopy_zstring(char[] s)
{
usize len = s.len;
char* str = mem::talloc(len + 1)!!;
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
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 usize utf8_codepoints(char[] utf8)
{
usize len = 0;
foreach (char c : utf8)
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
fn Char32[]! utf8to32(char[] utf8, Allocator* allocator = mem::current_allocator)
{
usize codepoints = conv::utf8_codepoints(utf8);
Char32* data = allocator.alloc(Char32.sizeof * (codepoints + 1))?;
conv::utf8to32_unsafe(utf8, data)?;
data[codepoints] = 0;
return data[0..codepoints - 1];
}
fn char[] utf32to8(Char32[] utf32, Allocator* allocator = mem::current_allocator)
{
usize len = conv::utf8len_for_utf32(utf32);
char* data = allocator.alloc(len + 1)!!;
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return data[0..len - 1];
}
fn Char16[]! utf8to16(char[] utf8, Allocator* allocator = mem::current_allocator)
{
usize len16 = conv::utf16len_for_utf8(utf8);
Char16* data = allocator.alloc((len16 + 1) * Char16.sizeof)?;
conv::utf8to16_unsafe(utf8, data)?;
data[len16] = 0;
return data[0..len16 - 1];
}
fn char[]! utf16to8(Char16[] utf16, Allocator* allocator = mem::current_allocator())
{
usize len = conv::utf8len_for_utf16(utf16);
char* data = allocator.alloc(len + 1)?;
conv::utf16to8_unsafe(utf16, data)?;
return data[0 .. len - 1];
}
fn char[] copy(char[] s)
{
usize len = s.len;
ZString str_copy = copy_zstring(s) @inline;
return str_copy[..len];
}
fn char[] tcopy(char[] s)
{
usize len = s.len;
ZString str_copy = tcopy_zstring(s) @inline;
return str_copy[..len];
}
fn char[] tconcat(char[] s1, char[] 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 char[] concat(char[] s1, char[] 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];
}
fn usize ZString.len(ZString *str)
{
usize len = 0;
char* ptr = (char*)*str;
while (char c = ptr++[0])
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}

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

@@ -0,0 +1,299 @@
module std::core::string;
import libc;
define String = distinct void*;
private struct StringData
{
Allocator* allocator;
usize len;
usize capacity;
char[*] chars;
}
const usize MIN_CAPACITY = 16;
fn String new_with_capacity(usize 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)
{
usize 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 usize String.len(String this)
{
if (!this) return 0;
return this.data().len;
}
/**
* @require new_size <= this.len()
*/
fn void String.chop(String this, usize 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, usize index, char c)
{
str.data().chars[index] = c;
}
fn void String.append_repeat(String* str, char c, usize times)
{
if (times == 0) return;
str.reserve(times);
StringData* data = str.data();
for (usize 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.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())
{
usize str_len = str.len();
if (!str_len)
{
return (ZString)allocator.calloc(1, 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 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;
usize 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;
usize str1_len = str1.len;
usize 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)
{
usize 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, usize addition)
{
StringData* data = str.data();
if (!data)
{
*str = string::new_with_capacity(addition);
return;
}
usize len = data.len + addition;
if (data.capacity >= len) return;
usize new_capacity = data.capacity * 2;
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
*str = (String)data.allocator.realloc(data, StringData.sizeof + new_capacity)!!;
}

View File

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

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

@@ -0,0 +1,159 @@
module std::core::types;
import libc;
fault ConversionResult
{
VALUE_OUT_OF_RANGE,
VALUE_OUT_OF_UNSIGNED_RANGE,
}
/**
* @require type.kind == SIGNED_INT || type.kind == UNSIGNED_INT || type.kind == ENUM, "Argument was not an integer"
**/
macro variant_to_int(variant v, $Type)
{
typeid variant_type = v.type;
TypeKind kind = variant_type.kind;
if (kind == TypeKind.ENUM)
{
variant_type = variant_type.inner;
kind = variant_type.kind;
}
bool is_mixed_signed = $Type.kind != variant_type.kind;
$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:
$if (env::I128_SUPPORT):
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;
$else:
unreachable();
$endif;
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:
$if (env::I128_SUPPORT):
uint128 i = *(uint128*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
return ($Type)i;
$else:
unreachable();
$endif;
default:
unreachable();
}
}
macro bool is_numerical($Type)
{
var $kind = $Type.kind;
$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_comparable($Type)
{
var $kind = $Type.kind;
$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_value(value)
{
$if ($defined(value.less) || $defined(value.compare_to) || $defined(value.equals)):
return true;
$else:
return is_comparable($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,
FAILABLE,
ARRAY,
SUBARRAY,
VECTOR,
DISTINCT,
POINTER,
VARIANT
}
struct TypeEnum
{
TypeKind type;
usize elements;
}

View File

@@ -1,23 +1,21 @@
// TODO: ensure the type is an enum first.
module enumset<Enum>;
module std::enumset<Enum>;
$assert(Enum.min < Enum.max, "Only strictly increasing enums may be used with enum sets.");
$assert(Enum.max < 64, "Maximum value of an enum used as enum set is 63");
$assert(Enum.min >= 0, "Minimum value of an enum used as enum set is 0");
$assert(Enum.elements < 64, "Maximum number of elements for an enum used as enum set is 63");
$switch ($$C_INT_SIZE):
$case 64:
private define EnumSetType = ulong;
$case 32:
$if (Enum.max < 32):
$if (Enum.elements < 32):
private define EnumSetType = uint;
$else:
private define EnumSetType = ulong;
$endif;
$default:
$if (Enum.max < 16):
$if (Enum.elements < 16):
private define EnumSetType = ushort;
$elif (Enum.max < 31):
$elif (Enum.elements < 31):
private define EnumSetType = uint;
$else:
private define EnumSetType = ulong;
@@ -26,17 +24,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);
@@ -44,37 +42,42 @@ 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 EnumSet EnumSet.and_of(EnumSet *this, EnumSet 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)
{
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

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

902
lib/std/io_printf.c3 Normal file
View File

@@ -0,0 +1,902 @@
module std::io;
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,
}
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;
}
struct PrintParam
{
OutputFn outfn;
void* buffer;
PrintFlags flags;
uint width;
uint prec;
usize idx;
}
define OutputFn = fn void!(char c, void* buffer, usize buffer_idx);
fn void! PrintParam.out(PrintParam* param, char c)
{
param.outfn(c, param.buffer, param.idx++)?;
}
private fn void! out_str(PrintParam* param, variant arg)
{
switch (arg.type.kind)
{
case TYPEID:
return out_substr(param, "<typeid>");
case VOID:
return out_substr(param, "void");
case ANYERR:
case FAULT:
return out_substr(param, (*(anyerr*)arg.ptr).nameof);
case VARIANT:
return out_substr(param, "<variant>");
case ENUM:
return out_substr(param, arg.type.names[types::variant_to_int(arg, usize)!!]);
case STRUCT:
return out_substr(param, "<struct>");
case UNION:
return out_substr(param, "<union>");
case BITSTRUCT:
return out_substr(param, "<bitstruct>");
case FUNC:
return out_substr(param, "<func>");
case FAILABLE:
unreachable();
case DISTINCT:
if (arg.type == String.typeid)
{
return out_substr(param, ((String*)arg).str());
}
return out_str(param, variant { arg.ptr, arg.type.inner });
case POINTER:
typeid inner = arg.type.inner;
if (inner.kind == TypeKind.ARRAY && inner.inner == char.typeid)
{
char *ptr = *(char**)arg.ptr;
return out_substr(param, ptr[:inner.len]);
}
return ntoa_variant(param, arg, 16);
case SIGNED_INT:
case UNSIGNED_INT:
return ntoa_variant(param, arg, 10);
case FLOAT:
return ftoa(param, float_from_variant(arg));
case ARRAY:
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usize size = inner.sizeof;
usize len = arg.type.len;
// Pretend this is a char[]
void* ptr = (void*)arg.ptr;
param.out('[')?;
for (usize i = 0; i < len; i++)
{
if (i != 0) out_substr(param, ", ")?;
out_str(param, variant { ptr, inner })?;
ptr += size;
}
return param.out(']');
case VECTOR:
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usize size = inner.sizeof;
usize len = arg.type.len;
// Pretend this is a char[]
void* ptr = (void*)arg.ptr;
out_substr(param, "[<")?;
for (usize i = 0; i < len; i++)
{
if (i != 0) out_substr(param, ", ")?;
out_str(param, variant { ptr, inner })?;
ptr += size;
}
return out_substr(param, ">]");
case SUBARRAY:
// this is SomeType[] so grab the "SomeType"
typeid inner = arg.type.inner;
if (inner == char.typeid)
{
return out_substr(param, *(char[]*)arg);
}
usize size = inner.sizeof;
// Pretend this is a char[]
char[]* temp = (void*)arg.ptr;
void* ptr = (void*)temp.ptr;
usize len = temp.len;
param.out('[')?;
for (usize i = 0; i < len; i++)
{
if (i != 0) out_substr(param, ", ")?;
out_str(param, variant { ptr, inner })?;
ptr += size;
}
param.out(']')?;
case BOOL:
if (*(bool*)arg.ptr)
{
return out_substr(param, "true");
}
else
{
return out_substr(param, "false");
}
default:
return out_substr(param, "Invalid type");
}
}
private fn uint simple_atoi(char* buf, usize maxlen, usize* len_ptr) @inline
{
uint i = 0;
usize 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;
}
fault FormattingFault
{
UNTERMINATED_FORMAT,
MISSING_ARG,
INVALID_WIDTH_ARG,
INVALID_FORMAT_TYPE,
}
private fn void! printf_advance_format(usize format_len, usize *index_ptr) @inline
{
usize val = ++(*index_ptr);
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!;
}
private fn variant! next_variant(variant* args_ptr, usize args_len, usize* 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, usize args_len, usize* args_index_ptr, char* format_ptr, usize format_len, usize* 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.kind.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;
}
private fn void! out_buffer_fn(char c, char[] buffer, usize buffer_idx)
{
if (buffer_idx >= buffer.len) return PrintFault.BUFFER_EXCEEDED!;
buffer[buffer_idx] = c;
}
private fn void! out_null_fn(char c @unused, void* data @unused, usize idx @unused)
{
}
private fn void! out_putchar_fn(char c, void* data @unused, usize idx @unused)
{
libc::putchar(c);
}
private fn void! out_fputchar_fn(char c, void* data, usize idx @unused)
{
File* f = data;
f.putc(c)?;
}
private fn void! out_string_append_fn(char c, void* data, usize idx @unused)
{
String* s = data;
s.append_char(c);
}
private fn void! PrintParam.out_reverse(PrintParam* param, char[] buf)
{
usize buffer_start_idx = param.idx;
usize len = buf.len;
// pad spaces up to given width
if (!param.flags.left && !param.flags.zeropad)
{
for (usize i = len; i < param.width; i++)
{
param.out(' ')?;
}
}
// reverse string
while (len) param.out(buf[--len])?;
// append pad spaces up to given width
return param.left_adjust(param.idx - buffer_start_idx);
}
private fn void! out_char(PrintParam* param, variant arg)
{
uint l = 1;
// pre padding
param.right_adjust(l)?;
// char output
Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD;
switch (true)
{
case c < 0x7f:
param.out((char)c)?;
case c < 0x7ff:
param.out((char)(0xC0 | c >> 6))?;
param.out((char)(0x80 | (c & 0x3F)))?;
case c < 0xffff:
param.out((char)(0xE0 | c >> 12))?;
param.out((char)(0x80 | (c >> 6 & 0x3F)))?;
param.out((char)(0x80 | (c & 0x3F)))?;
default:
param.out((char)(0xF0 | c >> 18))?;
param.out((char)(0x80 | (c >> 12 & 0x3F)))?;
param.out((char)(0x80 | (c >> 6 & 0x3F)))?;
param.out((char)(0x80 | (c & 0x3F)))?;
}
return param.left_adjust(l);
}
private fn void! ntoa_format(PrintParam* param, char[] buf, usize len, bool negative, uint base)
{
// pad leading zeros
if (!param.flags.left)
{
if (param.width && param.flags.zeropad && (negative || param.flags.plus || param.flags.space)) param.width--;
while (len < param.prec)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
}
while (param.flags.zeropad && len < param.width)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
}
}
// handle hash
if (param.flags.hash && base != 10)
{
if (!param.flags.precision && len && len == param.prec && len == param.width)
{
len--;
if (len) len--;
}
if (base != 10)
{
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
switch (base)
{
case 16:
buf[len++] = param.flags.uppercase ? 'X' : 'x';
case 8:
buf[len++] = param.flags.uppercase ? 'O' : 'o';
case 2:
buf[len++] = param.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 param.flags.plus:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '+';
case param.flags.space:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = ' ';
}
if (!len) return;
return param.out_reverse(buf[:len]);
}
$if (env::I128_SUPPORT):
define NtoaType = uint128;
$else:
define NtoaType = ulong;
$endif;
private fn void! ntoa_variant(PrintParam* param, variant arg, uint base)
{
bool is_neg;
NtoaType val = int_from_variant(arg, &is_neg);
return ntoa(param, val, is_neg, base) @inline;
}
private fn void! ntoa(PrintParam* param, NtoaType value, bool negative, uint base)
{
char[PRINTF_NTOA_BUFFER_SIZE] buf = void;
usize len = 0;
// no hash for 0 values
if (!value) param.flags.hash = false;
// write if precision != 0 or value is != 0
if (!param.flags.precision || value)
{
char past_10 = (param.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 ntoa_format(param, buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
}
define FloatType = double;
// internal ftoa for fixed decimal floating point
private fn void! ftoa(PrintParam* param, FloatType value)
{
char[PRINTF_FTOA_BUFFER_SIZE] buf = void;
usize 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 param.out_reverse("nan");
}
if (value < -FloatType.max)
{
return param.out_reverse("fni-");
}
if (value > FloatType.max)
{
if (param.flags.plus)
{
return param.out_reverse("fni+");
}
return param.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 etoa(param, value);
}
// test for negative
bool negative = value < 0;
if (negative) value = 0 - value;
// set default precision, if not set explicitly
if (!param.flags.precision) param.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while (param.prec > 9)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
param.prec--;
}
// Safe due to 1e9 limit.
int whole = (int)value;
FloatType tmp = (value - whole) * POW10[param.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[param.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 (!param.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 = param.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 (!param.flags.left && param.flags.zeropad)
{
if (param.width && (negative || param.flags.plus || param.flags.space)) param.width--;
while (len < param.width)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
}
}
char next = {|
if (negative) return '-';
if (param.flags.plus) return '+';
if (param.flags.space) return ' ';
return 0;
|};
if (next)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = next;
}
return param.out_reverse(buf[:len]);
}
union ConvUnion
{
ulong u;
double f;
}
private fn void! etoa(PrintParam* param, FloatType value)
{
// check for NaN and special values
if (value != value || value < FloatType.min || value > FloatType.max)
{
return ftoa(param, value);
}
// determine the sign
bool negative = value < 0;
if (negative) value = -value;
// default precision
if (!param.flags.precision)
{
param.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 (param.flags.adapt_exp)
{
// do we want to fall-back to "%f" mode?
if (value >= 1e-4 && value < 1e6)
{
param.prec = param.prec > expval ? param.prec - expval - 1 : 0;
param.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 (param.prec > 0 && param.flags.precision) param.prec--;
}
}
// Adjust width
uint fwidth = param.width > minwidth ? param.width - minwidth : 0;
// if we're padding on the right, DON'T pad the floating part
if (param.flags.left && minwidth) fwidth = 0;
// rescale the float value
if (expval) value /= conv.f;
// output the floating part
usize start_idx = param.idx;
PrintFlags old = param.flags;
param.flags.adapt_exp = false;
param.width = fwidth;
ftoa(param, negative ? -value : value)?;
param.flags = old;
// output the exponent part
if (minwidth)
{
// output the exponential symbol
param.out(param.flags.uppercase ? 'E' : 'e')?;
// output the exponent value
param.flags = { .zeropad = true, .plus = true };
param.width = minwidth - 1;
param.prec = 0;
ntoa(param, (NtoaType)(expval < 0 ? -expval : expval), expval < 0, 10)?;
param.flags = old;
// might need to right-pad spaces
param.left_adjust(param.idx - start_idx)?;
}
}
private fn FloatType float_from_variant(variant arg)
{
$if (env::I128_SUPPORT):
switch (arg)
{
case int128:
return *arg;
case uint128:
return *arg;
}
$endif;
if (arg.type.kind == 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 char:
return *arg;
case ushort:
return *arg;
case uint:
return *arg;
case ulong:
return *arg;
case float:
return (FloatType)*arg;
case double:
return (FloatType)*arg;
default:
return 0;
}
}
private fn NtoaType int_from_variant(variant arg, bool *is_neg)
{
*is_neg = false;
$if (NtoaType.typeid == uint128.typeid):
switch (arg)
{
case int128:
int128 val = *arg;
return (*is_neg = val < 0) ? -val : val;
case uint128:
return *arg;
}
$endif;
if (arg.type.kind == TypeKind.POINTER)
{
return (NtoaType)(uptr)(void*)arg.ptr;
}
switch (arg)
{
case bool:
return (NtoaType)*arg;
case ichar:
int val = *arg;
return (NtoaType)((*is_neg = val < 0) ? -val : val);
case short:
int val = *arg;
return (NtoaType)((*is_neg = val < 0) ? -val : val);
case int:
int val = *arg;
return (NtoaType)((*is_neg = val < 0) ? -val : val);
case long:
long val = *arg;
return (NtoaType)((*is_neg = val < 0) ? -val : val);
case char:
return *arg;
case ushort:
return *arg;
case uint:
return *arg;
case ulong:
return *arg;
case float:
float f = *arg;
return (NtoaType)((*is_neg = f < 0) ? -f : f);
case double:
double d = *arg;
return (NtoaType)((*is_neg = d < 0) ? -d : d);
default:
return 0;
}
}
fn usize! printf(char[] format, args...) @maydiscard
{
return vsnprintf(&out_putchar_fn, null, format, args);
}
fn usize! String.printf(String* str, char[] format, args...) @maydiscard
{
return vsnprintf(&out_string_append_fn, str, format, args);
}
fn usize! File.printf(File file, char[] format, args...) @maydiscard
{
return vsnprintf(&out_putchar_fn, &file, format, args);
}
private fn void! PrintParam.left_adjust(PrintParam* param, usize len)
{
if (!param.flags.left) return;
for (usize l = len; l < param.width; l++) param.out(' ')?;
}
private fn void! PrintParam.right_adjust(PrintParam* param, usize len)
{
if (param.flags.left) return;
for (usize l = len; l < param.width; l++) param.out(' ')?;
}
private fn void! out_substr(PrintParam* param, char[] str)
{
usize l = conv::utf8_codepoints(str);
uint prec = param.prec;
if (param.flags.precision && l < prec) l = prec;
param.right_adjust(' ')?;
usize index = 0;
usize 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 && param.flags.precision && !prec--) break;
param.out(c)?;
index++;
}
return param.left_adjust(l);
}
private fn usize! vsnprintf(OutputFn out, void* data, char[] format, variant[] variants)
{
if (!out)
{
// use null output function
out = &out_null_fn;
}
PrintParam param = { .outfn = out, .buffer = data };
usize format_len = format.len;
usize variant_index = 0;
for (usize i = 0; i < format_len; i++)
{
// format specifier? %[flags][width][.precision][length]
char c = format[i];
if (c != '%')
{
// no
param.out(c)?;
continue;
}
i++;
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
c = format[i];
if (c == '%')
{
param.out(c)?;
continue;
}
// evaluate flags
param.flags = {};
while FLAG_EVAL: (true)
{
switch (c)
{
case '0': param.flags.zeropad = true;
case '-': param.flags.left = true;
case '+': param.flags.plus = true;
case ' ': param.flags.space = true;
case '#': param.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)
{
param.flags.left = true;
w = -w;
}
param.width = w;
// evaluate precision field
param.prec = 0;
if (c == '.')
{
param.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)?;
param.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;
param.flags.hash = false;
case 'X' :
param.flags.uppercase = true;
nextcase;
case 'x' :
base = 16;
case 'O':
param.flags.uppercase = true;
nextcase;
case 'o' :
base = 8;
case 'B':
param.flags.uppercase = true;
nextcase;
case 'b' :
base = 2;
case 'F' :
param.flags.uppercase = true;
nextcase;
case 'f':
ftoa(&param, float_from_variant(current))?;
continue;
case 'E':
param.flags.uppercase = true;
nextcase;
case 'e':
etoa(&param, float_from_variant(current))?;
continue;
case 'G':
param.flags.uppercase = true;
nextcase;
case 'g':
param.flags.adapt_exp = true;
etoa(&param, float_from_variant(current))?;
continue;
case 'c':
out_char(&param, current)?;
continue;
case 's':
out_str(&param, current)?;
continue;
case 'p':
param.width = (uint)(void*.sizeof * 2);
param.flags.zeropad = true;
param.flags.hash = true;
base = 16;
default:
return PrintFault.INVALID_FORMAT_STRING!;
}
if (base != 10)
{
param.flags.plus = false;
param.flags.space = false;
}
// ignore '0' flag when precision is given
if (param.flags.precision) param.flags.zeropad = false;
bool is_neg;
NtoaType v = int_from_variant(current, &is_neg);
ntoa(&param, v, is_neg, base)?;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return param.idx;
}

View File

@@ -2,11 +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 libc;
import std::cinterop;
import std::env;
import std::os::linux;
import std::os::macos;
import std::os::windows;
// stdlib
@@ -27,142 +22,7 @@ struct LongDivResult
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()
{
@@ -173,7 +33,7 @@ fn Errno errno()
$elif (env::OS_TYPE == OsType.LINUX):
return (Errno)linux::errno();
$else:
return Errno.ENOTRECOVERABLE;
return errno::ENOTRECOVERABLE;
$endif;
}
@@ -256,6 +116,9 @@ $case OsType.WIN32:
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;
// The following needs to be set per arch+os
@@ -271,7 +134,7 @@ const int EOF = -1;
const int FOPEN_MAX = 20;
const int FILENAME_MAX = 1024;
define ErrnoType = CInt;
define Errno = distinct CInt;
define SeekIndex = CLong;
extern fn int fclose(CFile stream);
@@ -352,4 +215,140 @@ extern fn Time time(Time *timer);
// signal
define SignalFunction = fn void(int);
extern fn SignalFunction signal(int sig, SignalFunction function);
// Incomplete
// 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 */
const Errno EAGAIN = 11; /* Try again */
const Errno ENOMEM = 12; /* Out of memory */
const Errno EACCES = 13; /* Permission denied */
const Errno EFAULT = 14; /* Bad address */
const Errno ENOTBLK = 15; /* Block device required */
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 */
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 */
const Errno EDEADLK = 35; /* Resource deadlock would occur */
const Errno ENAMETOOLONG = 36; /* File name too long */
const Errno ENOLCK = 37; /* No record locks available */
const Errno ENOSYS = 38; /* Function not implemented */
const Errno ENOTEMPTY = 39; /* Directory not empty */
const Errno ELOOP = 40; /* Too many symbolic links encountered */
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 EOVERFLOW = 75; /* Value too large for defined data type */
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 ENETDOWN = 100; /* Network is down */
const Errno ENETUNREACH = 101; /* Network is unreachable */
const Errno ENETRESET = 102; /* Network dropped connection because of reset */
const Errno ECONNABORTED = 103; /* Software caused connection abort */
const Errno ECONNRESET = 104; /* Connection reset by peer */
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 */
const Errno EALREADY = 114; /* Operation already in progress */
const Errno EINPROGRESS = 115; /* Operation now in progress */
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 EDQUOT = 122; /* Quota exceeded */
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

@@ -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::array::linkedlist<Type>;
import std::mem;
private struct Node
{
@@ -26,7 +25,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::malloc(Node);
Node *new_node = mem::malloc(Node);
*new_node = { .next = first, .value = value };
list.first = new_node;
if (!first)
@@ -90,7 +89,7 @@ fn Type LinkedList.get(LinkedList* list, usize index)
private fn void LinkedList.linkBefore(LinkedList *list, Node *succ, Type value)
{
Node* pred = succ.prev;
Node* new_node = @mem::malloc(Node);
Node* new_node = mem::malloc(Node);
*new_node = { .prev = pred, .next = succ, .value = value };
succ.prev = new_node;
if (!pred)

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::array::list<Type>;
import std::mem;
struct List
{
@@ -11,7 +10,7 @@ struct List
Type *entries;
}
private fn void List.ensureCapacity(List *list) @inline
private fn void List.ensure_capacity(List *list) @inline
{
if (list.capacity == list.size)
{
@@ -20,21 +19,22 @@ private fn void List.ensureCapacity(List *list) @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.ensureCapacity();
list.ensure_capacity();
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];
}
@@ -42,14 +42,14 @@ fn Type List.pop(List *list)
/**
* @require list.size > 0
*/
fn Type List.popFirst(List *list)
fn Type List.pop_first(List *list)
{
Type value = list.entries[0];
list.removeAt(0);
list.remove_at(0);
return value;
}
fn void List.removeAt(List *list, usize index)
fn void List.remove_at(List *list, usize index)
{
for (usize i = index + 1; i < list.size; i++)
{
@@ -58,14 +58,14 @@ fn void List.removeAt(List *list, usize index)
list.size--;
}
fn void List.pushFront(List *list, Type type) @inline
fn void List.push_front(List *list, Type type) @inline
{
list.insertAt(0, type);
list.insert_at(0, type);
}
fn void List.insertAt(List *list, usize index, Type type)
fn void List.insert_at(List* list, usize index, Type type)
{
list.ensureCapacity();
list.ensure_capacity();
for (usize i = list.size; i > index; i--)
{
list.entries[i] = list.entries[i - 1];
@@ -74,14 +74,14 @@ fn void List.insertAt(List *list, usize index, Type type)
list.entries[index] = type;
}
fn void List.removeLast(List *list)
fn void List.remove_last(List* list)
{
list.size--;
}
fn void List.removeFirst(List *list)
fn void List.remove_first(List *list)
{
list.removeAt(0);
list.remove_at(0);
}
fn Type* List.first(List *list)
@@ -94,9 +94,9 @@ fn Type* List.last(List *list)
return list.size ? &list.entries[list.size - 1] : null;
}
fn bool List.isEmpty(List *list)
fn bool List.is_empty(List *list)
{
return list.size;
return !list.size;
}
fn usize List.len(List *list) @operator(len)
@@ -114,15 +114,20 @@ fn void List.free(List *list)
mem::free(list.entries);
list.capacity = 0;
list.size = 0;
list.entries = null;
}
fn void List.swap(List *list, usize i, usize j)
{
@swap(list.entries[i], list.entries[j]);
}
macro Type List.item_at(List &list, usize index) @operator(elementat)
macro Type List.@item_at(List &list, usize index) @operator(elementat)
{
return list.entries[index];
}
macro Type* List.item_ref(List &list, usize index) @operator(elementref)
macro Type* List.@item_ref(List &list, usize index) @operator(elementref)
{
return &list.entries[index];
}

View File

@@ -67,12 +67,12 @@ const QUAD_MIN_EXP = -16481;
const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34;
*/
macro max(x, y) @autoimport
macro max(x, y) @builtin
{
return x > y ? x : y;
}
macro min(x, y) @autoimport
macro min(x, y) @builtin
{
return x < y ? x : y;
}

391
lib/std/math.matrix.c3 Normal file
View File

@@ -0,0 +1,391 @@
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 {
float[16] m;
struct {
float m00, m01, m02, m03;
float m10, m11, m12, m13;
float m20, m21, m22, m23;
float m30, m31, m32, m33;
}
}
}
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 { .m = {
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 { .m = {
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 { .m = {
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 { .m = {
mat.m00 * s, mat.m01 * s,
mat.m10 * s, mat.m11 * s,
} };
}
fn Matrix3x3 Matrix3x3.component_mul(Matrix3x3* mat, float s) {
return Matrix3x3 { .m = {
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 { .m = {
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 { .m = { mat.m00, mat.m10, mat.m01, mat.m11 } };
}
fn Matrix3x3 Matrix3x3.transpose(Matrix3x3* mat) {
return Matrix3x3 { .m = {
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 { .m = {
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 { .m = { mat.m00, -mat.m01, -mat.m10, mat.m11 } };
}
fn Matrix3x3 Matrix3x3.adjoint(Matrix3x3* mat) {
return Matrix3x3 { .m = {
(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 { .m = {
(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 { .m = {
1.f, 0.f, v[0],
0.f, 1.f, v[1],
0.f, 0.f, 1.f,
} });
}
fn Matrix4x4 Matrix4x4.translate(Matrix4x4* m, float[<3>] v) {
return m.mul(Matrix4x4 { .m = {
1.f, 0.f, 0.f, v[0],
0.f, 1.f, 0.f, v[1],
0.f, 0.f, 1.f, v[2],
0.f, 0.f, 0.f, 1.f,
} });
}
// r in radians
fn Matrix3x3 Matrix3x3.rotate(Matrix3x3* m, float r) {
return m.mul(Matrix3x3 { .m = {
(float)math::cos(r), (float)-math::sin(r), 0.f,
(float)math::sin(r), (float) math::cos(r), 0.f,
0.f, 0.f, 1.f,
} });
}
// r in radians
fn Matrix4x4 Matrix4x4.rotateZ(Matrix4x4* m, float r) {
return m.mul(Matrix4x4 { .m = {
(float)math::cos(r), (float)-math::sin(r), 0.f, 0.f,
(float)math::sin(r), (float) math::cos(r), 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, 0.f, 1.f,
} });
}
// r in radians
fn Matrix4x4 Matrix4x4.rotateY(Matrix4x4* m, float r) {
return m.mul(Matrix4x4 { .m = {
(float)math::cos(r), 0.f, (float)-math::sin(r), 0.f,
0.f, 1.f, 0.f, 0.f,
(float)math::sin(r), 0.f, (float) math::cos(r), 0.f,
0.f, 0.f, 0.f, 1.f,
} });
}
// r in radians
fn Matrix4x4 Matrix4x4.rotateX(Matrix4x4* m, float r) {
return m.mul(Matrix4x4 { .m = {
1.f, 0.f, 0.f, 0.f,
0.f, (float)math::cos(r), (float)-math::sin(r), 0.f,
0.f, (float)math::sin(r), (float) math::cos(r), 0.f,
0.f, 0.f, 0.f, 1.f,
} });
}
fn Matrix3x3 Matrix3x3.scale(Matrix3x3* m, float[<2>] v) {
return m.mul(Matrix3x3 { .m = {
v[0], 0.f, 0.f,
0.f, v[1], 0.f,
0.f, 0.f, 1.f,
} });
}
fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, float[<3>] v) {
return m.mul(Matrix4x4 { .m = {
v[0], 0.f, 0.f, 0.f,
0.f, v[1], 0.f, 0.f,
0.f, 0.f, v[2], 0.f,
0.f, 0.f, 0.f, 1.f,
} });
}
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 {
.m = {
2.f / width, 0.f, 0.f, 0.f,
0.f, 2.f / height, 0.f, 0.f,
0.f, 0.f, -2.f / depth, 0.f,
-(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1.f,
}
};
}
// 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 {
.m = {
1.f / right, 0.f, 0.f, 0.f,
0.f, 1.f / top, 0.f, 0.f,
0.f, 0.f, -2.f / depth, 0.f,
0.f, 0.f, -(far + near) / depth, 1.f,
}
};
}

View File

@@ -1,280 +0,0 @@
module std::mem;
define AllocatorFunction = fn void*!(void *data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind);
const DEFAULT_MEM_ALIGNMENT = $alignof(void*) * 2;
Allocator main_allocator = { SYSTEM_ALLOCATOR, null };
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)?;
}
struct ArenaAllocator
{
void* memory;
void* last_ptr;
usize total;
usize used;
}
macro void*! allocator_to_function($Type, void* data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind)
{
$Type* allocator = data;
switch (kind)
{
case ALLOC:
return allocator.alloc(new_size, alignment) @inline;
case CALLOC:
return allocator.calloc(new_size, alignment) @inline;
case REALLOC:
return allocator.realloc(old_pointer, new_size, alignment) @inline;
case FREE:
allocator.free(old_pointer) @inline?;
return null;
}
@unreachable();
}
fn void*! arena_allocator_function(void* allocator, usize new_size, usize alignment, void* old_pointer, AllocationKind kind)
{
return @allocator_to_function(ArenaAllocator, allocator, new_size, alignment, old_pointer, kind);
}
fn Allocator ArenaAllocator.to_allocator(ArenaAllocator* allocator) @inline
{
return { &arena_allocator_function, allocator };
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! ArenaAllocator.alloc(ArenaAllocator* allocator, usize bytes, usize alignment = 0)
{
if (!bytes) return null;
if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT;
iptr next = aligned_offset((iptr)allocator.memory + allocator.used, alignment);
usize next_after = next - (iptr)allocator.memory + bytes;
if (next_after > allocator.total) return AllocationFailure.OUT_OF_MEMORY!;
allocator.used = next_after;
return allocator.last_ptr = (void*)next;
}
fn void*! ArenaAllocator.calloc(ArenaAllocator* allocator, usize bytes, usize alignment = 0)
{
char* bits = allocator.alloc(bytes) @inline?;
mem::set(bits, 0, bytes);
return bits;
}
/**
* @require ptr != null
* @require allocator != null
**/
fn void*! ArenaAllocator.realloc(ArenaAllocator* allocator, void *ptr, usize bytes, usize alignment = 0)
{
if (!ptr) return allocator.alloc(bytes, alignment);
if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT;
// Is last allocation and alignment matches?
if (allocator.last_ptr == ptr && ptr_is_aligned(ptr, alignment))
{
usize new_used = (usize)(ptr - allocator.memory) + bytes;
if (new_used > allocator.total) return AllocationFailure.OUT_OF_MEMORY!;
allocator.used = new_used;
return ptr;
}
// Otherwise just allocate new memory.
void* new_mem = allocator.alloc(bytes, alignment)?;
// And copy too much probably!
copy(new_mem, ptr, (new_mem - ptr) > bytes ? bytes : (usize)(new_mem - ptr));
return new_mem;
}
fn void! ArenaAllocator.free(ArenaAllocator* allocator, void* ptr)
{
if (!ptr) return;
if (ptr == allocator.last_ptr)
{
allocator.used = (usize)(ptr - allocator.memory);
allocator.last_ptr = null;
return;
}
}
fn void! ArenaAllocator.init(ArenaAllocator* allocator, usize arena_size)
{
allocator.memory = alloc_checked(arena_size)?;
allocator.total = arena_size;
allocator.used = 0;
allocator.last_ptr = null;
}
fn void ArenaAllocator.reset(ArenaAllocator* allocator)
{
allocator.used = 0;
allocator.last_ptr = null;
}
fn void ArenaAllocator.destroy(ArenaAllocator* allocator)
{
assert(allocator.memory);
free(allocator.memory);
allocator.total = allocator.used = 0;
}
private struct DynamicArenaPage
{
void* memory;
void* prev_arena;
usize total;
usize used;
}
struct DynamicArenaAllocator
{
DynamicArenaPage* page;
usize total;
usize used;
usize page_size;
Allocator allocator;
}
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator allocator = { null, null })
{
this.page = null;
this.used = this.total = 0;
this.page_size = page_size;
this.allocator = allocator.function ? allocator : thread_allocator;
}
fn void! DynamicArenaAllocator.reset(DynamicArenaAllocator* this)
{
DynamicArenaPage* page = this.page;
Allocator allocator = this.allocator;
while (page && page.prev_arena)
{
DynamicArenaPage* next_page = page.prev_arena;
void* mem = page.memory;
allocator.free(page)?;
allocator.free(mem)?;
page = next_page;
}
this.page = page;
}
fn void*! dynamic_arena_allocator_function(void* allocator, usize new_size, usize alignment, void* old_pointer, AllocationKind kind)
{
return @allocator_to_function(DynamicArenaAllocator, allocator, new_size, alignment, old_pointer, kind);
}
fn Allocator DynamicArenaAllocator.to_allocator(DynamicArenaAllocator* this)
{
return { &dynamic_arena_allocator_function, this };
}
fn void! DynamicArenaAllocator.destroy(DynamicArenaAllocator* this)
{
this.reset();
DynamicArenaPage* first_page = this.page;
if (!first_page) return;
void* mem = first_page.memory;
this.allocator.free(this.page)?;
this.page = null;
this.allocator.free(mem)?;
}
fn void! DynamicArenaAllocator.free(DynamicArenaAllocator* allocator, void* ptr)
{
// This can be made smarter.
return;
}
/**
* @require @math::is_power_of_2(alignment)
*/
private fn void*! DynamicArenaAllocator.alloc_new(DynamicArenaAllocator* this, usize size, usize alignment)
{
usize page_size = @max(this.page_size, size);
void* mem = this.allocator.alloc(page_size, alignment)?;
DynamicArenaPage*! page = this.allocator.alloc(DynamicArenaPage.sizeof);
if (catch err = page)
{
this.allocator.free(mem);
return err!;
}
page.memory = mem;
page.prev_arena = this.page;
page.total = page_size;
page.used = size;
this.page = page;
return page.memory;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! DynamicArenaAllocator.calloc(DynamicArenaAllocator* allocator, usize size, usize alignment = 0)
{
void* mem = allocator.alloc(size, alignment)?;
set(mem, 0, size);
return mem;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! DynamicArenaAllocator.realloc(DynamicArenaAllocator* allocator, void* ptr, usize size, usize alignment = 0)
{
void* mem = allocator.alloc(size, alignment)?;
copy(mem, ptr, size);
return mem;
}
/**
* @require !alignment || @math::is_power_of_2(alignment)
*/
fn void*! DynamicArenaAllocator.alloc(DynamicArenaAllocator* this, usize size, usize alignment)
{
DynamicArenaPage *page = this.page;
if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT;
if (!page) return this.alloc_new(size, alignment);
usize start = aligned_offset((uptr)page.memory + page.used, alignment) - (usize)page.memory;
usize new_used = start + size;
if (new_used > page.total) return this.alloc_new(size, alignment);
page.used = new_used;
return page.memory + start;
}

105
lib/std/priorityqueue.c3 Normal file
View File

@@ -0,0 +1,105 @@
// 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);
usize i = pq.heap.len() - 1;
while (i > 0)
{
usize 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)
{
usize i = 0;
usize len = pq.heap.len() @inline;
if (!len) return IteratorResult.NO_MORE_ELEMENT!;
usize newCount = len - 1;
pq.heap.swap(0, newCount);
while ((2 * i + 1) < newCount)
{
usize 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 usize PriorityQueue.len(PriorityQueue* pq) @operator(len)
{
return pq.heap.len();
}

View File

@@ -1,59 +0,0 @@
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

@@ -0,0 +1,25 @@
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((usize)(bits - i - 1), x & 1 ? '1' : '0');
x >>= 1;
}
return str;
}

View File

@@ -1,22 +1,28 @@
module test;
import libc;
import std::io;
struct Doc { Head *head; }
struct Head { char[]* title; }
struct Head { String* title; }
struct Summary
{
char[]* title;
String* title;
bool ok;
}
fn void Summary.print(Summary *s, CFile out)
private struct StringData
{
// We don't have a native printf in C3 yet, so use libc,
// which is not all that nice for the strings but...
char[] title = s.title ? *s.title : "missing";
libc::fprintf(out, "Summary({ .title = %.*s, .ok = %s})", (int)title.len, title.ptr, s.ok ? "true" : "false");
Allocator allocator;
usize len;
usize 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)
@@ -36,7 +42,7 @@ fn bool contains(char[] haystack, char[] needle)
return false;
}
macro dupe(value)
macro @dupe(value)
{
$typeof(&value) temp = mem::alloc_checked($sizeof(value))?;
*temp = value;
@@ -52,13 +58,11 @@ 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((char[])"")? })? };
// Not particularly elegant due to missing string functions.
int len = libc::snprintf(null, 0, "Title of %.*s", (int)url.len, url.ptr);
char* str = mem::alloc_checked(len + 1)?;
libc::snprintf(str, len + 1, "Title of %.*s", (int)url.len, url.ptr);
return { @dupe(Head { .title = @dupe(str[..len - 1])? })? };
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)
@@ -93,9 +97,9 @@ fault TitleResult
fn bool! isTitleNonEmpty(Doc doc)
{
if (!doc.head) return TitleResult.TITLE_MISSING!;
char[]* head = doc.head.title;
String* head = doc.head.title;
if (!head) return TitleResult.TITLE_MISSING!;
return (*head).len > 0;
return head.len() > 0;
}
@@ -104,49 +108,34 @@ fn bool! readWhetherTitleNonEmpty(char[] url)
return isTitleNonEmpty(readDoc(url));
}
fn char* bool_to_string(bool b)
fn char[] bool_to_string(bool b)
{
return b ? "true" : "false";
}
fn char* nameFromError(anyerr e)
{
switch (e)
{
case TitleResult.TITLE_MISSING:
return "no title";
case ReadError.BAD_READ:
return "bad read";
case AllocationFailure.OUT_OF_MEMORY:
return "out of memory";
default:
return "unknown error";
}
}
fn void main()
{
const char[][] URLS = { "good", "title-empty", "title-missing", "head-missing", "fail" };
DynamicArenaAllocator allocator;
allocator.init(1024);
DynamicArenaAllocator dynamic_arena;
dynamic_arena.init(1024);
foreach (char[] url : URLS)
{
@mem::with_allocator(allocator.to_allocator())
mem::@with_allocator(&dynamic_arena)
{
// Yes, it's pretty onerous to print strings for the moment in C3
libc::printf(`Checking "https://%.*s/":` "\n", (int)url.len, url.ptr);
io::printf(`Checking "https://%s/":` "\n", url);
Summary summary = readAndBuildSummary(url);
libc::printf(" Summary: ");
summary.print(@libc::stdout());
libc::printf("\n");
char[] title_sure = summary.title ? *summary.title : "";
libc::printf(" Title: %.*s\n", (int)title_sure.len, title_sure.ptr);
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.
libc::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? nameFromError(catch(has_title)), (has_title ?? false) ? "true" : "false");
io::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? catch(has_title).nameof, has_title ?? false);
};
allocator.reset();
dynamic_arena.reset();
}
allocator.destroy();
dynamic_arena.destroy();
}

View File

@@ -0,0 +1,28 @@
module foo;
import std::io;
tlocal char[] context_user = "safe";
macro long perform(task)
{
io::printf("%s: %s\n", context_user, task);
return task.len;
}
macro @with_mode(char[] user, #action, arg)
{
@scope(context_user)
{
context_user = user;
return #action(arg);
};
}
fn void main()
{
long result = perform("something!");
result += @with_mode("faster", perform, "reliable");
result += perform(char[][] {"again", "more"});
result += perform(int[<2>] { 56, 99 });
io::printf("Result: %d\n", result);
}

View File

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

View File

@@ -0,0 +1,27 @@
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,18 +1,14 @@
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::make(int, n);
int* perm1 = @array::make(int, n);
int* count = @array::make(int, n);
int* perm = array::alloc(int, n);
int* perm1 = array::alloc(int, n);
int* count = array::alloc(int, n);
int max_flips_count;
int perm_count;
int checksum;
@@ -42,7 +38,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 */
@@ -50,7 +46,7 @@ fn int fannkuchredux(int n)
{
if (r == n)
{
libc::printf("%d\n", checksum);
io::printf("%d\n", checksum);
return max_flips_count;
}
@@ -75,6 +71,6 @@ fn int fannkuchredux(int n)
fn int main(int argc, char** argv)
{
int n = argc > 1 ? libc::atoi(argv[1]) : 7;
libc::printf("Pfannkuchen(%d) = %d\n", n, fannkuchredux(n));
io::printf("Pfannkuchen(%d) = %d\n", n, fannkuchredux(n));
return 0;
}

View File

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

View File

@@ -1,11 +1,7 @@
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);
@@ -20,18 +16,18 @@ struct GameBoard
fn void GameBoard.show(GameBoard *board)
{
printf("\e[H");
io::printf("\e[H");
char* current = board.world;
for (int y = 0; y < board.h; y++)
{
for (int x = 0; x < board.w; x++)
{
printf(*current ? "\e[07m \e[m" : " ");
io::printf(*current ? "\e[07m \e[m" : " ");
current++;
}
printf("\e[E");
io::printf("\e[E");
}
fflush(__stdoutp);
libc::fflush(libc::stdout());
}
fn void GameBoard.evolve(GameBoard *board)
@@ -65,20 +61,20 @@ fn int main(int c, char** v)
{
int w = 0;
int h = 0;
if (c > 1) w = atoi(v[1]);
if (c > 2) h = atoi(v[2]);
if (c > 1) w = libc::atoi(v[1]);
if (c > 2) h = libc::atoi(v[2]);
if (w <= 0) w = 30;
if (h <= 0) h = 30;
GameBoard board;
board.w = w;
board.h = h;
board.world = malloc((ulong)(h * w));
board.temp = malloc((ulong)(h * w));
board.world = mem::alloc((ulong)(h * w));
board.temp = mem::alloc((ulong)(h * w));
for (int i = h * w - 1; i >= 0; i--)
{
board.world[i] = rand() % 10 == 0 ? 1 : 0;
board.world[i] = libc::rand() % 10 == 0 ? 1 : 0;
}
for (int j = 0; j < 1000; j++)
{

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,21 +0,0 @@
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

@@ -1,9 +1,14 @@
module map(Key, Type);
module std::container::map <Key, Type>;
fault MapResult
{
KEY_NOT_FOUND
}
public struct Entry
{
Key key;
Type* value;
Type value;
usize hash;
Entry* next;
}
@@ -13,34 +18,37 @@ public struct Map
usize size;
void* map;
uint mod;
Allocator allocator;
}
public fn Map* Map.init(Map *map)
/**
* @require map != null
**/
public fn void Map.init(Map *map, Allocator allocator)
{
*map = { };
return map;
map.allocator = allocator;
}
public fn Type* Map.valueForKey(Map *map, Key key)
public fn Type! Map.valueForKey(Map *map, Key key)
{
if (!map.map) return nil;
if (!map.map) return null;
usize hash = key.hash();
usize pos = hash & map.mod;
Entry* entry = &map.map[pop];
if () return nil;
if (!entry) return MapResult.KEY_NOT_FOUND!;
while (entry)
{
if (entry.hash == hash && entry.key == key) return entry.value;
entry = entry.next;
}
return nil;
return MapResult.KEY_NOT_FOUND!;
}
public fn Type *Map.setValueForKey(Map *map, Key key, Type *value)
public fn Type *Map.set(Map *map, Key key, Type value)
{
if (!map.map)
{
map.map = @calloc(Entry, 16);
map.map = allocator.calloc(Entry, 16);
map.mod = 0x0F;
}
@@ -66,42 +74,14 @@ public fn Type *Map.setValueForKey(Map *map, Key key, Type *value)
{
entry = entry.next;
}
entry.next = @malloc(Entry);
entry.next = allocator.alloc(Entry);
entry = entry.next;
}
}
public fn usize Map.size(Vector *vector)
public fn usize Map.size(Map* map)
{
return vector.array.size;
return map.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

@@ -1,19 +0,0 @@
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

@@ -1,21 +0,0 @@
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

@@ -1,13 +1,12 @@
module test;
import std::time;
import std::io;
public macro timeit(#call)
{
Time t = time::current();
typeof(#call) result = #call;
TimeDiff diff = time::current() - t;
io::printf("'%s' took %f ms\n", $stringify(#call), diff * 1000);
libc::printf("'%s' took %f ms\n", $stringify(#call), diff * 1000);
return result;
}

View File

@@ -0,0 +1,313 @@
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

@@ -0,0 +1,251 @@
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

@@ -0,0 +1,793 @@
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

@@ -0,0 +1,44 @@
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::make(double, n);
double[] u = @array::make(double, n);
double[] v = @array::make(double, n);
temparr = array::alloc(double, n);
double[] u = array::alloc(double, n);
double[] v = array::alloc(double, n);
foreach(&uval : u) *uval = 1;
for (int i = 0; i < 10; i++)
{

View File

@@ -0,0 +1,20 @@
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

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

View File

@@ -0,0 +1,792 @@
module tetris;
import raylib;
/**
* 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
check_detection(&detection);
// 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 void check_detection(bool *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+1] == GridSquare.FULL) || (grid[i][j+1] == GridSquare.BLOCK))) *detection_ref = true;
}
}
}
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

@@ -82,5 +82,5 @@ fn void main()
String w;
w.init("Yo man!");
s.concat(&w);
io::printf("Message was: %s\n", s.zstr());
libc::printf("Message was: %s\n", s.zstr());
}

View File

@@ -25,15 +25,15 @@ struct TopoList
fn void sort(InputPair[] pairs, uint elements)
{
InputPair[] result = @array::make(InputPair, pairs.len);
TopoList* top = @array::make(TopoList, elements);
InputPair[] result = array::alloc(InputPair, pairs.len);
TopoList* top = array::alloc(TopoList, elements);
for (int i = 0; i < pairs.len; i++)
{
InputPair pair = pairs[i];
assert(pair.value >= 0 && pair.value < elements);
assert(pair.successor >= 0 && pair.successor < elements);
top[pair.successor].count++;
Entry* successor_entry = @mem::malloc(Entry);
Entry* successor_entry = mem::malloc(Entry);
*successor_entry = { pair.successor, null };
Entry** next_ref = &top[pair.value].next;
while (*next_ref)
@@ -42,7 +42,7 @@ fn void sort(InputPair[] pairs, uint elements)
}
*next_ref = successor_entry;
}
int[] intout = @array::make(int, elements);
int[] intout = array::alloc(int, elements);
int count = 0;
while LOOP: (1)
{

View File

@@ -1,7 +1,14 @@
module hello_world;
import std;
import bar;
$if (env::OS_TYPE == OsType.WIN32):
fn int test_doubler(int x)
{
return x * x;
}
$else:
extern fn int test_doubler(int);
$endif;
extern fn void printf(char *, ...);
fn int main()
@@ -9,5 +16,5 @@ fn int main()
printf("Hello World!\n");
bar::test();
printf("Hello double: %d\n", test_doubler(11));
return 1;
return 17;
}

View File

@@ -16,12 +16,16 @@
// c compiler
"cc": "cc",
// c sources
"csources": [
"./csource/**"
],
"targets": {
"hello_world": {
"type": "executable",
"csources": [
"./csource/**"
]
},
"hello_world_win32": {
"type": "executable",
}
},
}

View File

@@ -1,14 +0,0 @@
[[executable]]
name = "hello_world"
version = "0.1.0"
authors = ["John Doe <john.doe@example.com>"]
langrev = "1"
warnings = ["no-unused"]
# sources compiled
sources = ["./**"]
# libraries to use
libs = []
# c compiler
cc = "cc"
# c sources
csources = ["./csource/**"]

View File

@@ -14,6 +14,7 @@
#include <utils/lib.h>
#include "../utils/whereami.h"
extern int llvm_version_major;
static int arg_index;
static int arg_count;
@@ -23,30 +24,30 @@ extern const char* llvm_version;
extern const char* llvm_target;
char *arch_os_target[ARCH_OS_TARGET_LAST + 1] = {
[X86_FREEBSD] = "x86-freebsd",
[X86_OPENBSD] = "x86-openbsd",
[X86_NETBSD] = "x86-netbsd",
[X86_MCU] = "x86-mcu",
[X86_ELF] = "x86-elf",
[X86_WINDOWS] = "x86-windows",
[X86_LINUX] = "x86-linux",
[X64_DARWIN] = "x64-darwin",
[X64_LINUX] = "x64-linux",
[X64_WINDOWS] = "x64-windows",
[X64_WINDOWS_GNU] = "x64-mingw",
[X64_FREEBSD] = "x64-freebsd",
[X64_OPENBSD] = "x64-openbsd",
[X64_NETBSD] = "x64-netbsd",
[X64_ELF] = "x64-elf",
[AARCH64_LINUX] = "aarch64-linux",
[AARCH64_DARWIN] = "aarch64-darwin",
[AARCH64_ELF] = "aarch64-elf",
[RISCV32_LINUX] = "riscv32-linux",
[RISCV32_ELF] = "riscv32-elf",
[RISCV64_LINUX] = "riscv64-linux",
[RISCV64_ELF] = "riscv64-elf",
[WINDOWS_X86] = "windows-x86",
[WINDOWS_X64] = "windows-x64",
[MINGW_X64] = "mingw-x64",
[MACOS_X64] = "macos-x64",
[MACOS_AARCH64] = "macos-aarch64",
[LINUX_X86] = "linux-x86",
[LINUX_X64] = "linux-x64",
[LINUX_AARCH64] = "linux-aarch64",
[LINUX_RISCV32] = "linux-riscv32",
[LINUX_RISCV64] = "linux-riscv64",
[WASM32] = "wasm32",
[WASM64] = "wasm64",
[ELF_X86] = "elf-x86",
[ELF_X64] = "elf-x64",
[ELF_AARCH64] = "elf-aarch64",
[ELF_RISCV32] = "elf-riscv32",
[ELF_RISCV64] = "elf-riscv64",
[FREEBSD_X86] = "freebsd-x86",
[FREEBSD_X64] = "freebsd-x64",
[OPENBSD_X86] = "openbsd-x86",
[OPENBSD_X64] = "openbsd-x64",
[NETBSD_X86] = "netbsd-x86",
[NETBSD_X64] = "netbsd-x64",
[MCU_X86] = "mcu-x86",
};
#define EOUTPUT(string, ...) fprintf(stderr, string "\n", ##__VA_ARGS__)
@@ -74,7 +75,8 @@ static void usage(void)
OUTPUT("Options:");
OUTPUT(" --tinybackend - Use the TinyBackend for compilation.");
OUTPUT(" --stdlib <dir> - Use this directory as the C3 standard library path.");
OUTPUT(" --lib <dir> - Use this directory as the C3 library path.");
OUTPUT(" --libdir <dir> - Add this directory to the C3 library search paths.");
OUTPUT(" --lib <name> - Add this library to the compilation.");
OUTPUT(" --path <dir> - Use this as the base directory for the current command.");
OUTPUT(" --template <template> - Use a different template: \"lib\", \"static-lib\" or a path.");
OUTPUT(" --about - Prints a short description of C3.");
@@ -89,6 +91,9 @@ static void usage(void)
OUTPUT(" -O2 - Default optimization level.");
OUTPUT(" -Os - Optimize for size.");
OUTPUT(" -O3 - Aggressive optimization.");
OUTPUT(" --build-dir <dir> - Override build output directory.");
OUTPUT(" --obj-out <dir> - Override object file output directory.");
OUTPUT(" --llvm-out <dir> - Override llvm output directory for '--emit-llvm'.");
OUTPUT(" --emit-llvm - Emit LLVM IR as a .ll file per module.");
OUTPUT(" --target <target> - Compile for a particular architecture + OS target.");
OUTPUT(" --threads <number> - Set the number of threads to use for compilation.");
@@ -100,7 +105,10 @@ static void usage(void)
OUTPUT(" -gline-tables-only - Only emit line tables for debugging.");
OUTPUT("");
OUTPUT("");
OUTPUT(" -l <library> - Link with the library provided.");
OUTPUT(" -L <library dir> - Append the directory to the linker search paths.");
OUTPUT(" -z <argument> - Send the <argument> as a parameter to the linker.");
OUTPUT(" --forcelinker - Force linker usage over using when doing non-cross linking.");
OUTPUT("");
OUTPUT(" --reloc=<option> - Relocation model: none, pic, PIC, pie, PIE");
OUTPUT(" --x86vec=<option> - Set max level of vector instructions: none, mmx, sse, avx, avx512.");
@@ -112,6 +120,12 @@ static void usage(void)
OUTPUT(" --list-attributes - List all attributes.");
OUTPUT(" --list-builtins - List all builtins.");
OUTPUT(" --list-precedence - List operator precedence order.");
OUTPUT("");
OUTPUT(" --winsdk <dir> - Set the directory for Windows system library files for cross compilation.");
OUTPUT(" --wincrt=<option> - Windows CRT linking: none, static, dynamic (default).");
OUTPUT("");
OUTPUT(" --macossdk <dir> - Set the directory for the MacOS SDK for cross compilation.");
#ifndef NDEBUG
OUTPUT(" --debug-log - Print debug logging to stdout.");
#endif
@@ -126,22 +140,11 @@ static const char* check_dir(const char *path)
{
original_path = getcwd(NULL, 0);
}
if (chdir(path) == -1) error_exit("The path \"%s\" does not point to a valid directory.", path);
int err = chdir(original_path);
if (err) FAIL_WITH_ERR("Failed to change path to %s.", original_path);
if (!dir_change(path)) error_exit("The path \"%s\" does not point to a valid directory.", path);
if (!dir_change(original_path)) FAIL_WITH_ERR("Failed to change path to %s.", original_path);
return path;
}
static const char* check_file(const char *file_path)
{
FILE *file = fopen(file_path, "rb");
if (file == NULL)
{
error_exit("Could not open file \"%s\".\n", file_path);
}
return file_path;
}
static inline bool at_end()
{
return arg_index == arg_count - 1;
@@ -300,10 +303,19 @@ static void print_version(void)
OUTPUT("LLVM default target: %s", llvm_target);
}
static void add_linker_arg(BuildOptions *options, const char *arg)
{
if (options->linker_arg_count == MAX_LIB_DIRS)
{
error_exit("Too many linker arguments are given, more than %d\n", MAX_LIB_DIRS);
}
options->linker_args[options->linker_arg_count++] = arg;
}
static int parse_multi_option(const char *start, unsigned count, const char** elements)
{
const char *arg = current_arg;
int select = str_in_list(start, count, elements);
int select = str_findlist(start, count, elements);
if (select < 0) error_exit("error: %.*s invalid option '%s' given.", (int)(start - arg), start, arg);
return select;
}
@@ -348,7 +360,7 @@ static void parse_option(BuildOptions *options)
break;
case 'z':
if (at_end()) error_exit("error: -z needs a value");
options->linker_args[options->linker_arg_count++] = next_arg();
add_linker_arg(options, next_arg());
return;
case 'o':
if (at_end()) error_exit("error: -o needs a name");
@@ -395,6 +407,28 @@ static void parse_option(BuildOptions *options)
}
options->compile_option = COMPILE_LEX_ONLY;
return;
case 'L':
if (at_end() || next_is_opt()) error_exit("error: -L needs a directory.");
add_linker_arg(options, "-L");
add_linker_arg(options, check_dir(next_arg()));
return;
case 'l':
{
if (at_end() || next_is_opt()) error_exit("error: -l needs a library name.");
const char *lib = next_arg();
const char *framework = str_remove_suffix(lib, ".framework");
if (framework)
{
add_linker_arg(options, "-framework");
add_linker_arg(options, framework);
}
else
{
add_linker_arg(options, "-l");
add_linker_arg(options, lib);
}
return;
}
case 'P':
if (options->compile_option != COMPILE_NORMAL)
{
@@ -428,6 +462,18 @@ static void parse_option(BuildOptions *options)
options->symtab_size = next_highest_power_of_2(symtab);
return;
}
if (match_longopt("forcelinker"))
{
if (llvm_version_major > 12)
{
options->force_linker = true;
}
else
{
printf("Force linking ignored on LLVM 12 and earlier.\n");
}
return;
}
if (match_longopt("version"))
{
print_version();
@@ -529,6 +575,12 @@ static void parse_option(BuildOptions *options)
options->emit_llvm = true;
return;
}
if (match_longopt("cc"))
{
if (at_end() || next_is_opt()) error_exit("error: --cc needs a compiler name.");
options->cc = next_arg();
return;
}
if (match_longopt("stdlib"))
{
if (at_end() || next_is_opt()) error_exit("error: --stdlib needs a directory.");
@@ -546,11 +598,59 @@ static void parse_option(BuildOptions *options)
options->panicfn = next_arg();
return;
}
if (match_longopt("macossdk"))
{
if (at_end() || next_is_opt()) error_exit("error: --macossdk needs a directory.");
options->macos.sdk = check_dir(next_arg());
return;
}
if (match_longopt("winsdk"))
{
if (at_end() || next_is_opt()) error_exit("error: --winsdk needs a directory.");
options->win.sdk = check_dir(next_arg());
return;
}
if ((argopt = match_argopt("wincrt")))
{
options->win.crt_linking = (WinCrtLinking)parse_multi_option(argopt, 3, wincrt_linking);
return;
}
if (match_longopt("build-dir"))
{
if (at_end() || next_is_opt()) error_exit("error: --build-dir needs a directory.");
options->build_dir = next_arg();
return;
}
if (match_longopt("obj-out"))
{
if (at_end() || next_is_opt()) error_exit("error: --obj-out needs a directory.");
options->obj_out = next_arg();
return;
}
if (match_longopt("llvm-out"))
{
if (at_end() || next_is_opt()) error_exit("error: --llvm-out needs a directory.");
options->llvm_out = next_arg();
return;
}
if (match_longopt("lib"))
{
if (at_end() || next_is_opt()) error_exit("error: --lib needs a directory.");
if (options->lib_count == MAX_LIB_DIRS) error_exit("Max %d libraries may be specified.", MAX_LIB_DIRS);
options->lib_dir[options->lib_count++] = check_dir(next_arg());
if (at_end() || next_is_opt()) error_exit("error: --lib needs a name.");
const char *name = next_arg();
if (!str_is_valid_lowercase_name(name))
{
char *name_copy = strdup(name);
str_ellide_in_place(name_copy, 32);
error_exit("Invalid library name '%s', it should be something like 'foo_lib'.", name_copy);
}
options->libs[options->lib_count++] = name;
return;
}
if (match_longopt("libdir"))
{
if (at_end() || next_is_opt()) error_exit("error: --libdir needs a directory.");
if (options->lib_dir_count == MAX_LIB_DIRS) error_exit("Max %d library directories may be specified.", MAX_LIB_DIRS);
options->lib_dir[options->lib_dir_count++] = check_dir(next_arg());
return;
}
if (match_longopt("test"))
@@ -611,7 +711,10 @@ BuildOptions parse_arguments(int argc, const char *argv[])
.reloc_model = RELOC_DEFAULT,
.backend = BACKEND_LLVM,
.x86_vector_capability = X86VECTOR_DEFAULT,
.files = NULL
.win.crt_linking = WIN_CRT_DEFAULT,
.files = NULL,
.build_dir = NULL,
};
for (int i = DIAG_NONE; i < DIAG_WARNING_TYPE; i++)
{

View File

@@ -11,7 +11,6 @@
#define MAX_FILES 2048
#define MAX_THREADS 0xFFFF
#define TB_BACKEND 0
typedef enum
{
@@ -144,6 +143,20 @@ static const char *vector_capability[5] = {
[X86VECTOR_AVX512] = "avx512",
};
typedef enum
{
WIN_CRT_DEFAULT = -1,
WIN_CRT_NONE = 0,
WIN_CRT_DYNAMIC = 1,
WIN_CRT_STATIC = 2,
} WinCrtLinking;
static const char *wincrt_linking[3] = {
[WIN_CRT_NONE] = "none",
[WIN_CRT_DYNAMIC] = "dynamic",
[WIN_CRT_STATIC] = "static",
};
typedef enum
{
RELOC_DEFAULT = -1,
@@ -173,40 +186,53 @@ typedef enum
typedef enum
{
ARCH_OS_TARGET_DEFAULT = 0,
X86_FREEBSD,
X86_OPENBSD,
X86_NETBSD,
X86_LINUX,
X86_WINDOWS,
X86_MCU,
X86_ELF,
X64_DARWIN,
X64_LINUX,
X64_NETBSD,
X64_FREEBSD,
X64_OPENBSD,
X64_WINDOWS,
X64_WINDOWS_GNU,
X64_ELF,
AARCH64_LINUX,
AARCH64_DARWIN,
AARCH64_ELF,
RISCV32_LINUX,
RISCV32_ELF,
RISCV64_LINUX,
RISCV64_ELF,
LINUX_X86,
LINUX_X64,
WINDOWS_X86,
WINDOWS_X64,
MINGW_X64,
MACOS_X64,
MACOS_AARCH64,
LINUX_AARCH64,
LINUX_RISCV32,
LINUX_RISCV64,
WASM32,
WASM64,
ARCH_OS_TARGET_LAST = WASM64
ELF_X86,
ELF_X64,
ELF_AARCH64,
ELF_RISCV32,
ELF_RISCV64,
FREEBSD_X86,
FREEBSD_X64,
OPENBSD_X86,
OPENBSD_X64,
NETBSD_X86,
NETBSD_X64,
MCU_X86,
ARCH_OS_TARGET_LAST = MCU_X86
} ArchOsTarget;
typedef struct BuildOptions_
{
const char* lib_dir[MAX_LIB_DIRS];
const char* linker_args[MAX_LIB_DIRS];
const char* std_lib_dir;
const char *lib_dir[MAX_LIB_DIRS];
int lib_dir_count;
const char *libs[MAX_LIB_DIRS];
int lib_count;
const char* linker_args[MAX_LIB_DIRS];
int linker_arg_count;
const char* linker_lib_dir[MAX_LIB_DIRS];
int linker_lib_dir_count;
const char* linker_libs[MAX_LIB_DIRS];
int linker_lib_count;
const char* std_lib_dir;
struct {
const char *sdk;
WinCrtLinking crt_linking;
} win;
struct {
const char *sdk;
} macos;
int build_threads;
const char** files;
const char* output_name;
@@ -227,7 +253,12 @@ typedef struct BuildOptions_
bool emit_bitcode;
bool test_mode;
bool no_stdlib;
bool force_linker;
const char *panicfn;
const char *cc;
const char *build_dir;
const char *llvm_out;
const char *obj_out;
RelocModel reloc_model;
X86VectorCapability x86_vector_capability;
bool print_keywords;
@@ -249,17 +280,41 @@ typedef enum
TARGET_TYPE_TEST
} TargetType;
typedef struct
{
ArchOsTarget arch_os;
const char **link_flags;
const char **linked_libs;
const char **depends;
} LibraryTarget;
typedef struct
{
const char *dir;
const char *provides;
const char **depends;
LibraryTarget *target_used;
LibraryTarget **targets;
} Library;
typedef struct
{
TargetType type;
Library **library_list;
const char *name;
const char *version;
const char *langrev;
const char **source_dirs;
const char **sources;
const char **libraries;
const char **libdirs;
const char **libs;
const char **linker_libdirs;
const char **linker_libs;
const char *cpu;
const char **link_args;
const char *build_dir;
const char *object_file_dir;
const char *llvm_file_dir;
bool run_after_compile : 1;
bool test_output : 1;
bool output_headers : 1;
@@ -271,6 +326,7 @@ typedef struct
bool no_stdlib : 1;
bool emit_object_files : 1;
bool no_link : 1;
bool force_linker : 1;
OptimizationLevel optimization_level;
SizeOptimizationLevel size_optimization_level;
DebugInfo debug_info;
@@ -292,6 +348,15 @@ typedef struct
bool trap_on_wrap : 1;
bool safe_mode : 1;
} feature;
struct
{
const char *sdk;
} macos;
struct
{
const char *sdk;
WinCrtLinking crt_linking;
} win;
} BuildTarget;

View File

@@ -1,76 +1,90 @@
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by a LGPLv3.0
// a copy of which can be found in the LICENSE file.
#ifndef _MSC_VER
#include <dirent.h>
#else
#include "utils/dirent.h"
#endif
#include "build_internal.h"
#include "build_options.h"
void load_library_files(void) {}
void load_files(void) {}
#if defined(_M_X64) || defined(_M_AMD64)
#if defined(__MINGW32__)
ArchOsTarget default_target = X64_WINDOWS_GNU;
#else
ArchOsTarget default_target = X64_WINDOWS;
#endif
ArchOsTarget default_target = WINDOWS_X64;
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
#if defined(__MACH__)
ArchOsTarget default_target = X64_DARWIN;
ArchOsTarget default_target = MACOS_X64;
#elif defined(__linux__) && __linux__
ArchOsTarget default_target = X64_LINUX;
ArchOsTarget default_target = LINUX_X64;
#elif defined(__NetBSD__)
ArchOsTarget default_target = X64_NETBSD;
ArchOsTarget default_target = NETBSD_X64;
#elif defined(__FreeBSD__)
ArchOsTarget default_target = X64_FREEBSD;
ArchOsTarget default_target = FREEBSD_X64;
#elif defined(__OpenBSD__)
ArchOsTarget default_target = X64_OPENBSD;
ArchOsTarget default_target = OPENBSD_X64;
#else
ArchOsTarget default_target = X64_ELF;
ArchOsTarget default_target = ELF_X64;
#endif
#elif defined(__aarch64__) || defined(_M_ARM64)
#if defined(__MACH__)
ArchOsTarget default_target = AARCH64_DARWIN;
ArchOsTarget default_target = MACOS_AARCH64;
#elif defined(__linux__) && __linux__
ArchOsTarget default_target = AARCH64_LINUX;
ArchOsTarget default_target = LINUX_AARCH64;
#else
ArchOsTarget default_target = AARCH64_ELF;
ArchOsTarget default_target = ELF_AARCH64;
#endif
#elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
#if defined(__linux__) && __linux__
ArchOsTarget default_target = X86_LINUX;
ArchOsTarget default_target = LINUX_X86;
#elif defined(__FreeBSD__)
ArchOsTarget default_target = X86_FREEBSD;
ArchOsTarget default_target = FREEBSD_X86;
#elif defined(__OpenBSD__)
ArchOsTarget default_target = X86_OPENBSD;
ArchOsTarget default_target = OPENBSD_X86;
#elif defined(__NetBSD__)
ArchOsTarget default_target = X86_NETBSD;
ArchOsTarget default_target = NETBSD_X86;
#elif defined(_MSC_VER) && _MSC_VER
ArchOsTarget default_target = X86_WINDOWS;
ArchOsTarget default_target = WINDOWS_X86;
#else
ArchOsTarget default_target = X86_ELF;
ArchOsTarget default_target = ELF_X86;
#endif
#elif defined(__riscv32)
#if defined(__linux__) && __linux__
ArchOsTarget default_target = RISCV32_LINUX;
ArchOsTarget default_target = LINUX_RISCV32;
#else
ArchOsTarget default_target = RISCV32_ELF;
ArchOsTarget default_target = ELF_RISCV32;
#endif
#elif defined(__riscv64)
#if defined(__linux__) && __linux__
ArchOsTarget default_target = RISCV64_LINUX;
ArchOsTarget default_target = LINUX_RISCV64;
#else
ArchOsTarget default_target = RISCV64_ELF;
ArchOsTarget default_target = ELF_RISCV64;
#endif
#else
ArchOsTarget default_target = ARCH_OS_TARGET_DEFAULT;
#endif
static bool command_is_compile(CompilerCommand command)
{
switch (command)
{
case COMMAND_COMPILE:
case COMMAND_COMPILE_ONLY:
case COMMAND_COMPILE_RUN:
return true;
case COMMAND_MISSING:
case COMMAND_GENERATE_HEADERS:
case COMMAND_INIT:
case COMMAND_BUILD:
case COMMAND_RUN:
case COMMAND_CLEAN_RUN:
case COMMAND_CLEAN:
case COMMAND_DIST:
case COMMAND_DOCS:
case COMMAND_BENCH:
case COMMAND_UNIT_TEST:
case COMMAND_PRINT_SYNTAX:
return false;
}
UNREACHABLE
}
static void update_build_target_from_options(BuildTarget *target, BuildOptions *options)
{
switch (options->command)
@@ -143,6 +157,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
default:
UNREACHABLE
}
if (options->cc) target->cc = options->cc;
if (options->safe_mode > -1)
{
target->feature.safe_mode = options->safe_mode == 1;
@@ -163,11 +178,27 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
}
target->no_stdlib = options->no_stdlib;
target->emit_llvm = options->emit_llvm;
target->force_linker = options->force_linker;
target->panicfn = options->panicfn;
if (options->macos.sdk) target->macos.sdk = options->macos.sdk;
if (options->win.sdk) target->win.sdk = options->win.sdk;
if (options->win.crt_linking != WIN_CRT_DEFAULT) target->win.crt_linking = options->win.crt_linking;
if (options->x86_vector_capability != X86VECTOR_DEFAULT)
{
target->feature.x86_vector_capability = options->x86_vector_capability;
}
if (command_is_compile(options->command))
{
target->build_dir = options->build_dir ? options->build_dir : NULL;
target->object_file_dir = options->obj_out ? options->obj_out : target->build_dir;
target->llvm_file_dir = options->llvm_out ? options->llvm_out : target->build_dir;
}
else
{
target->build_dir = options->build_dir ? options->build_dir : "build";
target->object_file_dir = options->obj_out ? options->obj_out : file_append_path(target->build_dir, "tmp");
target->llvm_file_dir = options->llvm_out ? options->llvm_out : file_append_path(target->build_dir, "llvm_ir");
}
switch (options->compile_option)
{
case COMPILE_NORMAL:
@@ -198,6 +229,14 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
target->emit_llvm = false;
target->emit_object_files = false;
}
for (int i = 0; i < options->lib_dir_count; i++)
{
vec_add(target->libdirs, options->lib_dir[i]);
}
for (int i = 0; i < options->lib_count; i++)
{
vec_add(target->libs, options->libs[i]);
}
}
void init_default_build_target(BuildTarget *target, BuildOptions *options)
@@ -214,6 +253,7 @@ void init_default_build_target(BuildTarget *target, BuildOptions *options)
.arch_os_target = ARCH_OS_TARGET_DEFAULT,
.reloc_model = RELOC_DEFAULT,
.feature.x86_vector_capability = X86VECTOR_DEFAULT,
.win.crt_linking = WIN_CRT_DEFAULT,
};
update_build_target_from_options(target, options);
}
@@ -221,13 +261,17 @@ void init_default_build_target(BuildTarget *target, BuildOptions *options)
void init_build_target(BuildTarget *target, BuildOptions *options)
{
*target = (BuildTarget) { 0 };
// Locate the project.toml
// Locate the project.c3p
file_find_top_dir();
// Parse it
Project *project = project_load();
*target = *project_select_target(project, options->target_select);
update_build_target_from_options(target, options);
if (target->build_dir && !file_exists(target->build_dir))
{
if (!dir_make(target->build_dir)) error_exit("Failed to create build directory '%s'.", target->build_dir);
if (!file_is_dir(target->build_dir)) error_exit("Expected '%s' to be a directory.", target->build_dir);
}
load_library_files();
}

View File

@@ -47,7 +47,7 @@ static int get_valid_string_setting(JSONObject *json, const char *key, const cha
}
if (value->type == J_STRING)
{
int res = str_in_list(value->str, count, values);
int res = str_findlist(value->str, count, values);
if (res >= 0) return res + first_result;
}
error_exit("%s had an invalid value for '%s', expected %s", category, key, expected);
@@ -68,7 +68,7 @@ long get_valid_integer(JSONObject *table, const char *key, const char *category,
{
error_exit("%s had an invalid mandatory '%s' field that was not an integer, please correct it.", category, key);
}
return trunc(value->f);
return (long)trunc(value->f);
}
@@ -109,6 +109,19 @@ static void load_into_build_target(JSONObject *json, const char *type, BuildTarg
const char *langrev = get_valid_string(json, "langrev", type, false);
const char **source_dirs = get_valid_array(json, "sources", type, target->source_dirs == NULL);
const char **libraries = get_valid_array(json, "libs", type, false);
const char **linker_libs = get_valid_array(json, "linker-libs", type, false);
VECEACH(libraries, i)
{
if (!str_is_valid_lowercase_name(libraries[i]))
{
char *name = strdup(libraries[i]);
str_ellide_in_place(name, 32);
error_exit("Error reading %s: invalid library target '%s'.", PROJECT_JSON, name);
}
}
const char **libdirs = get_valid_array(json, "libdir", type, false);
const char **linker_libdirs = get_valid_array(json, "linker-libdir", type, false);
static const char *debug_infos[3] = {
[DEBUG_INFO_FULL] = "full",
[DEBUG_INFO_NONE] = "none",
@@ -119,9 +132,11 @@ static void load_into_build_target(JSONObject *json, const char *type, BuildTarg
long symtab_size = get_valid_integer(json, "symtab", type, false);
const char *cpu = get_valid_string(json, "cpu", type, false);
int reloc = get_valid_string_setting(json, "reloc", type, reloc_models, 0, 5, "'none', 'pic', 'PIC', 'pie' or 'PIE'.");
int wincrt = get_valid_string_setting(json, "wincrt", type, wincrt_linking, 0, 5, "'none', 'static' or 'dynamic'.");
int x86vec = get_valid_string_setting(json, "x86vec", type, vector_capability, 0, 5, "none, mmx, sse, avx or avx512");
const char *panicfn = get_valid_string(json, "panicfn", type, false);
target->win.sdk = get_valid_string(json, "winsdk", type, false);
target->macos.sdk = get_valid_string(json, "macossdk", type, false);
target->panicfn = panicfn;
if (cc) target->cc = cc;
if (cflags) target->cflags = cflags;
@@ -129,9 +144,13 @@ static void load_into_build_target(JSONObject *json, const char *type, BuildTarg
if (version) target->version = version;
if (langrev) target->langrev = langrev;
if (source_dirs) target->source_dirs = source_dirs;
if (libraries) target->libraries = libraries;
if (libdirs) target->libdirs = libdirs;
if (linker_libdirs) target->linker_libdirs = linker_libdirs;
if (linker_libs) target->linker_libs = linker_libs;
if (libraries) target->libs = libraries;
if (info > -1) target->debug_info = info;
if (cpu) target->cpu = cpu;
if (wincrt > -1) target->win.crt_linking = (WinCrtLinking)wincrt;
if (reloc > -1) target->reloc_model = (RelocModel)reloc;
if (x86vec > -1) target->feature.x86_vector_capability = x86vec;
@@ -177,7 +196,7 @@ static void project_add_target(Project *project, BuildTarget *default_target, J
error_exit("More %s contained more than one target with the name %s. Please make all target names unique.", PROJECT_JSON, target->name);
}
}
type = strformat("%s %s", type, target->name);
type = str_printf("%s %s", type, target->name);
load_into_build_target(json, type, target);
}
@@ -209,6 +228,7 @@ static void project_add_targets(Project *project, JSONObject *project_data)
.feature.trap_on_wrap = false,
.feature.x86_vector_capability = X86VECTOR_DEFAULT,
.feature.safe_mode = true,
.win.crt_linking = WIN_CRT_DEFAULT,
};
load_into_build_target(project_data, "default target", &default_target);
JSONObject *targets_json = json_obj_get(project_data, "targets");
@@ -266,7 +286,7 @@ Project *project_load(void)
{
Project *project = CALLOCS(Project);
size_t size;
char *read = read_file(PROJECT_JSON, &size);
char *read = file_read_all(PROJECT_JSON, &size);
JsonParser parser;
json_init_string(&parser, read, &malloc_arena);
JSONObject *json = json_parse(&parser);

View File

@@ -2,14 +2,11 @@
// Use of this source code is governed by the GNU LGPLv3.0 license
// a copy of which can be found in the LICENSE file.
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <string.h>
#include "project_creation.h"
#include "build_options.h"
#include "../utils/lib.h"
@@ -21,7 +18,7 @@ const char* JSON =
" // warnings used for all targets\n"
" \"warnings\": [ \"no-unused\" ],\n"
" // libraries to use for all targets\n"
" \"libs\": [ \"lib/**\" ],\n"
" \"libs\": [ ],\n"
" // authors, optionally with email\n"
" \"authors\": [ \"John Doe <john.doe@example.com>\" ],\n"
" // Version using semantic versioning\n"
@@ -41,7 +38,7 @@ const char* JSON =
" // Debug information, may be 'none', 'full' and 'line-tables'\n"
" \"debug-info\": \"full\",\n"
" // Architecture and OS target:\n"
" \"target\": \"x64-windows\",\n"
" \"target\": \"windows-x64\",\n"
" // The size of the symtab, which limits the amount\n"
" // of symbols that can be used. Should usually not\n"
" // be changed.\n"
@@ -76,97 +73,85 @@ void create_project(BuildOptions *build_options)
{
char c = build_options->project_name[i];
if (c == '\0') break;
if (!is_alphanum_(c))
if (!char_is_alphanum_(c))
{
fprintf(stderr, "'%s' is not a valid project name.\n", build_options->project_name);
exit_compiler(EXIT_FAILURE);
}
}
if (chdir(build_options->path))
if (!dir_change(build_options->path))
{
fprintf(stderr, "Can't open path %s\n", build_options->path);
exit_compiler(EXIT_FAILURE);
}
int error = mkdir(build_options->project_name, 0755);
if (error)
if (!dir_make(build_options->project_name))
{
fprintf(stderr, "Could not create directory %s: %s\n", build_options->project_name, strerror(errno));
fprintf(stderr, "Could not create directory %s.\n", build_options->project_name);
exit_compiler(EXIT_FAILURE);
}
if (chdir(build_options->project_name)) goto ERROR;
if (!dir_change(build_options->project_name)) goto ERROR;
FILE *file = fopen("LICENCE", "a");
if (!file) goto ERROR;
if (fclose(file)) goto ERROR;
if (!file_touch("LICENSE")) goto ERROR;
file = fopen("README.md", "a");
if (!file) goto ERROR;
if (fclose(file)) goto ERROR;
if (!file_touch("README.md")) goto ERROR;
file = fopen("project.c3p", "a");
FILE *file = fopen("project.c3p", "a");
if (!file) goto ERROR;
(void) fprintf(file, JSON, build_options->project_name);
if (fclose(file)) goto ERROR;
if (mkdir("lib", 0755)) goto ERROR;
if (!dir_make("lib")) goto ERROR;
if (mkdir("build", 0755)) goto ERROR;
if (!dir_make("build")) goto ERROR;
if (mkdir("resources", 0755)) goto ERROR;
if (!dir_make("resources")) goto ERROR;
if (mkdir("test", 0755)) goto ERROR;
if (!dir_make("test")) goto ERROR;
if (chdir("test")) goto ERROR;
if (!dir_change("test")) goto ERROR;
if (mkdir(build_options->project_name, 0755)) goto ERROR;
if (!dir_make(build_options->project_name)) goto ERROR;
if (chdir("..")) goto ERROR;
if (!dir_change("..")) goto ERROR;
if (mkdir("directives", 0755)) goto ERROR;
if (!dir_make("directives")) goto ERROR;
if (chdir("directives") == -1) goto ERROR;
if (!dir_change("directives")) goto ERROR;
file = fopen("about.md", "a");
if (!file) goto ERROR;
if (fclose(file)) goto ERROR;
if (!file_touch("about.md")) goto ERROR;
if (mkdir("src", 0755)) goto ERROR;
if (!dir_make("src")) goto ERROR;
if (chdir("src")) goto ERROR;
if (!dir_change("src")) goto ERROR;
file = fopen("index.html", "a");
if (!file) goto ERROR;
if (fclose(file)) goto ERROR;
if (!file_touch("index.html")) goto ERROR;
if (chdir("../..")) goto ERROR;
if (!dir_change("../..")) goto ERROR;
if (mkdir("src", 0755)) goto ERROR;
if (!dir_make("src")) goto ERROR;
if (chdir("src")) goto ERROR;
if (!dir_change("src")) goto ERROR;
if (mkdir(build_options->project_name, 0755)) goto ERROR;
if (!dir_make(build_options->project_name)) goto ERROR;
if (chdir(build_options->project_name)) goto ERROR;
if (!dir_change(build_options->project_name)) goto ERROR;
file = fopen("main.c3", "a");
if (!file) goto ERROR;
if (fclose(file)) goto ERROR;
if (!file_touch("main.c3")) goto ERROR;
if (chdir("../..")) goto ERROR;
if (!dir_change("../..")) goto ERROR;
(void) printf("Project '%s' created.\n", build_options->project_name);
exit_compiler(COMPILER_SUCCESS_EXIT);
ERROR:
fprintf(stderr, "Err: %s\n", strerror(errno));
printf("Something went wrong creating the project.\n");
if (!chdir(build_options->path))
fprintf(stderr, "Error creating the project.\n");
if (dir_change(build_options->path))
{
(void)rmdir(build_options->project_name);
}
exit_compiler(EXIT_FAILURE);
}

View File

@@ -118,6 +118,28 @@ const char *decl_to_name(Decl *decl)
}
UNREACHABLE
}
void module_append_name_to_scratch(Module *module)
{
const char *name = module->name->module;
char c;
while ((c = *(name++)) != 0)
{
switch (c)
{
case '_':
scratch_buffer_append("_");
break;
case ':':
scratch_buffer_append_char('_');
name++;
break;
default:
scratch_buffer_append_char(c);
break;
}
}
}
void decl_set_external_name(Decl *decl)
{
if (decl->has_extname) return;
@@ -128,8 +150,8 @@ void decl_set_external_name(Decl *decl)
return;
}
scratch_buffer_clear();
scratch_buffer_append(decl->module->name->module);
scratch_buffer_append(".");
module_append_name_to_scratch(decl->unit->module);
scratch_buffer_append("_");
scratch_buffer_append(decl->name ? decl->name : "anon");
decl->extname = scratch_buffer_copy();
}
@@ -229,13 +251,17 @@ bool expr_is_pure(Expr *expr)
{
case EXPR_BUILTIN:
return false;
case EXPR_BUILTIN_ACCESS:
return exprid_is_pure(expr->builtin_access_expr.inner);
case EXPR_VARIANT:
return exprid_is_pure(expr->variant_expr.type_id) && exprid_is_pure(expr->variant_expr.ptr);
case EXPR_COMPILER_CONST:
case EXPR_CONST:
case EXPR_IDENTIFIER:
case EXPR_NOP:
case EXPR_PTR:
case EXPR_STRINGIFY:
case EXPR_RETVAL:
case EXPR_CT_CONV:
return true;
case EXPR_ARGV_TO_SUBARRAY:
case EXPR_BITASSIGN:
@@ -275,7 +301,6 @@ bool expr_is_pure(Expr *expr)
case EXPR_RETHROW:
case EXPR_HASH_IDENT:
case EXPR_MACRO_BLOCK:
case EXPR_MACRO_EXPANSION:
case EXPR_FLATPATH:
case EXPR_INITIALIZER_LIST:
case EXPR_DESIGNATED_INITIALIZER_LIST:
@@ -294,11 +319,10 @@ bool expr_is_pure(Expr *expr)
if (!expr_is_pure(expr->expression_list[i])) return false;
}
return true;
case EXPR_TYPEOFANY:
case EXPR_TYPEID_INFO:
return exprid_is_pure(expr->typeid_info_expr.parent);
case EXPR_CT_EVAL:
return expr_is_pure(expr->inner_expr);
case EXPR_LEN:
return expr_is_pure(expr->len_expr.inner);
case EXPR_SLICE:
return exprid_is_pure(expr->slice_expr.expr)
&& exprid_is_pure(expr->slice_expr.start)
@@ -329,7 +353,7 @@ bool expr_is_simple(Expr *expr)
expr = expr->inner_expr;
goto RETRY;
case EXPR_TERNARY:
return false;
return expr_is_simple(exprptr(expr->ternary_expr.else_expr)) && expr_is_simple(exprptr(expr->ternary_expr.then_expr));
case EXPR_RETHROW:
expr = expr->rethrow_expr.inner;
goto RETRY;
@@ -490,4 +514,14 @@ bool ast_is_not_empty(Ast *ast)
return ast_is_not_empty(stmt);
}
return false;
}
}
AttributeType attribute_by_name(const char *name)
{
for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++)
{
if (attribute_list[i] == name) return (AttributeType)i;
}
return ATTRIBUTE_NONE;
}

View File

@@ -112,7 +112,7 @@ UNUSED Int128 i128_from_hexstrl(const char *str, const char *end)
while (str != end)
{
c = *(str++);
x = i128_add64(i128_shl64(x, 4), (uint64_t)hex_nibble(c));
x = i128_add64(i128_shl64(x, 4), (uint64_t)char_hex_to_nibble(c));
}
return x;
}

View File

@@ -1,5 +1,15 @@
#include "compiler_internal.h"
enum IntrospectIndex
{
INTROSPECT_INDEX_KIND = 0,
INTROSPECT_INDEX_SIZEOF = 1,
INTROSPECT_INDEX_INNER = 2,
INTROSPECT_INDEX_LEN = 3,
INTROSPECT_INDEX_ADDITIONAL = 4,
INTROSPECT_INDEX_TOTAL,
};
bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements);
static inline Type *type_reduced_from_expr(Expr *expr);
static inline bool abi_type_is_type(AbiType type);

View File

@@ -3,10 +3,9 @@
// a copy of which can be found in the LICENSE file.
#include "compiler_internal.h"
#include <compiler_tests/benchmark.h>
#ifndef _MSC_VER
#include <unistd.h>
#include <compiler_tests/benchmark.h>
#endif
#define MAX_OUTPUT_FILES 1000000
@@ -25,6 +24,8 @@ double compiler_ir_gen_time;
double compiler_codegen_time;
double compiler_link_time;
const char* c3_suffix_list[3] = { ".c3", ".c3t", ".c3i" };
void compiler_init(const char *std_lib_dir)
{
compiler_init_time = -1;
@@ -47,13 +48,13 @@ void compiler_init(const char *std_lib_dir)
htable_init(&global_context.compiler_defines, 16 * 1024);
global_context.module_list = NULL;
global_context.generic_module_list = NULL;
vmem_init(&ast_arena, 4 * 1024);
vmem_init(&ast_arena, 512);
ast_calloc();
vmem_init(&expr_arena, 4 * 1024);
vmem_init(&expr_arena, 512);
expr_calloc();
vmem_init(&decl_arena, 1024);
vmem_init(&decl_arena, 256);
decl_calloc();
vmem_init(&type_info_arena, 1024);
vmem_init(&type_info_arena, 256);
type_info_calloc();
// Create zero index value.
if (std_lib_dir)
@@ -124,14 +125,18 @@ void thread_compile_task_tb(void *compile_data)
static const char *active_target_name(void)
{
if (active_target.name) return active_target.name;
switch (active_target.arch_os_target)
{
case X86_WINDOWS:
case X64_WINDOWS:
case X64_WINDOWS_GNU:
case WINDOWS_X86:
case WINDOWS_X64:
case MINGW_X64:
if (active_target.name)
{
return str_cat(active_target.name, ".exe");
}
return "a.exe";
default:
if (active_target.name) return active_target.name;
return "a.out";
}
}
@@ -179,7 +184,7 @@ static void compiler_print_bench(void)
if (compiler_sema_time >= 0) printf("Analysis took: %.4f ms\n", (compiler_sema_time - compiler_parsing_time) * 1000);
if (compiler_ir_gen_time >= 0) printf("Ir gen took: %.4f ms\n", (compiler_ir_gen_time - compiler_sema_time) * 1000);
if (compiler_codegen_time >= 0) printf("Codegen took: %.4f ms\n", (compiler_codegen_time - compiler_ir_gen_time) * 1000);
if (compiler_link_time >= 0) printf("Linking took: %.4f ms\n", (compiler_link_time - compiler_codegen_time) * 1000);
if (compiler_link_time >= 0) printf("Linking took: %f ms\n", (compiler_link_time - compiler_codegen_time) * 1000);
}
}
@@ -217,6 +222,27 @@ void compiler_compile(void)
void **gen_contexts = VECNEW(void*, module_count);
void (*task)(void *);
if (active_target.llvm_file_dir || active_target.emit_object_files)
{
if (active_target.build_dir && !file_exists(active_target.build_dir) && !dir_make(active_target.build_dir))
{
error_exit("Failed to create build directory '%s'.", active_target.build_dir);
}
}
if (active_target.llvm_file_dir && active_target.emit_llvm)
{
if (!file_exists(active_target.llvm_file_dir) && !dir_make(active_target.llvm_file_dir))
{
error_exit("Failed to create output directory '%s'.", active_target.llvm_file_dir);
}
}
if (active_target.object_file_dir && active_target.emit_object_files)
{
if (!file_exists(active_target.object_file_dir) && !dir_make(active_target.object_file_dir))
{
error_exit("Failed to create output directory '%s'.", active_target.object_file_dir);
}
}
switch (active_target.backend)
{
case BACKEND_LLVM:
@@ -269,7 +295,7 @@ void compiler_compile(void)
for (int i = 0; i < cfiles; i++)
{
char *filename = NULL;
bool split_worked = filenamesplit(active_target.csources[i], &filename, NULL);
bool split_worked = file_namesplit(active_target.csources[i], &filename, NULL);
assert(split_worked);
size_t len = strlen(filename);
// .c -> .o (quick hack to fix the name on linux)
@@ -306,7 +332,7 @@ void compiler_compile(void)
if (create_exe)
{
const char *output_name = active_target_name();
if (active_target.arch_os_target == default_target)
if (platform_target.os != OS_TYPE_WIN32 && active_target.arch_os_target == default_target && !active_target.force_linker)
{
platform_linker(output_name, obj_files, output_file_count);
compiler_link_time = bench_mark();
@@ -325,8 +351,9 @@ void compiler_compile(void)
if (active_target.run_after_compile)
{
DEBUG_LOG("Will run");
printf("Launching %s...\n", output_name);
int ret = system(strformat("./%s", output_name));
int ret = system(platform_target.os == OS_TYPE_WIN32 ? output_name : str_printf("./%s", output_name));
printf("Program finished with exit code %d.", ret);
}
}
@@ -334,11 +361,9 @@ void compiler_compile(void)
free(obj_files);
}
static const char **target_expand_source_names(const char** dirs, const char *suffix1, const char *suffix2, bool error_on_mismatch)
static const char **target_expand_source_names(const char** dirs, const char **suffix_list, int suffix_count, bool error_on_mismatch)
{
const char **files = NULL;
size_t len1 = strlen(suffix1);
size_t len2 = strlen(suffix2);
VECEACH(dirs, i)
{
const char *name = dirs[i];
@@ -348,27 +373,26 @@ static const char **target_expand_source_names(const char** dirs, const char *su
{
if (name_len == 1 || name[name_len - 2] == '/')
{
char *path = copy_string(name, name_len - 1);
file_add_wildcard_files(&files, path, false, suffix1, suffix2);
char *path = str_copy(name, name_len - 1);
file_add_wildcard_files(&files, path, false, suffix_list, suffix_count);
continue;
}
if (name[name_len - 2] != '*') goto INVALID_NAME;
if (name_len == 2 || name[name_len - 3] == '/')
{
char *path = copy_string(name, name_len - 2);
file_add_wildcard_files(&files, path, true, suffix1, suffix2);
char *path = str_copy(name, name_len - 2);
file_add_wildcard_files(&files, path, true, suffix_list, suffix_count);
continue;
}
goto INVALID_NAME;
}
if (name_len < 4) goto INVALID_NAME;
if (strcmp(&name[name_len - len1], suffix1) != 0 &&
(name_len < 5 || strcmp(&name[name_len - len2], suffix2) != 0)) goto INVALID_NAME;
if (name_len < 5 || !file_has_suffix_in_list(name, name_len, suffix_list, suffix_count)) goto INVALID_NAME;
vec_add(files, name);
continue;
INVALID_NAME:
if (!error_on_mismatch) continue;
error_exit("File names must end with %s or they cannot be compiled: '%s' is invalid.", name, suffix1);
error_exit("File names must end with %s or they cannot be compiled: '%s' is invalid.", suffix_list[0], name);
}
return files;
}
@@ -379,9 +403,18 @@ void compile_target(BuildOptions *options)
compile();
}
void compile_clean(BuildOptions *options)
{
init_build_target(&active_target, options);
file_delete_all_files_in_dir_with_suffix(active_target.build_dir, get_object_extension());
}
void compile_file_list(BuildOptions *options)
{
init_build_target(&active_target, options);
if (options->command == COMMAND_CLEAN_RUN)
{
file_delete_all_files_in_dir_with_suffix(active_target.build_dir, get_object_extension());
}
compile();
}
@@ -490,16 +523,21 @@ void print_syntax(BuildOptions *options)
}
}
void resolve_libraries(void);
void compile()
{
active_target.sources = target_expand_source_names(active_target.source_dirs, ".c3", ".c3t", true);
symtab_init(active_target.symtab_size);
active_target.sources = target_expand_source_names(active_target.source_dirs, c3_suffix_list, 3, true);
if (active_target.csource_dirs)
{
active_target.csources = target_expand_source_names(active_target.csource_dirs, ".c", ".c", false);
static const char* c_suffix_list[3] = { ".c" };
active_target.csources = target_expand_source_names(active_target.csource_dirs, c_suffix_list, 1, false);
}
global_context.sources = active_target.sources;
symtab_init(active_target.symtab_size);
target_setup(&active_target);
resolve_libraries();
setup_int_define("C_SHORT_SIZE", platform_target.width_c_short, type_long);
setup_int_define("C_INT_SIZE", platform_target.width_c_int, type_long);
@@ -522,7 +560,6 @@ void compile()
{
compiler_lex();
compiler_parsing_time = bench_mark();
return;
}
if (active_target.parse_only)
@@ -558,9 +595,9 @@ const char *get_object_extension(void)
{
switch (active_target.arch_os_target)
{
case X64_WINDOWS:
case X86_WINDOWS:
case X64_WINDOWS_GNU:
case WINDOWS_X64:
case WINDOWS_X86:
case MINGW_X64:
return ".obj";
default:
return ".o";
@@ -599,69 +636,11 @@ Module *compiler_find_or_create_module(Path *module_name, const char **parameter
return module;
}
void scratch_buffer_clear(void)
{
global_context.scratch_buffer_len = 0;
}
void scratch_buffer_append_len(const char *string, size_t len)
{
if (len + global_context.scratch_buffer_len > MAX_STRING_BUFFER - 1)
{
error_exit("Scratch buffer size (%d chars) exceeded", MAX_STRING_BUFFER - 1);
}
memcpy(global_context.scratch_buffer + global_context.scratch_buffer_len, string, len);
global_context.scratch_buffer_len += len;
}
void scratch_buffer_append(const char *string)
{
scratch_buffer_append_len(string, strlen(string));
}
void scratch_buffer_append_signed_int(int64_t i)
{
uint32_t len_needed = (uint32_t)sprintf(&global_context.scratch_buffer[global_context.scratch_buffer_len], "%lld", (long long)i);
if (global_context.scratch_buffer_len + len_needed > MAX_STRING_BUFFER - 1)
{
error_exit("Scratch buffer size (%d chars) exceeded", MAX_STRING_BUFFER - 1);
}
global_context.scratch_buffer_len += len_needed;
}
void scratch_buffer_append_unsigned_int(uint64_t i)
{
uint32_t len_needed = (uint32_t)sprintf(&global_context.scratch_buffer[global_context.scratch_buffer_len], "%llu", (unsigned long long)i);
if (global_context.scratch_buffer_len + len_needed > MAX_STRING_BUFFER - 1)
{
error_exit("Scratch buffer size (%d chars) exceeded", MAX_STRING_BUFFER - 1);
}
global_context.scratch_buffer_len += len_needed;
}
void scratch_buffer_append_char(char c)
{
if (global_context.scratch_buffer_len + 1 > MAX_STRING_BUFFER - 1)
{
error_exit("Scratch buffer size (%d chars) exceeded", MAX_STRING_BUFFER - 1);
}
global_context.scratch_buffer[global_context.scratch_buffer_len++] = c;
}
char *scratch_buffer_to_string(void)
{
global_context.scratch_buffer[global_context.scratch_buffer_len] = '\0';
return global_context.scratch_buffer;
}
const char *scratch_buffer_interned(void)
{
TokenType type = TOKEN_INVALID_TOKEN;
return symtab_add(global_context.scratch_buffer, global_context.scratch_buffer_len,
fnv1a(global_context.scratch_buffer, global_context.scratch_buffer_len), &type);
return symtab_add(scratch_buffer.str, scratch_buffer.len,
fnv1a(scratch_buffer.str, scratch_buffer.len), &type);
}
char *scratch_buffer_copy(void)
{
return copy_string(global_context.scratch_buffer, global_context.scratch_buffer_len);
}

View File

@@ -10,6 +10,7 @@ void compiler_init(const char *std_lib_dir);
void compile();
void compile_target(BuildOptions *options);
void compile_file_list(BuildOptions *options);
void compile_clean(BuildOptions *options);
void init_build_target(BuildTarget *build_target, BuildOptions *build_options);
void init_default_build_target(BuildTarget *target, BuildOptions *options);
void symtab_init(uint32_t max_size);
@@ -21,4 +22,6 @@ extern double compiler_parsing_time;
extern double compiler_sema_time;
extern double compiler_ir_gen_time;
extern double compiler_codegen_time;
extern double compiler_link_time;
extern double compiler_link_time;
extern const char* c3_suffix_list[3];
extern char *arch_os_target[ARCH_OS_TARGET_LAST + 1];

View File

@@ -34,6 +34,7 @@ typedef uint64_t BitSize;
#define INITIAL_GENERIC_SYMBOL_MAP 0x1000
#define MAX_MACRO_ITERATIONS 0xFFFFFF
#define MAX_PARAMS 512
#define MAX_BITSTRUCT 0x1000
#define MAX_MEMBERS ((MemberIndex)(((uint64_t)2) << 28))
#define MAX_ALIGNMENT ((MemberIndex)(((uint64_t)2) << 28))
#define MAX_TYPE_SIZE UINT32_MAX
@@ -51,6 +52,7 @@ typedef unsigned ExprId;
typedef unsigned DeclId;
typedef unsigned TypeInfoId;
typedef struct Int128_
{
uint64_t high;
@@ -113,10 +115,6 @@ typedef struct ConstInitializer_
};
} ConstInitializer;
typedef struct
{
ConstKind const_kind : 8;
@@ -145,6 +143,7 @@ typedef struct
};
} ExprConst;
typedef uint16_t FileId;
typedef struct
{
@@ -235,7 +234,6 @@ struct Type_
const char *name;
Type **type_cache;
void *backend_type;
void *backend_aux_type;
void *backend_typeid;
void *backend_debug_type;
union
@@ -246,7 +244,7 @@ struct Type_
TypeBuiltin builtin;
// Type[], Type[*], Type[123], Type[<123>] or Type<[123]>
TypeArray array;
// func Type1(Type2, Type3, ...) throws Err1, Err2, ...
// fn Type1(Type2, Type3, ...) throws Err1, Err2, ...
TypeFunc func;
// Type*
Type *pointer;
@@ -300,18 +298,16 @@ typedef struct
Path *path;
const char *name;
SourceSpan span;
union
{
Expr *expr;
uint32_t alignment;
OperatorOverload operator;
};
AttributeType attr_kind : 8;
bool is_custom : 1;
Expr **exprs;
} Attr;
typedef struct
{
Path *path;
bool private;
Module *module;
} ImportDecl;
typedef struct
@@ -336,7 +332,6 @@ typedef struct
typedef struct VarDecl_
{
VarDeclKind kind : 8;
bool constant : 1;
bool unwrap : 1;
bool shadow : 1;
bool vararg : 1;
@@ -358,6 +353,7 @@ typedef struct VarDecl_
};
union
{
int32_t index;
struct
{
struct SemaContext_ *context;
@@ -416,9 +412,8 @@ typedef struct
typedef struct
{
Expr *expr;
Expr **args;
uint64_t ordinal;
uint32_t ordinal;
DeclId parent;
} EnumConstantDecl;
@@ -445,6 +440,7 @@ typedef struct FunctionSignature_
bool has_default : 1;
bool use_win64 : 1;
bool is_pure : 1;
CallABI abi : 8;
TypeInfoId returntype;
Decl** params;
} FunctionSignature;
@@ -455,12 +451,13 @@ typedef struct
{
struct
{
bool attr_weak : 1;
bool attr_noreturn : 1;
bool attr_inline : 1;
bool attr_noinline : 1;
bool attr_extname : 1;
bool attr_naked : 1;
bool attr_nodiscard : 1;
bool attr_maydiscard: 1;
};
TypeInfoId type_parent;
FunctionSignature function_signature;
@@ -470,8 +467,8 @@ typedef struct
typedef struct
{
AttributeDomain domains;
FunctionSignature attr_signature;
Decl **params;
Attr **attrs;
} AttrDecl;
typedef struct
@@ -500,6 +497,8 @@ typedef struct
struct
{
bool attr_noreturn : 1;
bool attr_nodiscard : 1;
bool attr_maydiscard : 1;
};
TypeInfoId type_parent; // May be 0
TypeInfoId rtype; // May be 0
@@ -524,7 +523,6 @@ typedef enum
DEFINE_TYPE_GENERIC,
DEFINE_IDENT_ALIAS,
DEFINE_IDENT_GENERIC,
DEFINE_ATTRIBUTE,
} DefineType;
typedef struct
@@ -532,11 +530,6 @@ typedef struct
DefineType define_kind: 5;
union
{
struct
{
Decl **params;
Attr **attrs;
} attributes;
struct
{
union
@@ -586,7 +579,13 @@ typedef struct Decl_
bool is_autoimport : 1;
bool has_extname : 1;
bool is_external_visible : 1;
OperatorOverload operator : 3;
bool is_weak : 1;
bool is_maybe_unused : 1;
bool is_must_use : 1;
bool will_reflect : 1;
bool obfuscate : 1;
bool is_dynamic : 1;
OperatorOverload operator : 4;
union
{
void *backend_ref;
@@ -608,7 +607,7 @@ typedef struct Decl_
uint32_t counter;
};
uint32_t size;*/
Module *module;
struct CompilationUnit_ *unit;
Attr** attributes;
Type *type;
union
@@ -619,8 +618,9 @@ typedef struct Decl_
Decl **methods;
union
{
// Unions, Fault and Struct use strukt
// Unions, Struct use strukt
StructDecl strukt;
// Enums and Fault
EnumDecl enums;
DistinctDecl distinct_decl;
BitStructDecl bitstruct;
@@ -632,7 +632,7 @@ typedef struct Decl_
LabelDecl label;
EnumConstantDecl enum_constant;
FuncDecl func_decl;
AttrDecl attr;
AttrDecl attr_decl;
TypedefDecl typedef_decl;
MacroDecl macro_decl;
GenericDecl generic_decl;
@@ -689,6 +689,7 @@ typedef struct
bool is_builtin : 1;
bool is_func_ref : 1;
bool attr_pure : 1;
bool result_unused : 1;
AstId body;
union
{
@@ -710,6 +711,7 @@ typedef struct
{
bool start_from_back : 1;
bool end_from_back : 1;
bool is_lenrange : 1;
ExprId expr;
ExprId start;
ExprId end;
@@ -721,7 +723,26 @@ typedef struct
ExprId right;
} ExprSliceAssign;
typedef struct
{
ExprId ptr;
ExprId type_id;
} ExprVariant;
typedef enum
{
ACCESS_LEN,
ACCESS_PTR,
ACCESS_TYPEOFANY,
ACCESS_ENUMNAME,
ACCESS_FAULTNAME,
} BuiltinAccessKind;
typedef struct
{
BuiltinAccessKind kind : 8;
ExprId inner;
} ExprBuiltinAccess;
typedef struct
{
Expr *parent;
@@ -786,19 +807,21 @@ typedef struct
typedef struct
{
TokenType token_type;
struct
union
{
Expr *main_var;
ExprFlatElement *flat_path;
struct
{
Expr *main_var;
ExprFlatElement *flat_path;
};
struct
{
TypeInfoId type_from;
TypeInfoId type_to;
};
};
} ExprCtCall;
typedef struct
{
Expr *inner;
Decl *decl;
} ExprMacroExpansion;
typedef struct
{
CastKind kind : 8;
@@ -815,16 +838,27 @@ typedef struct
} ExprBodyExpansion;
typedef struct
{
void *block_return_exit;
void *block_failable_exit;
void *block_error_var;
void *block_return_out;
} BlockExit;
typedef struct
{
AstId first_stmt;
BlockExit **block_exit_ref;
} ExprFuncBlock;
typedef struct
{
AstId first_stmt;
Expr **args;
Decl **params;
BlockExit **block_exit;
} ExprMacroBlock;
@@ -917,6 +951,12 @@ typedef struct
};
} ExprVariantSwitch;
typedef struct
{
ExprId parent;
TypeIdInfoKind kind;
} ExprTypeidInfo;
typedef struct
{
Decl *argc;
@@ -930,9 +970,10 @@ struct Expr_
ExprKind expr_kind : 8;
ResolveStatus resolve_status : 4;
union {
ExprTypeidInfo typeid_info_expr;
ExprVariantSwitch variant_switch; // 32
ExprLen len_expr; // 8
ExprCast cast_expr; // 12
ExprVariant variant_expr;
TypeInfo *type_expr; // 8
ExprConst const_expr; // 32
ExprArgv argv_expr; // 16
@@ -947,6 +988,7 @@ struct Expr_
ExprCall call_expr; // 32
ExprSlice slice_expr; // 16
Expr *inner_expr; // 8
ExprBuiltinAccess builtin_access_expr;
ExprCatchUnwrap catch_unwrap_expr; // 24
ExprSubscript subscript_expr; // 12
ExprAccess access_expr; // 16
@@ -955,7 +997,6 @@ struct Expr_
ExprIdentifierRaw ct_ident_expr; // 24
ExprCtCall ct_call_expr; // 24
ExprIdentifierRaw ct_macro_ident_expr; // 24
ExprMacroExpansion macro_expansion_expr; // 16
ExprIdentifierRaw hash_ident_expr; // 24
TypeInfo *typeid_expr; // 8
ExprBodyExpansion body_expansion_expr; // 24
@@ -983,6 +1024,7 @@ typedef struct
{
Expr *expr; // May be NULL
AstId cleanup;
BlockExit** block_exit_ref; // For block exits
} AstReturnStmt;
typedef struct
@@ -1268,6 +1310,9 @@ typedef struct Module_
Decl** generic_cache;
HTable symbols;
struct CompilationUnit_ **units;
Module *parent_module;
Module *top_module;
Module **sub_modules;
} Module;
@@ -1332,6 +1377,13 @@ typedef struct
LexMode mode;
} Lexer;
typedef struct
{
uint32_t count;
uint32_t capacity;
uint32_t max_load;
DeclId *entries;
} DeclTable;
typedef struct CompilationUnit_
{
@@ -1341,6 +1393,7 @@ typedef struct CompilationUnit_
Decl **types;
Decl **functions;
Decl **enums;
Decl **attributes;
Decl **faulttypes;
struct
{
@@ -1402,6 +1455,7 @@ typedef struct SemaContext_
uint32_t original_inline_line;
Decl **yield_params;
Ast *yield_body;
BlockExit** block_exit_ref;
Type *expected_block_type;
Ast **returns;
// Reusable returns cache.
@@ -1414,13 +1468,6 @@ typedef struct SemaContext_
Expr *return_expr;
} SemaContext;
typedef struct
{
uint32_t count;
uint32_t capacity;
uint32_t max_load;
DeclId *entries;
} DeclTable;
typedef struct
{
@@ -1435,9 +1482,7 @@ typedef struct
bool in_test_mode : 1;
unsigned errors_found;
unsigned warnings_found;
char scratch_buffer[MAX_STRING_BUFFER];
Decl ***locals_list;
uint32_t scratch_buffer_len;
HTable compiler_defines;
Module std_module;
DeclTable symbols;
@@ -1546,6 +1591,7 @@ typedef struct
{
Decl *ambiguous_other_decl;
Decl *private_decl;
Decl *maybe_decl;
Path *path;
SourceSpan span;
const char *symbol;
@@ -1554,6 +1600,7 @@ typedef struct
} NameResolve;
extern GlobalContext global_context;
extern BuildTarget active_target;
extern Ast *poisoned_ast;
@@ -1575,16 +1622,24 @@ extern Type *type_complist;
extern Type *type_anyfail;
extern Type *type_cint;
extern Type *type_cuint;
extern Type *type_chars;
extern const char *attribute_list[NUMBER_OF_ATTRIBUTES];
extern const char *builtin_list[NUMBER_OF_BUILTINS];
extern const char *kw_std__core;
extern const char *kw_std__core__types;
extern const char *kw_typekind;
extern const char *kw_std;
extern const char *kw_max;
extern const char *kw_min;
extern const char *kw_elements;
extern const char *kw_align;
extern const char *kw_nameof;
extern const char *kw_names;
extern const char *kw_sizeof;
extern const char *kw_in;
extern const char *kw_out;
@@ -1593,6 +1648,8 @@ extern const char *kw_deprecated;
extern const char *kw_distinct;
extern const char *kw_inline;
extern const char *kw_inf;
extern const char *kw_kind;
extern const char *kw_inner;
extern const char *kw_elementat;
extern const char *kw_elementref;
extern const char *kw_elementset;
@@ -1600,6 +1657,8 @@ extern const char *kw_len;
extern const char *kw_nan;
extern const char *kw_noinline;
extern const char *kw_main;
extern const char *kw_max;
extern const char *kw_min;
extern const char *kw_ordinal;
extern const char *kw_pure;
extern const char *kw_ptr;
@@ -1615,6 +1674,13 @@ extern const char *kw_check_assign;
extern const char *kw_argc;
extern const char *kw_argv;
extern const char *kw_mainstub;
extern const char *kw_at_ensure;
extern const char *kw_at_require;
extern const char *kw_at_pure;
extern const char *kw_at_optreturn;
extern const char *kw_at_param;
extern const char *kw_at_return;
extern const char *kw_at_checked;
ARENA_DEF(chars, char)
ARENA_DEF(ast, Ast)
@@ -1660,6 +1726,8 @@ typedef enum CmpRes_
CMP_GT = 1,
} CmpRes;
AttributeType attribute_by_name(const char *name);
void type_setup(PlatformTarget *target);
Float float_add(Float op1, Float op2);
Float float_sub(Float op1, Float op2);
@@ -1872,6 +1940,7 @@ void expr_const_set_float(ExprConst *expr, Real d, TypeKind kind);
void expr_const_set_bool(ExprConst *expr, bool b);
void expr_const_set_null(ExprConst *expr);
bool expr_const_in_range(const ExprConst *left, const ExprConst *right, const ExprConst *right_to);
bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op);
bool expr_const_will_overflow(const ExprConst *expr, TypeKind kind);
@@ -1879,6 +1948,7 @@ Expr *expr_generate_decl(Decl *decl, Expr *assign);
void expr_insert_addr(Expr *original);
void expr_insert_deref(Expr *expr);
Expr *expr_variable(Decl *decl);
void expr_rewrite_to_builtin_access(SemaContext *context, Expr *expr, Expr *parent, BuiltinAccessKind kind, Type *type);
typedef enum
{
CONSTANT_EVAL_ANY,
@@ -1942,13 +2012,13 @@ bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement);
bool sema_analyse_statement(SemaContext *context, Ast *statement);
bool sema_expr_analyse_assign_right_side(SemaContext *context, Expr *expr, Type *left_type, Expr *right, bool is_unwrapped_var);
bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro, bool failable);
bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool failable);
Decl *sema_resolve_symbol_in_current_dynamic_scope(SemaContext *context, const char *symbol);
Decl *sema_find_decl_in_modules(Module **module_list, Path *path, const char *interned_name);
Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, NameResolve *name_resolve);
Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref);
Decl *sema_find_extension_method_in_module(Module *module, Type *type, const char *method_name);
Decl *sema_resolve_normal_symbol(SemaContext *context, NameResolve *name_resolve);
Decl *sema_find_symbol(SemaContext *context, const char *symbol);
Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path);
Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path, SourceSpan span);
@@ -1989,15 +2059,7 @@ void decltable_init(DeclTable *table, uint32_t initial_size);
DeclId decltable_get(DeclTable *table, const char *name);
void decltable_set(DeclTable *table, Decl *decl);
void scratch_buffer_clear(void);
void scratch_buffer_append(const char *string);
void scratch_buffer_append_len(const char *string, size_t len);
void scratch_buffer_append_char(char c);
void scratch_buffer_append_signed_int(int64_t i);
UNUSED void scratch_buffer_append_unsigned_int(uint64_t i);
char *scratch_buffer_to_string(void);
const char *scratch_buffer_interned(void);
char *scratch_buffer_copy(void);
const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
@@ -2005,22 +2067,27 @@ void *llvm_target_machine_create(void);
void target_setup(BuildTarget *build_target);
int target_alloca_addr_space();
const char *macos_sysroot(void);
MacSDK *macos_sysroot_sdk_information(const char *sdk_path);
WindowsSDK *windows_get_sdk(void);
void c_abi_func_create(FunctionPrototype *proto);
bool token_is_any_type(TokenType type);
const char *token_type_to_string(TokenType type);
void type_mangle_introspect_name_to_buffer(Type *type);
AlignSize type_abi_alignment(Type *type);
AlignSize type_alloca_alignment(Type *type);
static inline bool type_convert_will_trunc(Type *destination, Type *source);
bool type_is_comparable(Type *type);
bool type_is_ordered(Type *type);
unsigned type_get_introspection_kind(TypeKind kind);
Type *type_find_common_ancestor(Type *left, Type *right);
Type *type_find_largest_union_element(Type *type);
Type *type_find_max_type(Type *type, Type *other);
Type *type_find_max_type_may_fail(Type *type, Type *other);
Type *type_abi_find_single_struct_element(Type *type);
bool type_is_valid_for_vector(Type *type);
Type *type_get_array(Type *arr_type, ArraySize len);
@@ -2033,8 +2100,8 @@ Type *type_get_flexible_array(Type *arr_type);
Type *type_get_failable(Type *failable_type);
Type *type_get_vector(Type *vector_type, unsigned len);
Type *type_get_vector_bool(Type *original_type);
Type *type_int_signed_by_bitsize(unsigned bitsize);
Type *type_int_unsigned_by_bitsize(unsigned bytesize);
Type *type_int_signed_by_bitsize(BitSize bitsize);
Type *type_int_unsigned_by_bitsize(BitSize bit_size);
void type_init_cint(void);
void type_func_prototype_init(uint32_t capacity);
static inline bool type_is_builtin(TypeKind kind);
@@ -2464,6 +2531,7 @@ Expr *expr_macro_copy(Expr *source_expr);
Decl **decl_copy_list(Decl **decl_list);
Ast *ast_macro_copy(Ast *source_ast);
Ast *ast_defer_copy(Ast *source_ast);
Decl *decl_macro_copy(Decl *source_decl);
Expr **copy_expr_list(CopyStruct *c, Expr **expr_list);
Expr *copy_expr(CopyStruct *c, Expr *source_expr);
@@ -2486,6 +2554,7 @@ bool obj_format_linking_supported(ObjectFormatType format_type);
bool linker(const char *output_file, const char **files, unsigned file_count);
void platform_linker(const char *output_file, const char **files, unsigned file_count);
void platform_compiler(const char **files, unsigned file_count, const char* flags);
const char *arch_to_linker_arch(ArchType arch);
#define CAT(a,b) CAT2(a,b) // force expand
#define CAT2(a,b) a##b // actually concatenate
@@ -2495,6 +2564,7 @@ void platform_compiler(const char **files, unsigned file_count, const char* flag
#define ASSIGN_EXPR_OR_RET(_assign, _expr_stmt, _res) Expr* TEMP(_expr) = (_expr_stmt); if (!expr_ok(TEMP(_expr))) return _res; _assign = TEMP(_expr)
#define ASSIGN_EXPRID_OR_RET(_assign, _expr_stmt, _res) Expr* TEMP(_expr) = (_expr_stmt); if (!expr_ok(TEMP(_expr))) return _res; _assign = exprid(TEMP(_expr))
#define ASSIGN_TYPE_OR_RET(_assign, _type_stmt, _res) TypeInfo* TEMP(_type) = (_type_stmt); if (!type_info_ok(TEMP(_type))) return _res; _assign = TEMP(_type)
#define ASSIGN_TYPEID_OR_RET(_assign, _type_stmt, _res) TypeInfo* TEMP(_type) = (_type_stmt); if (!type_info_ok(TEMP(_type))) return _res; _assign = type_infoid(TEMP(_type))
#define ASSIGN_DECL_OR_RET(_assign, _decl_stmt, _res) Decl* TEMP(_decl) = (_decl_stmt); if (!decl_ok(TEMP(_decl))) return _res; _assign = TEMP(_decl)

View File

@@ -66,9 +66,9 @@ static bool filename_to_module_in_buffer(const char *path)
for (int i = last_slash + 1; i < last_dot; i++)
{
char c = path[i];
if (is_letter(c) || is_digit(c))
if (char_is_letter(c) || char_is_digit(c))
{
c = (char)(is_upper(c) ? c + 'a' - 'A' : c);
c = (char)(char_is_upper(c) ? c + 'a' - 'A' : c);
}
else
{
@@ -89,9 +89,9 @@ bool context_set_module_from_filename(ParseContext *context)
}
TokenType type = TOKEN_IDENT;
const char *module_name = symtab_add(global_context.scratch_buffer,
global_context.scratch_buffer_len,
fnv1a(global_context.scratch_buffer, (uint32_t) global_context.scratch_buffer_len),
const char *module_name = symtab_add(scratch_buffer.str,
scratch_buffer.len,
fnv1a(scratch_buffer.str, (uint32_t) scratch_buffer.len),
&type);
if (type != TOKEN_IDENT)
@@ -103,16 +103,16 @@ bool context_set_module_from_filename(ParseContext *context)
Path *path = CALLOCS(Path);
path->span = INVALID_SPAN;
path->module = module_name;
path->len = global_context.scratch_buffer_len;
path->len = scratch_buffer.len;
return create_module_or_check_name(context->unit, path, NULL, true);
}
bool context_set_module(ParseContext *context, Path *path, const char **generic_parameters, bool is_private)
{
// Note that we allow the illegal name for now, to be able to parse further.
if (!is_all_lower(path->module))
if (!str_has_no_uppercase(path->module))
{
SEMA_ERROR(path, "A module name may not have any upper case characters.");
SEMA_ERROR(path, "A module name may not have any uppercase characters.");
return false;
}
@@ -122,7 +122,7 @@ bool context_set_module(ParseContext *context, Path *path, const char **generic_
void unit_register_external_symbol(CompilationUnit *unit, Decl *decl)
{
if (!decl->module || decl->module == unit->module || !decl->extname) return;
if (!decl->unit || decl->unit->module == unit->module || !decl->extname) return;
decl->is_external_visible = true;
}
@@ -166,7 +166,9 @@ void decl_register(Decl *decl)
void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
{
decl->module = unit->module;
assert(!decl->unit || decl->unit->module->is_generic);
decl->unit = unit;
switch (decl->decl_kind)
{
case DECL_POISONED:
@@ -241,12 +243,15 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
decl_set_external_name(decl);
decl_register(decl);
break;
case DECL_ATTRIBUTE:
vec_add(unit->attributes, decl);
decl_register(decl);
break;
case DECL_FAULTVALUE:
case DECL_ENUM_CONSTANT:
case DECL_IMPORT:
case DECL_CT_ELSE:
case DECL_CT_ELIF:
case DECL_ATTRIBUTE:
case DECL_LABEL:
case DECL_CT_CASE:
case DECL_DECLARRAY:
@@ -277,10 +282,9 @@ bool unit_add_import(CompilationUnit *unit, Path *path, bool private_import)
{
DEBUG_LOG("SEMA: Add import of '%s'.", path->module);
if (!is_all_lower(path->module))
if (!str_has_no_uppercase(path->module))
{
SEMA_ERROR(path, "A module is not expected to have any upper case characters, please change it.");
SEMA_ERROR(path, "A module is not expected to have any uppercase characters, please change it.");
return false;
}

View File

@@ -148,6 +148,13 @@ Ast *ast_macro_copy(Ast *source_ast)
return ast_copy_deep(&copy_struct, source_ast);
}
Decl *decl_macro_copy(Decl *source_decl)
{
copy_struct.current_fixup = copy_struct.fixups;
copy_struct.single_static = false;
return copy_decl(&copy_struct, source_decl);
}
Ast *ast_defer_copy(Ast *source_ast)
{
copy_struct.current_fixup = copy_struct.fixups;
@@ -177,9 +184,20 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
case EXPR_BUILTIN:
case EXPR_RETVAL:
return expr;
case EXPR_BUILTIN_ACCESS:
MACRO_COPY_EXPRID(expr->builtin_access_expr.inner);
return expr;
case EXPR_CT_CONV:
MACRO_COPY_TYPEID(expr->ct_call_expr.type_from);
MACRO_COPY_TYPEID(expr->ct_call_expr.type_to);
return expr;
case EXPR_DECL:
MACRO_COPY_DECL(expr->decl_expr);
return expr;
case EXPR_VARIANT:
MACRO_COPY_EXPRID(expr->variant_expr.ptr);
MACRO_COPY_EXPRID(expr->variant_expr.type_id);
return expr;
case EXPR_CT_CALL:
MACRO_COPY_EXPR(expr->ct_call_expr.main_var);
return expr;
@@ -206,11 +224,6 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
return expr;
case EXPR_COMPILER_CONST:
return expr;
case EXPR_MACRO_EXPANSION:
SCOPE_FIXUP_START
MACRO_COPY_EXPR(expr->macro_expansion_expr.inner);
SCOPE_FIXUP_END;
return expr;
case EXPR_DESIGNATOR:
expr->designator_expr.path = macro_copy_designator_list(c, expr->designator_expr.path);
MACRO_COPY_EXPR(expr->designator_expr.value);
@@ -227,20 +240,18 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
MACRO_COPY_EXPRID(expr->slice_expr.start);
MACRO_COPY_EXPRID(expr->slice_expr.end);
return expr;
case EXPR_LEN:
MACRO_COPY_EXPR(expr->len_expr.inner);
return expr;
case EXPR_FORCE_UNWRAP:
case EXPR_TRY:
case EXPR_CATCH:
case EXPR_FAILABLE:
case EXPR_GROUP:
case EXPR_TYPEOFANY:
case EXPR_PTR:
case EXPR_STRINGIFY:
case EXPR_CT_EVAL:
MACRO_COPY_EXPR(expr->inner_expr);
return expr;
case EXPR_TYPEID_INFO:
MACRO_COPY_EXPRID(expr->typeid_info_expr.parent);
return expr;
case EXPR_COND:
MACRO_COPY_EXPR_LIST(expr->cond_expr);
return expr;
@@ -545,6 +556,7 @@ static void copy_function_signature_deep(CopyStruct *c, FunctionSignature *signa
void copy_decl_type(Decl *decl)
{
Type *type = decl->type;
if (!type) return;
Type *copy = type_new(type->type_kind, type->name);
*copy = *type;
copy->type_cache = NULL;
@@ -562,7 +574,7 @@ static Attr **copy_attributes(CopyStruct *c, Attr** attr_list)
Attr *attribute = attr_list[i];
Attr *copy = MALLOCS(Attr);
*copy = *attribute;
MACRO_COPY_EXPR(copy->expr);
MACRO_COPY_EXPR_LIST(copy->exprs);
vec_add(list, copy);
}
return list;
@@ -597,10 +609,16 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
MACRO_COPY_DECL_LIST(copy->methods);
break;
case DECL_DECLARRAY:
case DECL_BITSTRUCT:
UNREACHABLE
case DECL_BITSTRUCT:
copy_decl_type(copy);
MACRO_COPY_DECL_LIST(copy->bitstruct.members);
MACRO_COPY_TYPE(copy->bitstruct.base_type);
MACRO_COPY_DECL_LIST(copy->methods);
MACRO_COPY_DECL_LIST(copy->methods);
break;
case DECL_ENUM:
case DECL_FAULT:
case DECL_FAULT:
copy_decl_type(copy);
MACRO_COPY_DECL_LIST(copy->methods);
MACRO_COPY_DECL_LIST(copy->enums.parameters);
@@ -608,6 +626,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
MACRO_COPY_DECL_LIST(copy->enums.values);
break;
case DECL_FUNC:
copy_decl_type(copy);
MACRO_COPY_TYPEID(copy->func_decl.type_parent);
MACRO_COPY_ASTID(copy->func_decl.docs);
copy_function_signature_deep(c, &copy->func_decl.function_signature);
@@ -628,14 +647,12 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
// Note that the ast id should be patched by the parent.
return copy;
case DECL_ENUM_CONSTANT:
MACRO_COPY_EXPR(copy->enum_constant.expr);
MACRO_COPY_EXPR_LIST(copy->enum_constant.args);
break;
case DECL_FAULTVALUE:
MACRO_COPY_EXPR(copy->enum_constant.expr);
MACRO_COPY_EXPR_LIST(copy->enum_constant.args);
break;
case DECL_TYPEDEF:
copy_decl_type(copy);
if (copy->typedef_decl.is_func)
{
copy_function_signature_deep(c, &copy->typedef_decl.function_signature);
@@ -644,6 +661,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
MACRO_COPY_TYPE(copy->typedef_decl.type_info);
break;
case DECL_DISTINCT:
copy_decl_type(copy);
MACRO_COPY_DECL_LIST(copy->methods);
if (copy->distinct_decl.typedef_decl.is_func)
{
@@ -699,10 +717,6 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
break;
case DEFINE_IDENT_ALIAS:
break;
case DEFINE_ATTRIBUTE:
decl->define_decl.attributes.attrs = copy_attributes(c, decl->define_decl.attributes.attrs);
MACRO_COPY_DECL_LIST(decl->define_decl.attributes.params);
break;
}
break;
}

View File

@@ -35,7 +35,7 @@ static inline void decltable_resize(DeclTable *table)
*dest = id;
}
table->entries = new_data;
table->max_load = new_capacity * TABLE_MAX_LOAD;
table->max_load = (uint32_t)(new_capacity * TABLE_MAX_LOAD);
table->capacity = new_capacity;
}
@@ -87,6 +87,6 @@ void decltable_init(DeclTable *table, uint32_t initial_size)
DeclId *entries = CALLOC(initial_size * sizeof(DeclId));
table->count = 0;
table->capacity = initial_size;
table->max_load = initial_size * TABLE_MAX_LOAD;
table->max_load = (uint32_t)(initial_size * TABLE_MAX_LOAD);
table->entries = entries;
}

View File

@@ -66,7 +66,7 @@ static void print_error(SourceSpan location, const char *message, PrintType prin
{
current += row_len + 1;
row_len = 0;
while (current[row_len] != '\n') row_len++;
while (current[row_len] != '\n' && current[row_len]) row_len++;
if (row_len > max_lines_for_display)
{
eprintf(number_buffer_elided, row, max_lines_for_display - 1, current);
@@ -148,15 +148,7 @@ static void print_error(SourceSpan location, const char *message, PrintType prin
static void vprint_error(SourceSpan location, const char *message, va_list args)
{
#define MAX_ERROR_LEN 4096
char buffer[MAX_ERROR_LEN];
size_t written = vsnprintf(buffer, MAX_ERROR_LEN - 1, message, args);
if (written > MAX_ERROR_LEN - 2)
{
print_error(location, "<Error message was too long>", PRINT_TYPE_ERROR);
return;
}
print_error(location, buffer, PRINT_TYPE_ERROR);
print_error(location, str_vprintf(message, args), PRINT_TYPE_ERROR);
}
@@ -251,7 +243,7 @@ const char *span_to_string(SourceSpan span)
}
assert(row == row_to_find);
const char *start = current + col - 1;
return copy_string(start, length);
return str_copy(start, length);
}

View File

@@ -168,6 +168,31 @@ typedef enum
DOC_DIRECTIVE_ENSURE,
} DocDirectiveKind;
typedef enum
{
INTROSPECT_TYPE_VOID = 0,
INTROSPECT_TYPE_BOOL = 1,
INTROSPECT_TYPE_SIGNED_INT = 2,
INTROSPECT_TYPE_UNSIGNED_INT = 3,
INTROSPECT_TYPE_FLOAT = 4,
INTROSPECT_TYPE_TYPEID = 5,
INTROSPECT_TYPE_ANYERR = 6,
INTROSPECT_TYPE_ANY = 7,
INTROSPECT_TYPE_ENUM = 8,
INTROSPECT_TYPE_FAULT = 9,
INTROSPECT_TYPE_STRUCT = 10,
INTROSPECT_TYPE_UNION = 11,
INTROSPECT_TYPE_BITSTRUCT = 12,
INTROSPECT_TYPE_FUNC = 13,
INTROSPECT_TYPE_FAILABLE = 14,
INTROSPECT_TYPE_ARRAY = 15,
INTROSPECT_TYPE_SUBARRAY = 16,
INTROSPECT_TYPE_VECTOR = 17,
INTROSPECT_TYPE_DISTINCT = 18,
INTROSPECT_TYPE_POINTER = 19,
INTROSPECT_TYPE_VARIANT = 20,
} IntrospectType;
typedef enum
{
EXPR_POISONED,
@@ -185,6 +210,7 @@ typedef enum
EXPR_COMPOUND_LITERAL,
EXPR_CONST,
EXPR_CT_CALL,
EXPR_CT_CONV,
EXPR_CT_IDENT,
EXPR_CT_EVAL,
EXPR_COND,
@@ -198,14 +224,11 @@ typedef enum
EXPR_FORCE_UNWRAP,
EXPR_HASH_IDENT,
EXPR_MACRO_BLOCK,
EXPR_MACRO_EXPANSION,
EXPR_IDENTIFIER,
EXPR_RETVAL,
EXPR_FLATPATH,
EXPR_INITIALIZER_LIST,
EXPR_DESIGNATED_INITIALIZER_LIST,
EXPR_LEN,
EXPR_PTR,
EXPR_POST_UNARY,
EXPR_SLICE,
EXPR_SLICE_ASSIGN,
@@ -218,13 +241,26 @@ typedef enum
EXPR_TRY_UNWRAP,
EXPR_TRY_UNWRAP_CHAIN,
EXPR_TYPEID,
EXPR_TYPEOFANY,
EXPR_TYPEINFO,
EXPR_UNARY,
EXPR_VARIANTSWITCH,
EXPR_NOP,
EXPR_TYPEID_INFO,
EXPR_VARIANT,
EXPR_BUILTIN_ACCESS,
} ExprKind;
typedef enum
{
TYPEID_INFO_KIND,
TYPEID_INFO_MIN,
TYPEID_INFO_MAX,
TYPEID_INFO_INNER,
TYPEID_INFO_LEN,
TYPEID_INFO_SIZEOF,
TYPEID_INFO_NAMES,
} TypeIdInfoKind;
typedef enum
{
CONST_FLOAT,
@@ -393,7 +429,7 @@ typedef enum
// Literals.
TOKEN_IDENT, // Any normal ident.
TOKEN_CONST_IDENT, // Any purely upper case ident,
TOKEN_CONST_IDENT, // Any purely uppercase ident,
TOKEN_TYPE_IDENT, // Any ident on the format FooBar or __FooBar
// Asm
@@ -411,13 +447,16 @@ typedef enum
TOKEN_HASH_CONST_IDENT, // #FOOBAR
TOKEN_HASH_TYPE_IDENT, // #Foobar
TOKEN_AT_IDENT, // @macro
TOKEN_AT_CONST_IDENT, // @MACRO
TOKEN_AT_TYPE_IDENT, // @Macro
TOKEN_STRING, // "Teststring"
TOKEN_INTEGER, // 123 0x23 0b10010 0o327
TOKEN_CHAR_LITERAL, // 'a' 'FO' 'BARS' '\u1232'
TOKEN_REAL, // 0x23.2p-2a 43.23e23
TOKEN_BYTES, // Base64 or Hex
TOKEN_DOC_DIRECTIVE, // Doc Directive
TOKEN_DOC_COMMENT, // Doc Comment start
// Keywords
@@ -443,7 +482,6 @@ typedef enum
TOKEN_FOR,
TOKEN_FOREACH,
TOKEN_FN,
TOKEN_FUNC,
TOKEN_GENERIC,
TOKEN_TLOCAL,
TOKEN_IF,
@@ -487,17 +525,12 @@ typedef enum
TOKEN_CT_STRINGIFY, // $stringify
TOKEN_CT_SWITCH, // $switch
TOKEN_CT_TYPEOF, // $typeof
TOKEN_CT_CONVERTIBLE, // $convertible
TOKEN_CT_CASTABLE, // $castable
TOKEN_DOCS_START, // /**
TOKEN_DOCS_END, // */ (may start with an arbitrary number of `*`
TOKEN_DOCS_ENSURE, // @ensure
TOKEN_DOCS_REQUIRE, // @require
TOKEN_DOCS_CHECKED, // @checked
TOKEN_DOCS_PARAM, // @param
TOKEN_DOCS_RETURN, // @return
TOKEN_DOCS_OPTRETURN, // @optreturn
TOKEN_DOCS_PURE, // @pure
TOKEN_DOC_DIRECTIVE, // Any doc directive
TOKEN_EOF, // \n - SHOULD ALWAYS BE THE LAST TOKEN.
@@ -642,7 +675,7 @@ typedef enum
{
OVERLOAD_ELEMENT_AT = 1,
OVERLOAD_ELEMENT_REF,
OVERLOAT_ELEMENT_SET,
OVERLOAD_ELEMENT_SET,
OVERLOAD_LEN
} OperatorOverload;
@@ -667,8 +700,13 @@ typedef enum
ATTRIBUTE_REGCALL,
ATTRIBUTE_FASTCALL,
ATTRIBUTE_OVERLAP,
ATTRIBUTE_AUTOIMPORT,
ATTRIBUTE_BUILTIN,
ATTRIBUTE_OPERATOR,
ATTRIBUTE_PURE,
ATTRIBUTE_REFLECT,
ATTRIBUTE_OBFUSCATE,
ATTRIBUTE_MAYDISCARD,
ATTRIBUTE_NODISCARD,
ATTRIBUTE_NONE,
NUMBER_OF_ATTRIBUTES = ATTRIBUTE_NONE,
} AttributeType;
@@ -689,6 +727,8 @@ typedef enum
typedef enum
{
ANALYSIS_NOT_BEGUN,
ANALYSIS_MODULE_HIERARCHY,
ANALYSIS_MODULE_TOP,
ANALYSIS_IMPORTS,
ANALYSIS_REGISTER_GLOBALS,
ANALYSIS_CONDITIONAL_COMPILATION,

View File

@@ -178,7 +178,7 @@ Float float_from_string(const char *string, char **error)
if (error) *error = err_float_out_of_range;
return (Float){ .type = TYPE_POISONED };
}
char *expected_end = global_context.scratch_buffer + global_context.scratch_buffer_len;
char *expected_end = scratch_buffer.str + scratch_buffer.len;
if (d == 0 && end != expected_end)
{
if (error) *error = err_float_format_invalid;
@@ -200,7 +200,7 @@ Float float_from_hex(const char *string, char **error)
char c;
scratch_buffer_clear();
scratch_buffer_append("0x");
while ((c = *(index++)) && (c == '_' || is_hex(c)))
while ((c = *(index++)) && (c == '_' || char_is_hex(c)))
{
if (c == '_') continue;
scratch_buffer_append_char(c);
@@ -208,7 +208,7 @@ Float float_from_hex(const char *string, char **error)
if (c == '.')
{
scratch_buffer_append_char(c);
while ((c = *(index++)) && (c == '_' || is_hex(c)))
while ((c = *(index++)) && (c == '_' || char_is_hex(c)))
{
if (c == '_') continue;
scratch_buffer_append_char(c);
@@ -271,7 +271,7 @@ Float float_from_hex(const char *string, char **error)
if (error) *error = err_float_out_of_range;
return (Float){ .type = TYPE_POISONED };
}
if (d == 0 && end != global_context.scratch_buffer + global_context.scratch_buffer_len)
if (d == 0 && end != scratch_buffer.str + scratch_buffer.len)
{
if (error) *error = err_float_format_invalid;
return (Float){ .type = TYPE_POISONED };

View File

@@ -227,7 +227,7 @@ void header_gen(Module *module)
{
TODO
CompilationUnit *unit = module->units[0];
const char *filename = strcat_arena(unit->file->name, ".h");
const char *filename = str_cat(unit->file->name, ".h");
FILE *file = fopen(filename, "w");
OUTPUT("#include <stdint.h>\n");
OUTPUT("#ifndef __c3__\n");

View File

@@ -378,7 +378,7 @@ static inline bool scan_ident(Lexer *lexer, TokenType normal, TokenType const_to
static bool scan_number_suffix(Lexer *lexer, bool *is_float)
{
char c = peek(lexer);
if (!is_alphanum_(c)) return true;
if (!char_is_alphanum_(c)) return true;
switch (c)
{
case 'u':
@@ -390,17 +390,17 @@ static bool scan_number_suffix(Lexer *lexer, bool *is_float)
return add_error_token_at_current(lexer, "Integer suffix '%c' is not valid for a floating point literal.", c);
}
next(lexer);
while (is_number(c = peek(lexer))) next(lexer);
while (char_is_digit(c = peek(lexer))) next(lexer);
break;
case 'f':
next(lexer);
*is_float = true;
while (is_number(c = peek(lexer))) next(lexer);
while (char_is_digit(c = peek(lexer))) next(lexer);
break;
default:
break;
}
if (is_alphanum_(c))
if (char_is_alphanum_(c))
{
next(lexer);
return add_error_token(lexer, "This doesn't seem to be a valid literal.");
@@ -414,13 +414,13 @@ static bool scan_number_suffix(Lexer *lexer, bool *is_float)
*/
static bool scan_oct(Lexer *lexer)
{
if (!is_oct(peek(lexer)))
if (!char_is_oct(peek(lexer)))
{
return add_error_token_at_current(lexer, "An expression starting with '0o' should be followed by octal numbers (0-7).");
}
next(lexer);
while (is_oct_or_(peek(lexer))) next(lexer);
if (is_number(peek(lexer)))
while (char_is_oct_or_(peek(lexer))) next(lexer);
if (char_is_digit(peek(lexer)))
{
return add_error_token_at_current(lexer, "An expression starting with '0o' should be followed by octal numbers (0-7).");
}
@@ -438,13 +438,13 @@ static bool scan_oct(Lexer *lexer)
**/
static bool scan_binary(Lexer *lexer)
{
if (!is_binary(peek(lexer)))
if (!char_is_binary(peek(lexer)))
{
return add_error_token_at_current(lexer, "An expression starting with '0b' should be followed by binary digits (0-1).");
}
next(lexer);
while (is_binary_or_(peek(lexer))) next(lexer);
if (is_number(peek((lexer))))
while (char_is_binary_or_(peek(lexer))) next(lexer);
if (char_is_digit(peek((lexer))))
{
return add_error_token_at_current(lexer, "An expression starting with '0b' should be followed by binary digits (0-1).");
}
@@ -475,7 +475,7 @@ static inline bool scan_exponent(Lexer *lexer)
next(lexer);
}
// Now we need at least one digit
if (!is_digit(c))
if (!char_is_digit(c))
{
if (c == 0)
{
@@ -487,7 +487,7 @@ static inline bool scan_exponent(Lexer *lexer)
return add_error_token(lexer, "Parsing the floating point exponent failed, because '%c' is not a number.", c);
}
// Walk through all of the digits.
while (is_digit(peek(lexer))) next(lexer);
while (char_is_digit(peek(lexer))) next(lexer);
return true;
}
@@ -497,12 +497,12 @@ static inline bool scan_exponent(Lexer *lexer)
**/
static inline bool scan_hex(Lexer *lexer)
{
if (!is_hex(peek(lexer)))
if (!char_is_hex(peek(lexer)))
{
return add_error_token_at_current(lexer, "'0x' starts a hexadecimal number, so the next character should be 0-9, a-f or A-F.");
}
next(lexer);
while (is_hex_or_(peek(lexer))) next(lexer);
while (char_is_hex_or_(peek(lexer))) next(lexer);
bool is_float = false;
if (peek(lexer) == '.' && peek_next(lexer) != '.')
{
@@ -510,8 +510,8 @@ static inline bool scan_hex(Lexer *lexer)
next(lexer);
char c = peek(lexer);
if (c == '_') return add_error_token_at_current(lexer, "'_' is not allowed directly after decimal point, try removing it.");
if (is_hex(c)) next(lexer);
while (is_hex_or_(peek(lexer))) next(lexer);
if (char_is_hex(c)) next(lexer);
while (char_is_hex_or_(peek(lexer))) next(lexer);
}
char c = peek(lexer);
if (c == 'p' || c == 'P')
@@ -533,11 +533,11 @@ static inline bool scan_hex(Lexer *lexer)
*/
static inline bool scan_dec(Lexer *lexer)
{
assert(is_digit(peek(lexer)));
assert(char_is_digit(peek(lexer)));
// Walk through the digits, we don't need to worry about
// initial _ because we only call this if we have a digit initially.
while (is_digit_or_(peek(lexer))) next(lexer);
while (char_is_digit_or_(peek(lexer))) next(lexer);
// Assume no float.
bool is_float = false;
@@ -555,7 +555,7 @@ static inline bool scan_dec(Lexer *lexer)
if (c == '_') return add_error_token_at_current(lexer, "'_' is not allowed directly after decimal point, try removing it.");
// Now walk until we see no more digits.
// This allows 123. as a floating point number.
while (is_digit_or_(peek(lexer))) next(lexer);
while (char_is_digit_or_(peek(lexer))) next(lexer);
}
char c = peek(lexer);
// We might have an exponential. We allow 123e1 and 123.e1 as floating point, so
@@ -619,7 +619,7 @@ static inline int64_t scan_hex_literal(Lexer *lexer, int positions)
for (int j = 0; j < positions; j++)
{
hex <<= 4U;
int i = char_to_nibble(peek(lexer));
int i = char_hex_to_nibble(peek(lexer));
if (i < 0)
{
return -1;
@@ -739,7 +739,7 @@ static inline bool scan_char(Lexer *lexer)
{
assert(c == '\\');
c = peek(lexer);
escape = is_valid_escape(c);
escape = char_is_valid_escape(c);
if (escape == -1)
{
lexer->lexing_start += 1;
@@ -826,15 +826,15 @@ static int append_esc_string_token(char *restrict dest, const char *restrict src
{
int scanned;
uint64_t unicode_char;
signed char scanned_char = is_valid_escape(src[0]);
signed char scanned_char = char_is_valid_escape(src[0]);
if (scanned_char < 0) return -1;
switch (scanned_char)
{
case 'x':
{
int h = char_to_nibble(src[1]);
int h = char_hex_to_nibble(src[1]);
if (h < 0) return -1;
int l = char_to_nibble(src[2]);
int l = char_hex_to_nibble(src[2]);
if (l < 0) return -1;
unicode_char = ((unsigned) h << 4U) + (unsigned)l;
scanned = 3;
@@ -842,13 +842,13 @@ static int append_esc_string_token(char *restrict dest, const char *restrict src
}
case 'u':
{
int x1 = char_to_nibble(src[1]);
int x1 = char_hex_to_nibble(src[1]);
if (x1 < 0) return -1;
int x2 = char_to_nibble(src[2]);
int x2 = char_hex_to_nibble(src[2]);
if (x2 < 0) return -1;
int x3 = char_to_nibble(src[3]);
int x3 = char_hex_to_nibble(src[3]);
if (x3 < 0) return -1;
int x4 = char_to_nibble(src[4]);
int x4 = char_hex_to_nibble(src[4]);
if (x4 < 0) return -1;
unicode_char = ((unsigned) x1 << 12U) + ((unsigned) x2 << 8U) + ((unsigned) x3 << 4U) + (unsigned)x4;
scanned = 5;
@@ -856,21 +856,21 @@ static int append_esc_string_token(char *restrict dest, const char *restrict src
}
case 'U':
{
int x1 = char_to_nibble(src[1]);
int x1 = char_hex_to_nibble(src[1]);
if (x1 < 0) return -1;
int x2 = char_to_nibble(src[2]);
int x2 = char_hex_to_nibble(src[2]);
if (x2 < 0) return -1;
int x3 = char_to_nibble(src[3]);
int x3 = char_hex_to_nibble(src[3]);
if (x3 < 0) return -1;
int x4 = char_to_nibble(src[4]);
int x4 = char_hex_to_nibble(src[4]);
if (x4 < 0) return -1;
int x5 = char_to_nibble(src[5]);
int x5 = char_hex_to_nibble(src[5]);
if (x5 < 0) return -1;
int x6 = char_to_nibble(src[6]);
int x6 = char_hex_to_nibble(src[6]);
if (x6 < 0) return -1;
int x7 = char_to_nibble(src[7]);
int x7 = char_hex_to_nibble(src[7]);
if (x7 < 0) return -1;
int x8 = char_to_nibble(src[8]);
int x8 = char_hex_to_nibble(src[8]);
if (x8 < 0) return -1;
unicode_char = ((unsigned) x1 << 28U) + ((unsigned) x2 << 24U) + ((unsigned) x3 << 20U) + ((unsigned) x4 << 16U) +
((unsigned) x5 << 12U) + ((unsigned) x6 << 8U) + ((unsigned) x7 << 4U) + (unsigned)x8;
@@ -1031,13 +1031,13 @@ static inline bool scan_hex_array(Lexer *lexer)
return add_error_token_at_current(lexer, "The hex string seems to be missing a terminating '%c'", start_char);
}
if (c == start_char) break;
if (is_hex(c))
if (char_is_hex(c))
{
next(lexer);
len++;
continue;
}
if (is_whitespace(c))
if (char_is_whitespace(c))
{
next(lexer);
continue;
@@ -1080,7 +1080,7 @@ static inline bool scan_base64(Lexer *lexer)
}
next(lexer);
if (c == start_char) break;
if (is_base64(c))
if (char_is_base64(c))
{
if (end_len)
{
@@ -1098,7 +1098,7 @@ static inline bool scan_base64(Lexer *lexer)
end_len++;
continue;
}
if (!is_whitespace(c))
if (!char_is_whitespace(c))
{
if (c < ' ' || c > 127)
{
@@ -1160,33 +1160,23 @@ INLINE void skip_to_doc_line_end(Lexer *lexer)
}
static bool parse_doc_directive(Lexer *lexer)
static bool lex_doc_directive(Lexer *lexer)
{
begin_new_token(lexer);
next(lexer);
// Then our keyword
if (!scan_ident(lexer, TOKEN_IDENT, TOKEN_CONST, TOKEN_TYPE_IDENT, '@'))
if (!scan_ident(lexer, TOKEN_AT_IDENT, TOKEN_AT_CONST_IDENT, TOKEN_AT_TYPE_IDENT, '@'))
{
return false;
}
switch (lexer->token_type)
if (lexer->token_type != TOKEN_AT_IDENT)
{
case TOKEN_DOCS_ENSURE:
case TOKEN_DOCS_CHECKED:
case TOKEN_DOCS_REQUIRE:
case TOKEN_DOCS_OPTRETURN:
case TOKEN_DOCS_PARAM:
case TOKEN_DOCS_RETURN:
case TOKEN_DOCS_PURE:
return true;
case TOKEN_IDENT:
lexer->token_type = TOKEN_DOC_DIRECTIVE;
return true;
default:
add_error_token_at_current(lexer, "A doc directive was expected.");
return false;
add_error_token_at_current(lexer, "A doc directive was expected.");
return false;
}
lexer->token_type = TOKEN_DOC_DIRECTIVE;
return true;
}
static bool scan_doc_line(Lexer *lexer)
@@ -1224,7 +1214,7 @@ RETRY:;
// If we have '@' [_A-Za-z] then parse the directive
if (c == '@')
{
return parse_doc_directive(lexer);
return lex_doc_directive(lexer);
}
// Otherwise scan to the end of the line
@@ -1265,51 +1255,6 @@ static bool parse_doc_start(Lexer *lexer)
return true;
}
// --- Lexer public functions
// This works because everything would be an expression
/**
* @require foo > 102, "Foo needs to be at least 103"
* @require abc != NULL, "abc must be a valid pointer"
* @param [inout] foo, "The foo parameter"
*/
/**
* @require foo > 102 *Foo needs to be at least 103*
* @require abc != NULL --> abc must be a valid pointer
*/
// This works because one could require everything's inside
// of the ()
/**
* @require (foo > 102) Foo needs to be at least 103
* @require (abc != NULL) abc must be a valid pointer
* @param [inout] foo The foo parameter
*/
/**
* @require foo > 102 // Foo needs to be at least 103
* @require abc != NULL // abc must be a valid pointer
* @param [inout] foo // The foo parameter
*/
/**
* @require foo > 102 `Foo needs to be at least 103`
* @require abc != NULL `abc must be a valid pointer`
* @param [inout] foo `The foo parameter`
*/
// This works because // could behave differently in docs.
/**
* @require foo > 102 `Foo needs to be at least 103`
* @require abc != NULL // abc must be a valid pointer
*/
static bool lexer_scan_token_inner(Lexer *lexer)
{
// Now skip the whitespace.
@@ -1330,6 +1275,10 @@ static bool lexer_scan_token_inner(Lexer *lexer)
case '\n':
return scan_doc_line(lexer);
case '@':
if (char_is_letter(peek(lexer)))
{
return scan_ident(lexer, TOKEN_AT_IDENT, TOKEN_AT_CONST_IDENT, TOKEN_AT_TYPE_IDENT, '@');
}
return return_token(lexer, TOKEN_AT, "@");
case '\'':
return scan_char(lexer);
@@ -1342,7 +1291,7 @@ static bool lexer_scan_token_inner(Lexer *lexer)
case '$':
if (match(lexer, '$'))
{
if (is_letter(peek(lexer)))
if (char_is_letter(peek(lexer)))
{
return return_token(lexer, TOKEN_BUILTIN, "$$");
}

190
src/compiler/libraries.c Normal file
View File

@@ -0,0 +1,190 @@
#include "compiler_internal.h"
#include "../utils/json.h"
#define MANIFEST_FILE "manifest.json"
static inline JSONObject *get_mandatory(Library *library, JSONObject *object, const char *key)
{
JSONObject *value = json_obj_get(object, key);
if (!value) error_exit("The mandatory '%s' field was missing in '%s'.", library->dir);
return value;
}
static inline const char *get_mandatory_string(Library *library, JSONObject *object, const char *key)
{
JSONObject *value = get_mandatory(library, object, key);
if (value->type != J_STRING) error_exit("Expected string value for '%s' in '%s'.", library->dir);
return value->str;
}
static inline JSONObject *get_optional_string_array(Library *library, JSONObject *object, const char *key)
{
JSONObject *value = json_obj_get(object, key);
if (!value) return NULL;
if (value->type != J_ARRAY) error_exit("Expected an array value for '%s' in '%s'.", library->dir);
for (int i = 0; i < value->array_len; i++)
{
JSONObject *val = value->elements[i];
if (val->type != J_STRING) error_exit("Expected only strings in array '%s' in '%s'.", library->dir);
}
return value;
}
static inline const char **get_optional_string_array_as_array(Library *library, JSONObject *object, const char *key)
{
JSONObject *array = get_optional_string_array(library, object, key);
if (!array || !array->array_len) return NULL;
const char **array_result = VECNEW(const char*, array->array_len);
for (size_t i = 0; i < array->array_len; i++)
{
vec_add(array_result, array->elements[i]->str);
}
return array_result;
}
static inline void parse_provides(Library *library, JSONObject *object)
{
const char *provides = get_mandatory_string(library, object, "provides");
if (!str_is_valid_lowercase_name(provides))
{
char *res = strdup(provides);
str_ellide_in_place(res, 32);
error_exit("Invalid 'provides' module name in %s, was '%s'.", library->dir, json_obj_get(object, "provides")->str);
}
library->provides = provides;
}
static inline void parse_depends(Library *library, JSONObject *object)
{
JSONObject *depends = get_optional_string_array(library, object, "depends");
if (!depends) return;
TODO
}
static inline void parse_library_target(Library *library, LibraryTarget *target, JSONObject *object)
{
target->link_flags = get_optional_string_array_as_array(library, object, "linkflags");
target->linked_libs = get_optional_string_array_as_array(library, object, "linked-libs");
target->depends = get_optional_string_array_as_array(library, object, "depends");
}
static inline void parse_library_type(Library *library, LibraryTarget ***target_group, JSONObject *object)
{
if (!object) return;
if (object->type != J_OBJECT) error_exit("Expected a set of targets in %s.", library->dir);
for (size_t i = 0; i < object->member_len; i++)
{
JSONObject *member = object->members[i];
const char *key = object->keys[i];
if (member->type != J_OBJECT) error_exit("Expected a list of properties for a target in %s.", library->dir);
LibraryTarget *library_target = CALLOCS(LibraryTarget);
ArchOsTarget target = arch_os_target_from_string(key);
if (target == ARCH_OS_TARGET_DEFAULT)
{
error_exit("Invalid arch/os '%s' in %s.", key, library->dir);
}
library_target->arch_os = target;
vec_add(*target_group, library_target);
parse_library_target(library, library_target, member);
}
}
static Library *add_library(JSONObject *object, const char *dir)
{
Library *library = CALLOCS(Library);
library->dir = dir;
parse_provides(library, object);
parse_depends(library, object);
parse_library_type(library, &library->targets, json_obj_get(object, "targets"));
return library;
}
static Library *find_library(Library **libs, size_t lib_count, const char *name)
{
for (size_t i = 0; i < lib_count; i++)
{
if (strcmp(libs[i]->provides, name) == 0)
{
return libs[i];
break;
}
}
error_exit("Required library '%s' could not be found.\n", name);
}
static void add_library_dependency(Library *library, Library **library_list, size_t lib_count)
{
if (library->target_used) return;
LibraryTarget *target_found = NULL;
VECEACH(library->targets, j)
{
LibraryTarget *target = library->targets[j];
if (target->arch_os == active_target.arch_os_target)
{
target_found = target;
break;
}
}
if (!target_found)
{
error_exit("Library '%s' cannot be used with arch/os '%s'.", library->provides, arch_os_target[active_target.arch_os_target]);
}
library->target_used = target_found;
VECEACH(library->depends, i)
{
add_library_dependency(find_library(library_list, lib_count, library->depends[i]), library_list, lib_count);
}
VECEACH(target_found->depends, i)
{
add_library_dependency(find_library(library_list, lib_count, target_found->depends[i]),
library_list,
lib_count);
}
}
void resolve_libraries(void)
{
static const char *c3lib_suffix = ".c3l";
const char **c3_libs = NULL;
VECEACH(active_target.libdirs, i)
{
file_add_wildcard_files(&c3_libs, active_target.libdirs[i], false, &c3lib_suffix, 1);
}
JsonParser parser;
Library *libraries[MAX_LIB_DIRS * 2];
size_t lib_count = 0;
VECEACH(c3_libs, i)
{
size_t size;
const char *lib = c3_libs[i];
if (!file_is_dir(lib))
{
error_exit("Packaged .c3l are not supported yet.");
}
const char *manifest_path = file_append_path(lib, MANIFEST_FILE);
char *read = file_read_all(manifest_path, &size);
json_init_string(&parser, read, &malloc_arena);
JSONObject *json = json_parse(&parser);
if (parser.error_message)
{
error_exit("Error on line %d reading '%s':'%s'", parser.line, manifest_path, parser.error_message);
}
if (lib_count == MAX_LIB_DIRS * 2) error_exit("Too many libraries added, exceeded %d.", MAX_LIB_DIRS * 2);
libraries[lib_count++] = add_library(json, lib);
}
VECEACH(active_target.libs, i)
{
const char *lib_name = active_target.libs[i];
add_library_dependency(find_library(libraries, lib_count, lib_name), libraries, lib_count);
}
for (size_t i = 0; i < lib_count; i++)
{
Library *library = libraries[i];
LibraryTarget *target = library->target_used;
if (!target) continue;
file_add_wildcard_files(&active_target.sources, library->dir, false, c3_suffix_list, 3);
vec_add(active_target.library_list, library);
vec_add(active_target.linker_libdirs, file_append_path(library->dir, arch_os_target[active_target.arch_os_target]));
}
}

View File

@@ -2,54 +2,198 @@
#include <llvm/Config/llvm-config.h> // for LLVM_VERSION_STRING
#ifdef PLATFORM_WINDOWS
#include "utils/find_msvc.h"
#endif
extern bool llvm_link_elf(const char **args, int arg_count, const char **error_string);
extern bool llvm_link_macho(const char **args, int arg_count, const char **error_string);
extern bool llvm_link_coff(const char **args, int arg_count, const char **error_string);
extern bool llvm_link_wasm(const char **args, int arg_count, const char **error_string);
extern bool llvm_link_mingw(const char **args, int arg_count, const char **error_string);
static void add_files(const char ***args, const char **files_to_link, unsigned file_count)
typedef enum
{
for (unsigned i = 0; i < file_count; i++)
{
vec_add(*args, files_to_link[i]);
}
LINKER_LINK_EXE,
LINKER_LD,
LINKER_LD64,
LINKER_WASM,
LINKER_CC,
} LinkerType;
#define add_arg(arg_) vec_add(*args_ref, (arg_))
#define add_arg2(arg_, arg_2) vec_add(*args_ref, str_cat((arg_), (arg_2)))
static inline bool is_no_pie(RelocModel reloc)
{
return reloc == RELOC_NONE;
}
static inline bool is_pie(RelocModel reloc)
{
return reloc == RELOC_BIG_PIE || reloc == RELOC_BIG_PIE;
}
static const char *join_strings(const char **args, unsigned count)
static const char *ld_target(ArchType arch_type)
{
char *res = "";
for (unsigned i = 0; i < count; ++i)
switch (platform_target.arch)
{
res = strcat_arena(res, args[i]);
case ARCH_TYPE_X86_64:
return "elf_x86_64";
case ARCH_TYPE_X86:
return "elf_i386";
case ARCH_TYPE_AARCH64:
return "aarch64elf";
case ARCH_TYPE_RISCV32:
return "elf32lriscv";
case ARCH_TYPE_RISCV64:
return "elf64lriscv";
default:
error_exit("Architecture currently not available for cross linking.");
}
return res;
}
static void prepare_msys2_linker_flags(const char ***args, const char **files_to_link, unsigned file_count)
}
static const char *string_esc(const char *str)
{
scratch_buffer_clear();
size_t len = strlen(str);
for (size_t i = 0; i < len; i++)
{
if (i > 3 && !char_is_alphanum_(str[i])) scratch_buffer_append_char('\\');
scratch_buffer_append_char(str[i]);
}
return strdup(scratch_buffer_to_string());
}
static void linker_setup_windows(const char ***args_ref, LinkerType linker_type)
{
if (linker_type == LINKER_CC) return;
//add_arg("/MACHINE:X64");
switch (active_target.debug_info)
{
case DEBUG_INFO_NOT_SET:
break;
case DEBUG_INFO_NONE:
add_arg("/DEBUG:NONE");
break;
case DEBUG_INFO_LINE_TABLES:
case DEBUG_INFO_FULL:
add_arg("/DEBUG:FULL");
break;
default:
UNREACHABLE
}
if (active_target.win.sdk)
{
add_arg(str_printf("/LIBPATH:%s", active_target.win.sdk));
}
else
{
WindowsSDK *windows_sdk = windows_get_sdk();
if (!windows_sdk) error_exit("Windows applications cannot be cross compiled without --winsdk.");
if (!file_is_dir(windows_sdk->vs_library_path)) error_exit("Failed to find windows sdk.");
add_arg(str_printf("/LIBPATH:%s", windows_sdk->windows_sdk_um_library_path));
add_arg(str_printf("/LIBPATH:%s", windows_sdk->windows_sdk_ucrt_library_path));
add_arg(str_printf("/LIBPATH:%s", windows_sdk->vs_library_path));
}
// Do not link any.
if (active_target.win.crt_linking == WIN_CRT_NONE) return;
add_arg("kernel32.lib");
add_arg("ntdll.lib");
add_arg("legacy_stdio_definitions.lib");
if (active_target.win.crt_linking == WIN_CRT_STATIC)
{
add_arg("libucrt.lib");
add_arg("libvcruntimed.lib");
add_arg("libcmt.lib");
add_arg("libcpmt.lib");
}
else
{
add_arg("ucrt.lib");
add_arg("vcruntime.lib");
add_arg("msvcrt.lib");
add_arg("msvcprt.lib");
}
add_arg("/NOLOGO");
}
#ifdef mingw64_support
static void linker_setup_mingw64_gcc(const char ***args_ref)
{
const char *root = getenv("MSYSTEM_PREFIX");
#define add_arg(opt) vec_add(*args, opt)
add_arg("-m");
add_arg("i386pep");
add_arg("-Bdynamic");
add_arg(join_strings((const char *[]){ root, "\\x86_64-w64-mingw32\\lib\\crt2.o" }, 2));
add_arg(join_strings((const char *[]){ root, "\\x86_64-w64-mingw32\\lib\\crtbegin.o" }, 2));
add_arg(join_strings((const char *[]){ "-L", root, "\\x86_64-w64-mingw32\\lib" }, 3));
add_arg(join_strings((const char *[]){ "-L", root, "\\lib" }, 3));
add_arg(join_strings((const char *[]){ "-L", root, "\\x86_64-w64-mingw32\\sys-root\\mingw\\lib" }, 3));
add_arg(join_strings((const char *[]){ "-L", root, "\\lib\\clang\\", LLVM_VERSION_STRING, "\\lib\\windows" }, 5));
add_files(args, files_to_link, file_count);
const char *root = getenv("MSYSTEM_PREFIX");
const char *gcc_base = strformat("%s/lib/gcc/x86_64-w64-mingw32", root);
if (!file_exists(gcc_base)) error_exit("Missing GCC");
const char *name = file_first(gcc_base);
const char *gcc_path = strformat("%s/%s/", gcc_base, name);
add_arg(strformat("-L%s/x86_64-w64-mingw32/lib", root));
add_arg(strformat("-L%s/lib", root));
add_arg2(gcc_path, "crtbegin.o");
add_arg(strformat("%s/lib/crt2.o", root));
add_arg(strformat("%s/lib/default-manifest.o", root));
add_arg2("-L", gcc_path);
add_arg("-lkernel32");
add_arg("-lmingw32");
add_arg(join_strings((const char *[]){ root, "\\lib\\clang\\", LLVM_VERSION_STRING,
"\\lib\\windows\\libclang_rt.builtins-x86_64.a" }, 4));
add_arg("-lunwind");
add_arg("-lgcc");
add_arg("-lgcc_eh");
add_arg("-lmoldname");
add_arg("-lmingwex");
add_arg("-lmsvcrt");
add_arg("-ladvapi32");
add_arg("-lshell32");
add_arg("-luser32");
add_arg("-lpthread");
add_arg2(gcc_path, "crtend.o");
}
static void linker_setup_windows_gnu(const char ***args_ref, LinkerType linker_type)
{
if (linker_type == LINKER_CC) return;
bool is_clang = strcmp(getenv("MSYSTEM"), "CLANG64") == 0;
bool is_mingw = strcmp(getenv("MSYSTEM"), "MINGW64") == 0;
if (!is_clang && !is_mingw)
{
error_exit("Crosslinking MSYS is not yet supported.");
}
if (is_mingw)
{
linker_setup_mingw64_gcc(args_ref);
return;
}
const char *root = getenv("MSYSTEM_PREFIX");
const char *compiler_prefix;
if (is_clang)
{
char *filename;
char *dir;
path_get_dir_and_filename_from_full(root, &filename, &dir);
compiler_prefix = filename;
root = dir;
}
else
{
compiler_prefix = "x86_64-w64-mingw32";
}
add_arg("-m");
add_arg("i386pep");
add_arg("-Bdynamic");
const char *lib = strformat("%s/%s/lib", root, compiler_prefix);
if (!file_exists(lib))
{
error_exit("Cannot find '%s'.", lib);
}
add_arg2(lib, "/crt2.o");
add_arg2(lib, "/crtbegin.o");
add_arg2("-L", lib);
add_arg(strformat("-L%s/lib", root));
add_arg(strformat("-L%s/%s/sys-root/mingw/lib", root, compiler_prefix));
const char *clang_dir = strformat("%s/lib/clang/" LLVM_VERSION_STRING, root);
add_arg(strformat("-L%s/lib/windows", clang_dir));
add_arg("-lmingw32");
add_arg(strformat("%s/lib/windows/libclang_rt.builtins-x86_64.a", clang_dir));
add_arg("-lmoldname");
add_arg("-lmingwex");
add_arg("-lmsvcrt");
@@ -58,36 +202,246 @@ static void prepare_msys2_linker_flags(const char ***args, const char **files_to
add_arg("-luser32");
add_arg("-lkernel32");
add_arg("-lmingw32");
add_arg(join_strings((const char *[]){ root, "\\lib\\clang\\", LLVM_VERSION_STRING,
"\\lib\\windows\\libclang_rt.builtins-x86_64.a" }, 4));
add_arg("-lunwind");
add_arg("-lmoldname");
add_arg("-lmingwex");
add_arg("-lmsvcrt");
add_arg("-lkernel32");
add_arg(join_strings((const char *[]){ root, "\\x86_64-w64-mingw32\\lib\\crtend.o" }, 2));
#undef add_arg
add_arg2(lib, "\\crtend.o");
}
*/
#endif
static void linker_setup_macos(const char ***args_ref, LinkerType linker_type)
{
if (linker_type == LINKER_CC) return;
const char *sysroot = active_target.macos.sdk ? active_target.macos.sdk : macos_sysroot();
if (!sysroot)
{
error_exit("Cannot crosslink MacOS without providing --macossdk.");
}
DEBUG_LOG("Macos SDK: %s", sysroot);
MacSDK *mac_sdk = macos_sysroot_sdk_information(sysroot);
add_arg("-arch");
add_arg(arch_to_linker_arch(platform_target.arch));
add_arg("-lSystem");
add_arg("-lm");
add_arg("-syslibroot");
add_arg(sysroot);
if (is_no_pie(platform_target.reloc_model)) add_arg("-no_pie");
if (is_pie(platform_target.reloc_model)) add_arg("-pie");
add_arg("-platform_version");
add_arg("macos");
add_arg(str_printf("%d.0.0", mac_sdk->macos_deploy_target.major));
add_arg(str_printf("%d.%d", mac_sdk->macos_deploy_target.major, mac_sdk->macos_deploy_target.minor));
}
static void append_linker_pie_options(RelocModel reloc, const char ***args_ref)
static const char *find_linux_crt(void)
{
switch (reloc)
if (file_exists("/usr/lib/x86_64-linux-gnu/crt1.o"))
{
case RELOC_DEFAULT:
UNREACHABLE
case RELOC_NONE:
vec_add(*args_ref, "-no_pie");
break;
case RELOC_SMALL_PIC:
case RELOC_BIG_PIC:
break;
case RELOC_SMALL_PIE:
case RELOC_BIG_PIE:
vec_add(*args_ref, "-pie");
break;
return "/usr/lib/x86_64-linux-gnu/";
}
return NULL;
}
static const char *find_freebsd_crt(void)
{
if (file_exists("/usr/lib/crt1.o"))
{
return "/usr/lib/";
}
return NULL;
}
static const char *find_linux_crt_begin(void)
{
if (file_exists("/usr/lib/gcc/x86_64-linux-gnu/10/crtbegin.o"))
{
return "/usr/lib/gcc/x86_64-linux-gnu/10/";
}
return NULL;
}
static void linker_setup_linux(const char ***args_ref, LinkerType linker_type)
{
if (linker_type == LINKER_CC) return;
if (is_no_pie(platform_target.reloc_model)) add_arg("-no-pie");
if (is_pie(platform_target.reloc_model)) add_arg("-pie");
if (platform_target.arch == ARCH_TYPE_X86_64) add_arg("--eh-frame-hdr");
const char *crt_begin_dir = find_linux_crt_begin();
const char *crt_dir = find_linux_crt();
if (!crt_begin_dir || !crt_dir)
{
error_exit("Failed to find the C runtime at link time.");
}
if (is_pie_pic(platform_target.reloc_model))
{
add_arg("-pie");
add_arg2(crt_dir, "Scrt1.o");
add_arg2(crt_begin_dir, "crtbeginS.o");
add_arg2(crt_dir, "crti.o");
add_arg2(crt_begin_dir, "crtendS.o");
}
else
{
add_arg2(crt_dir, "crt1.o");
add_arg2(crt_begin_dir, "crtbegin.o");
add_arg2(crt_dir, "crti.o");
add_arg2(crt_begin_dir, "crtend.o");
}
add_arg2(crt_dir, "crtn.o");
add_arg2("-L", crt_dir);
add_arg("--dynamic-linker=/lib64/ld-linux-x86-64.so.2");
add_arg("-lc");
add_arg("-lm");
add_arg("-L/usr/lib/");
add_arg("-L/lib/");
add_arg("-m");
add_arg(ld_target(platform_target.arch));
}
static void linker_setup_freebsd(const char ***args_ref, LinkerType linker_type)
{
if (linker_type == LINKER_CC) return;
if (is_no_pie(platform_target.reloc_model)) add_arg("-no-pie");
if (is_pie(platform_target.reloc_model)) add_arg("-pie");
if (platform_target.arch == ARCH_TYPE_X86_64) add_arg("--eh-frame-hdr");
const char *crt_dir = find_freebsd_crt();
if (!crt_dir)
{
error_exit("Failed to find the C runtime at link time.");
}
if (is_pie_pic(platform_target.reloc_model))
{
add_arg("-pie");
add_arg2(crt_dir, "Scrt1.o");
add_arg2(crt_dir, "crtbeginS.o");
add_arg2(crt_dir, "crti.o");
add_arg2(crt_dir, "crtendS.o");
}
else
{
add_arg2(crt_dir, "crt1.o");
add_arg2(crt_dir, "crtbegin.o");
add_arg2(crt_dir, "crti.o");
add_arg2(crt_dir, "crtend.o");
}
add_arg2(crt_dir, "crtn.o");
add_arg2("-L", crt_dir);
add_arg("--dynamic-linker=/libexec/ld-elf.so.1");
add_arg("-lc");
add_arg("-lm");
add_arg("-lgcc");
add_arg("-lgcc_s");
add_arg("-L/usr/lib/");
add_arg("-m");
add_arg(ld_target(platform_target.arch));
}
static bool linker_setup(const char ***args_ref, const char **files_to_link, unsigned file_count,
const char *output_file, LinkerType linker_type)
{
bool use_win = linker_type == LINKER_LINK_EXE;
if (use_win)
{
add_arg2("/OUT:", output_file);
}
else
{
add_arg("-o");
add_arg(output_file);
}
const char *lib_path_opt = use_win ? "/LIBPATH:" : "-L";
switch (platform_target.os)
{
case OS_UNSUPPORTED:
UNREACHABLE
case OS_TYPE_WIN32:
linker_setup_windows(args_ref, linker_type);
break;
case OS_TYPE_MACOSX:
linker_setup_macos(args_ref, linker_type);
break;
case OS_TYPE_WATCHOS:
case OS_TYPE_IOS:
case OS_TYPE_OPENBSD:
case OS_TYPE_NETBSD:
case OS_TYPE_TVOS:
case OS_TYPE_WASI:
break;
case OS_TYPE_FREE_BSD:
linker_setup_freebsd(args_ref, linker_type);
break;
case OS_TYPE_LINUX:
linker_setup_linux(args_ref, linker_type);
break;
case OS_TYPE_UNKNOWN:
error_exit("Linking is not supported for unknown OS.");
case OS_TYPE_NONE:
error_exit("Linking is not supported for freestanding.");
}
for (unsigned i = 0; i < file_count; i++)
{
add_arg(files_to_link[i]);
}
VECEACH(active_target.linker_libdirs, i)
{
add_arg2(lib_path_opt, active_target.linker_libdirs[i]);
}
VECEACH(active_target.link_args, i)
{
add_arg(active_target.link_args[i]);
}
VECEACH(active_target.linker_libs, i)
{
const char *lib = active_target.linker_libs[i];
const char *framework = str_remove_suffix(lib, ".framework");
if (framework)
{
add_arg("-framework");
add_arg(framework);
continue;
}
if (use_win)
{
add_arg2(lib, ".lib");
}
else
{
add_arg2("-l", lib);
}
}
VECEACH(active_target.library_list, i)
{
Library *library = active_target.library_list[i];
LibraryTarget *target = library->target_used;
VECEACH(target->link_flags, j)
{
add_arg(target->link_flags[j]);
}
VECEACH(target->linked_libs, j)
{
const char *lib = target->linked_libs[j];
const char *framework = str_remove_suffix(lib, ".framework");
if (framework)
{
add_arg("-framework");
add_arg(framework);
continue;
}
if (use_win)
{
add_arg2(lib, ".lib");
}
else
{
add_arg2("-l", lib);
}
}
}
return true;
}
#undef add_arg2
#undef add_arg
static void append_fpie_pic_options(RelocModel reloc, const char ***args_ref)
{
switch (reloc)
@@ -117,166 +471,57 @@ static void append_fpie_pic_options(RelocModel reloc, const char ***args_ref)
}
}
LinkerType linker_find_linker_type(void)
{
switch (platform_target.os)
{
case OS_UNSUPPORTED:
case OS_TYPE_UNKNOWN:
case OS_TYPE_NONE:
case OS_TYPE_FREE_BSD:
case OS_TYPE_LINUX:
case OS_TYPE_NETBSD:
case OS_TYPE_OPENBSD:
return LINKER_LD;
case OS_TYPE_IOS:
case OS_TYPE_MACOSX:
case OS_TYPE_TVOS:
case OS_TYPE_WATCHOS:
return LINKER_LD64;
case OS_TYPE_WIN32:
return LINKER_LINK_EXE;
case OS_TYPE_WASI:
return LINKER_WASM;
}
UNREACHABLE
}
static bool link_exe(const char *output_file, const char **files_to_link, unsigned file_count)
{
DEBUG_LOG("Using linker directly.");
const char **args = NULL;
#ifdef _MSC_VER
if (platform_target.os == OS_TYPE_WIN32)
{
vec_add(args, join_strings((const char* []) {"/out:", output_file}, 2));
}
else
{
#endif
vec_add(args, "-o");
vec_add(args, output_file);
#ifdef _MSC_VER
}
#endif
LinkerType linker_type = linker_find_linker_type();
linker_setup(&args, files_to_link, file_count, output_file, linker_type);
VECEACH(active_target.link_args, i)
{
vec_add(args, active_target.link_args[i]);
}
const char *error = NULL;
// This isn't used in most cases, but its contents should get freed after linking.
WindowsLinkPathsUTF8 windows_paths = { 0 };
switch (platform_target.os)
{
case OS_TYPE_WIN32:
// TODO: properly detect if llvm-lld is available
// TODO: check if running inside MSYS2, it could be done via getting MSYSTEM environment variable
// https://stackoverflow.com/questions/65527286/how-to-check-if-my-program-is-running-on-mingwor-msys-shell-or-on-cmd
if (NULL == getenv("MSYSTEM"))
{
// "native" windows
// find paths to library directories.
// ex:
// C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC\\14.28.29910\\lib\\x64
// C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC\\14.28.29910\\atlmfc\\lib\\x64
// C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.19041.0\\ucrt\\x64
// C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.19041.0\\um\\x64
#ifdef _MSC_VER
windows_paths = get_windows_link_paths();
vec_add(args, join_strings((const char* []) { "-libpath:", windows_paths.windows_sdk_um_library_path }, 2));
vec_add(args, join_strings((const char* []) { "-libpath:", windows_paths.windows_sdk_ucrt_library_path }, 2));
vec_add(args, join_strings((const char* []) { "-libpath:", windows_paths.vs_library_path }, 2));
vec_add(args, "-defaultlib:libcmt");
vec_add(args, "-nologo");
add_files(&args, files_to_link, file_count);
#else
error_exit("ERROR - c3c must be compiled with MSVC to target x64-windows\n");
#endif
}
else
{
if (!strcmp(getenv("MSYSTEM"), "CLANG64") || !strcmp(getenv("MSYSTEM"), "MINGW64"))
{
prepare_msys2_linker_flags(&args, files_to_link, file_count);
}
else
{
return false;
}
}
break;
case OS_TYPE_MACOSX:
add_files(&args, files_to_link, file_count);
vec_add(args, "-lSystem");
vec_add(args, "-lm");
vec_add(args, "-syslibroot");
vec_add(args, "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk");
append_linker_pie_options(platform_target.reloc_model, &args);
if (platform_target.reloc_model == RELOC_SMALL_PIE || platform_target.reloc_model == RELOC_BIG_PIE)
{
vec_add(args, "-macosx_version_min");
vec_add(args, platform_target.arch == ARCH_TYPE_AARCH64 ? "11.0" : "10.8");
}
break;
case OS_TYPE_WATCHOS:
case OS_TYPE_IOS:
return false;
case OS_TYPE_WASI:
return false;
case OS_TYPE_OPENBSD:
case OS_TYPE_NETBSD:
case OS_TYPE_FREE_BSD:
return false;
case OS_TYPE_LINUX:
vec_add(args, "-m");
switch (platform_target.arch)
{
case ARCH_TYPE_X86_64:
vec_add(args, "elf_x86_64");
append_linker_pie_options(platform_target.reloc_model, &args);
if (is_pie_pic(platform_target.reloc_model))
{
vec_add(args, "--eh-frame-hdr");
vec_add(args, "/usr/lib/x86_64-linux-gnu/crt1.o");
vec_add(args, "/usr/lib/gcc/x86_64-linux-gnu/10/crtbeginS.o");
add_files(&args, files_to_link, file_count);
vec_add(args, "/usr/lib/x86_64-linux-gnu/crti.o");
vec_add(args, "/usr/lib/gcc/x86_64-linux-gnu/10/crtendS.o");
}
else
{
vec_add(args, "/usr/lib/x86_64-linux-gnu/Scrt1.o");
vec_add(args, "/usr/lib/gcc/x86_64-linux-gnu/10/crtbegin.o");
add_files(&args, files_to_link, file_count);
vec_add(args, "-lc");
vec_add(args, "-lm");
vec_add(args, "/usr/lib/x86_64-linux-gnu/crti.o");
vec_add(args, "/usr/lib/gcc/x86_64-linux-gnu/10/crtend.o");
}
vec_add(args, "/usr/lib/x86_64-linux-gnu/crtn.o");
vec_add(args, "-L/usr/lib/x86_64-linux-gnu/");
vec_add(args, "--dynamic-linker=/lib64/ld-linux-x86-64.so.2");
break;
case ARCH_TYPE_X86:
// vec_add(args, "elf_i386");
return false;
case ARCH_TYPE_AARCH64:
vec_add(args, "aarch64elf");
return false;
case ARCH_TYPE_RISCV32:
vec_add(args, "elf32lriscv");
return false;
case ARCH_TYPE_RISCV64:
vec_add(args, "elf64lriscv");
return false;
default:
UNREACHABLE
}
vec_add(args, "-L/usr/lib/");
vec_add(args, "-L/lib/");
break;
default:
add_files(&args, files_to_link, file_count);
append_linker_pie_options(platform_target.reloc_model, &args);
return false;
}
bool success;
const char *arg_list = "";
VECEACH(args, i)
{
arg_list = str_cat(arg_list, " ");
arg_list = str_cat(arg_list, args[i]);
}
DEBUG_LOG("Linker arguments: %s to %d", arg_list, platform_target.object_format);
switch (platform_target.object_format)
{
case OBJ_FORMAT_COFF:
if (platform_target.x64.is_mingw64)
{
success = llvm_link_mingw(args, (int)vec_size(args), &error);
}
else
{
success = llvm_link_coff(args, (int)vec_size(args), &error);
}
// This is only defined if compiling with MSVC
#ifdef _MSC_VER
if (windows_paths.windows_sdk_um_library_path) {
free_windows_link_paths(&windows_paths);
}
#endif
success = llvm_link_coff(args, (int)vec_size(args), &error);
break;
case OBJ_FORMAT_ELF:
success = llvm_link_elf(args, (int)vec_size(args), &error);
@@ -294,6 +539,7 @@ static bool link_exe(const char *output_file, const char **files_to_link, unsign
{
error_exit("Failed to create an executable: %s", error);
}
DEBUG_LOG("Linking complete.");
return true;
}
@@ -338,19 +584,11 @@ const char *concat_string_parts(const char **args)
void platform_linker(const char *output_file, const char **files, unsigned file_count)
{
DEBUG_LOG("Using cc linker.");
const char **parts = NULL;
vec_add(parts, active_target.cc ? active_target.cc : "cc");
VECEACH(active_target.link_args, i)
{
vec_add(parts, active_target.link_args[i]);
}
append_fpie_pic_options(platform_target.reloc_model, &parts);
vec_add(parts, "-o");
vec_add(parts, output_file);
for (unsigned i = 0; i < file_count; i++)
{
vec_add(parts, files[i]);
}
linker_setup(&parts, files, file_count, output_file, LINKER_CC);
vec_add(parts, "-lm");
const char *output = concat_string_parts(parts);
if (system(output) != 0)
@@ -375,7 +613,6 @@ void platform_compiler(const char **files, unsigned file_count, const char *flag
{
append_fpie_pic_options(platform_target.reloc_model, &parts);
}
vec_add(parts, "-c");
if (flags) vec_add(parts, flags);
for (unsigned i = 0; i < file_count; i++)

View File

@@ -7,8 +7,6 @@
const char* llvm_version = LLVM_VERSION_STRING;
const char* llvm_target = LLVM_DEFAULT_TARGET_TRIPLE;
void llvm_emit_local_global_variable_definition(GenContext *c, Decl *decl);
static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context)
{
char *message = LLVMGetDiagInfoDescription(ref);
@@ -43,8 +41,6 @@ static void gencontext_init(GenContext *context, Module *module)
{
memset(context, 0, sizeof(GenContext));
context->context = LLVMContextCreate();
context->bool_type = LLVMInt1TypeInContext(context->context);
context->byte_type = LLVMInt8TypeInContext(context->context);
LLVMContextSetDiagnosticHandler(context->context, &diagnostics_handler, context);
context->code_module = module;
}
@@ -99,7 +95,7 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_
ArraySize size = array_type->array.len;
assert(size > 0);
LLVMValueRef *parts = VECNEW(LLVMValueRef, size);
for (MemberIndex i = 0; i < size; i++)
for (MemberIndex i = 0; i < (MemberIndex)size; i++)
{
LLVMValueRef element = llvm_emit_const_initializer(c, elements[i]);
if (element_type_llvm != LLVMTypeOf(element)) was_modified = true;
@@ -210,6 +206,7 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_
Decl *decl = const_init->type->decl;
Decl **members = decl->strukt.members;
uint32_t count = vec_size(members);
if (decl->decl_kind == DECL_UNION && count) count = 1;
LLVMValueRef *entries = NULL;
bool was_modified = false;
for (MemberIndex i = 0; i < count; i++)
@@ -255,28 +252,6 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_
}
void llvm_emit_local_global_variable_definition(GenContext *c, Decl *decl)
{
assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST);
// Skip real constants.
if (!decl->type) return;
if (decl->type != type_void)
{
decl->backend_ref = LLVMAddGlobal(c->module, llvm_get_type(c, decl->type), "tempglobal");
}
if (IS_FAILABLE(decl))
{
scratch_buffer_clear();
scratch_buffer_append(decl->extname);
scratch_buffer_append(".f");
decl->var.failable_ref = LLVMAddGlobal(c->module, llvm_get_type(c, type_anyerr), scratch_buffer_to_string());
LLVMSetUnnamedAddress(decl->var.failable_ref, LLVMGlobalUnnamedAddr);
}
}
void llvm_emit_ptr_from_array(GenContext *c, BEValue *value)
{
switch (value->type->type_kind)
@@ -291,8 +266,6 @@ void llvm_emit_ptr_from_array(GenContext *c, BEValue *value)
return;
case TYPE_SUBARRAY:
{
// TODO insert trap on overflow.
assert(value->kind == BE_ADDRESS);
BEValue member;
llvm_emit_subarray_pointer(c, value, &member);
llvm_value_rvalue(c, &member);
@@ -383,7 +356,7 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl)
// TODO fix name
LLVMValueRef old = decl->backend_ref;
LLVMValueRef global_ref = decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->extname);
LLVMValueRef global_ref = decl->backend_ref = llvm_add_global_type(c, decl->extname, LLVMTypeOf(init_value), decl->alignment);
if (decl->var.is_addr)
{
LLVMSetUnnamedAddress(global_ref, LLVMNoUnnamedAddr);
@@ -399,7 +372,6 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl)
LLVMSetSection(global_ref, decl->section);
}
llvm_set_global_tls(decl);
llvm_set_alignment(global_ref, alignment);
LLVMValueRef failable_ref = decl->var.failable_ref;
if (failable_ref)
@@ -472,16 +444,18 @@ static void gencontext_verify_ir(GenContext *context)
}
}
void gencontext_emit_object_file(GenContext *context)
{
char *err = "";
DEBUG_LOG("Target: %s", platform_target.target_triple);
LLVMSetTarget(context->module, platform_target.target_triple);
char *layout = LLVMCopyStringRepOfTargetData(context->target_data);
LLVMSetDataLayout(context->module, layout);
LLVMDisposeMessage(layout);
// Generate .o or .obj file
if (LLVMTargetMachineEmitToFile(context->machine, context->module, context->object_filename, LLVMObjectFile, &err))
if (LLVMTargetMachineEmitToFile(context->machine, context->module, (char *)context->object_filename, LLVMObjectFile, &err))
{
error_exit("Could not emit object file: %s", err);
}
@@ -666,12 +640,33 @@ void llvm_codegen_setup()
intrinsics_setup = true;
}
void llvm_set_comdat(GenContext *c, LLVMValueRef global)
{
if (!platform_target.use_comdat) return;
LLVMComdatRef comdat = LLVMGetOrInsertComdat(c->module, LLVMGetValueName(global));
LLVMSetComdatSelectionKind(comdat, LLVMAnyComdatSelectionKind);
LLVMSetComdat(global, comdat);
}
void llvm_set_linkonce(GenContext *c, LLVMValueRef global)
{
LLVMSetLinkage(global, LLVMLinkOnceAnyLinkage);
LLVMSetVisibility(global, LLVMDefaultVisibility);
llvm_set_comdat(c, global);
}
void llvm_set_weak(GenContext *c, LLVMValueRef global)
{
LLVMSetLinkage(global, LLVMWeakAnyLinkage);
LLVMSetVisibility(global, LLVMDefaultVisibility);
llvm_set_comdat(c, global);
}
void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value)
{
if (decl->module != c->code_module)
if (decl->unit->module != c->code_module)
{
LLVMSetLinkage(value, LLVMLinkOnceODRLinkage);
LLVMSetVisibility(value, LLVMDefaultVisibility);
llvm_set_linkonce(c, value);
return;
}
Visibility visibility = decl->visibility;
@@ -680,8 +675,7 @@ void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value)
{
case VISIBLE_MODULE:
case VISIBLE_PUBLIC:
LLVMSetLinkage(value, LLVMLinkOnceODRLinkage);
LLVMSetVisibility(value, LLVMDefaultVisibility);
llvm_set_linkonce(c, value);
break;
case VISIBLE_EXTERN:
case VISIBLE_LOCAL:
@@ -691,46 +685,11 @@ void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value)
}
}
void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl)
{
llvm_get_type(c, decl->type);
if (decl_is_struct_type(decl))
{
Decl **decls = decl->strukt.members;
VECEACH(decls, i)
{
Decl *member_decl = decls[i];
if (decl_is_struct_type(member_decl))
{
llvm_emit_introspection_type_from_decl(c, member_decl);
}
}
}
if (decl_is_enum_kind(decl))
{
unsigned elements = vec_size(decl->enums.values);
LLVMTypeRef element_type = llvm_get_type(c, type_voidptr);
LLVMTypeRef elements_type = LLVMArrayType(element_type, elements);
scratch_buffer_clear();
scratch_buffer_append(decl->extname);
scratch_buffer_append("$elements");
LLVMValueRef enum_elements = LLVMAddGlobal(c->module, elements_type, scratch_buffer_to_string());
LLVMSetGlobalConstant(enum_elements, 1);
llvm_set_linkage(c, decl, enum_elements);
LLVMSetInitializer(enum_elements, LLVMConstNull(elements_type));
AlignSize alignment = type_alloca_alignment(type_voidptr);
for (unsigned i = 0; i < elements; i++)
{
AlignSize store_align;
decl->enums.values[i]->backend_ref = llvm_emit_array_gep_raw(c, enum_elements, elements_type, i, alignment, &store_align);
}
}
LLVMValueRef global_name = LLVMAddGlobal(c->module, llvm_get_type(c, type_char), decl->name ? decl->name : "anon");
LLVMSetGlobalConstant(global_name, 1);
LLVMSetInitializer(global_name, LLVMConstInt(llvm_get_type(c, type_char), 1, false));
decl->type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(c, type_typeid));
llvm_set_linkage(c, decl, global_name);
}
void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value)
@@ -776,9 +735,6 @@ LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name)
return LLVMCreateBasicBlockInContext(c->context, name);
}
static void llvm_emit_type_decls(GenContext *context, Decl *decl)
{
switch (decl->decl_kind)
@@ -793,19 +749,17 @@ static void llvm_emit_type_decls(GenContext *context, Decl *decl)
break;
case DECL_TYPEDEF:
break;
case DECL_DISTINCT:
// TODO
break;
case DECL_ENUM_CONSTANT:
case DECL_FAULTVALUE:
// TODO
break;;
case DECL_DISTINCT:
case DECL_STRUCT:
case DECL_UNION:
case DECL_ENUM:
case DECL_FAULT:
case DECL_BITSTRUCT:
llvm_emit_introspection_type_from_decl(context, decl);
llvm_get_typeid(context, decl->type);
break;
case DECL_BODYPARAM:
case NON_TYPE_DECLS:
@@ -878,14 +832,16 @@ void llvm_add_global(GenContext *c, Decl *decl)
{
assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST);
const char *name = decl->module == c->code_module ? "tempglobal" : decl_get_extname(decl);
decl->backend_ref = LLVMAddGlobal(c->module, llvm_get_type(c, type_lowering(type_no_fail(decl->type))), name);
const char *name = decl->unit->module == c->code_module ? "tempglobal" : decl_get_extname(decl);
decl->backend_ref = llvm_add_global_var(c, name, decl->type, decl->alignment);
llvm_set_alignment(decl->backend_ref, decl->alignment);
if (IS_FAILABLE(decl))
{
scratch_buffer_clear();
scratch_buffer_append(decl_get_extname(decl));
scratch_buffer_append(".f");
decl->var.failable_ref = LLVMAddGlobal(c->module, llvm_get_type(c, type_anyerr), scratch_buffer_to_string());
decl->var.failable_ref = llvm_add_global_var(c, scratch_buffer_to_string(), type_anyerr, 0);
}
llvm_set_global_tls(decl);
}
@@ -917,7 +873,7 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl)
return decl->backend_ref;
case DECL_FUNC:
backend_ref = decl->backend_ref = LLVMAddFunction(c->module, decl_get_extname(decl), llvm_get_type(c, decl->type));
if (decl->module == c->code_module && !decl->is_external_visible && !visible_external(decl->visibility))
if (decl->unit->module == c->code_module && !decl->is_external_visible && !visible_external(decl->visibility))
{
llvm_set_internal_linkage(backend_ref);
}
@@ -926,7 +882,10 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl)
if (decl->define_decl.define_kind != DEFINE_TYPE_GENERIC) return llvm_get_ref(c, decl->define_decl.alias);
UNREACHABLE
case DECL_FAULTVALUE:
llvm_emit_introspection_type_from_decl(c, declptr(decl->enum_constant.parent));
if (!decl->backend_ref)
{
llvm_get_typeid(c, declptr(decl->enum_constant.parent)->type);
}
assert(decl->backend_ref);
return decl->backend_ref;
case DECL_POISONED:
@@ -963,7 +922,6 @@ void *llvm_gen(Module *module)
gencontext_init(gen_context, module);
gencontext_begin_module(gen_context);
VECEACH(module->units, j)
{
CompilationUnit *unit = module->units[j];
@@ -979,6 +937,10 @@ void *llvm_gen(Module *module)
{
llvm_emit_type_decls(gen_context, unit->types[i]);
}
VECEACH(unit->enums, i)
{
llvm_emit_type_decls(gen_context, unit->enums[i]);
}
VECEACH(unit->functions, i)
{
llvm_emit_function_decl(gen_context, unit->functions[i]);
@@ -1017,7 +979,11 @@ void *llvm_gen(Module *module)
}
// EmitDeferred()
if (llvm_use_debug(gen_context)) LLVMDIBuilderFinalize(gen_context->debug.builder);
if (llvm_use_debug(gen_context))
{
LLVMDIBuilderFinalize(gen_context->debug.builder);
LLVMDisposeDIBuilder(gen_context->debug.builder);
}
// If it's in test, then we want to serialize the IR before it is optimized.
if (active_target.test_output)

View File

@@ -173,4 +173,15 @@ void c_abi_func_create_aarch64(FunctionPrototype *prototype)
}
prototype->abi_args = args;
}
Type **va_params = prototype->varargs;
unsigned va_param_count = vec_size(va_params);
if (va_param_count)
{
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * va_param_count);
for (unsigned i = 0; i < va_param_count; i++)
{
args[i] = aarch64_classify_argument_type(va_params[i]);
}
prototype->abi_varargs = args;
}
}

View File

@@ -251,7 +251,17 @@ static ABIArgInfo *riscv_classify_return(Type *return_type)
// classifyArgumentType.
return riscv_classify_argument_type(return_type, true, &arg_gpr_left, &arg_fpr_left);
}
ABIArgInfo **riscv_create_params(Type** params, bool is_fixed, unsigned *arg_gprs_left, unsigned *arg_fprs_left)
{
unsigned param_count = vec_size(params);
if (!param_count) return NULL;
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
for (unsigned i = 0; i < param_count; i++)
{
args[i] = riscv_classify_argument_type(type_lowering(params[i]), is_fixed, arg_gprs_left, arg_fprs_left);
}
return args;
}
void c_abi_func_create_riscv(FunctionPrototype *prototype)
{
// Registers
@@ -287,16 +297,6 @@ void c_abi_func_create_riscv(FunctionPrototype *prototype)
true, &arg_gprs_left, &arg_fprs_left);
}
Type **params = prototype->params;
unsigned param_count = vec_size(prototype->params);
if (param_count)
{
bool is_fixed = true;
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
for (unsigned i = 0; i < param_count; i++)
{
args[i] = riscv_classify_argument_type(type_lowering(params[i]), is_fixed, &arg_gprs_left, &arg_fprs_left);
}
prototype->abi_args = args;
}
prototype->abi_args = riscv_create_params(prototype->params, true, &arg_gprs_left, &arg_fprs_left);
prototype->abi_varargs = riscv_create_params(prototype->varargs, false, &arg_gprs_left, &arg_fprs_left);
}

View File

@@ -51,6 +51,18 @@ static ABIArgInfo *wasm_classify_return(Type *type)
return c_abi_classify_return_type_default(type);
}
ABIArgInfo **wasm_create_params(Type **params)
{
unsigned param_count = vec_size(params);
if (!param_count) return NULL;
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
for (unsigned i = 0; i < param_count; i++)
{
args[i] = wasm_classify_argument_type(type_lowering(params[i]));
}
return args;
}
void c_abi_func_create_wasm(FunctionPrototype *prototype)
{
prototype->ret_abi_info = wasm_classify_return(type_lowering(prototype->abi_ret_type));
@@ -59,15 +71,6 @@ void c_abi_func_create_wasm(FunctionPrototype *prototype)
prototype->ret_by_ref_abi_info = wasm_classify_argument_type(type_get_ptr(prototype->ret_by_ref_type));
}
Type **params = prototype->params;
unsigned param_count = vec_size(prototype->params);
if (param_count)
{
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
for (unsigned i = 0; i < param_count; i++)
{
args[i] = wasm_classify_argument_type(type_lowering(params[i]));
}
prototype->abi_args = args;
}
prototype->abi_args = wasm_create_params(prototype->params);
prototype->abi_varargs = wasm_create_params(prototype->varargs);
}

View File

@@ -7,11 +7,7 @@
ABIArgInfo *win64_classify(Regs *regs, Type *type, bool is_return, bool is_vector, bool is_reg)
{
if (type_is_void(type)) return abi_arg_ignore();
if (type_lowering(type)->type_kind == TYPE_TYPEDEF)
{
printf("foekf");
}
// Lower enums etc.
type = type_lowering(type);
@@ -141,6 +137,18 @@ void win64_vector_call_args(Regs *regs, FunctionPrototype *prototype, bool is_ve
}
}
ABIArgInfo **win64_create_params(Type **params, Regs *regs, bool is_vector_call, bool is_reg_call)
{
unsigned param_count = vec_size(params);
if (!param_count) return NULL;
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
for (unsigned i = 0; i < param_count; i++)
{
args[i] = win64_classify(regs, params[i], false, is_vector_call, is_reg_call);
}
return args;
}
void c_abi_func_create_win64(FunctionPrototype *prototype)
{
// allow calling sysv?
@@ -191,15 +199,6 @@ void c_abi_func_create_win64(FunctionPrototype *prototype)
return;
}
Type **params = prototype->params;
unsigned param_count = vec_size(prototype->params);
if (param_count)
{
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
for (unsigned i = 0; i < param_count; i++)
{
args[i] = win64_classify(&regs, params[i], false, is_vector_call, is_reg_call);
}
prototype->abi_args = args;
}
prototype->abi_args = win64_create_params(prototype->params, &regs, is_vector_call, is_reg_call);
prototype->abi_varargs = win64_create_params(prototype->varargs, &regs, is_vector_call, is_reg_call);
}

View File

@@ -885,7 +885,8 @@ void c_abi_func_create_x64(FunctionPrototype *prototype)
{
if (prototype->use_win64)
{
return c_abi_func_create_win64(prototype);
c_abi_func_create_win64(prototype);
return;
}
// TODO 32 bit pointers
bool is_regcall = prototype->call_abi == CALL_X86_REG;

View File

@@ -8,6 +8,8 @@
static bool x86_try_use_free_regs(Regs *regs, Type *type);
ABIArgInfo **x86_create_params(CallABI abi, Type **p_type, Regs *ptr);
static inline bool type_is_simd_vector(Type *type)
{
type = type->canonical;
@@ -606,6 +608,18 @@ static ABIArgInfo *x86_classify_argument(CallABI call, Regs *regs, Type *type)
UNREACHABLE
}
ABIArgInfo **x86_create_params(CallABI abi, Type **params, Regs *regs)
{
unsigned param_count = vec_size(params);
if (!param_count) return NULL;
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
for (unsigned i = 0; i < param_count; i++)
{
args[i] = x86_classify_argument(abi, regs, params[i]);
}
return args;
}
void c_abi_func_create_x86(FunctionPrototype *prototype)
{
// 1. Calculate the registers we have available
@@ -668,19 +682,8 @@ void c_abi_func_create_x86(FunctionPrototype *prototype)
{
FATAL_ERROR("X86 vector call not supported");
}
else
{
Type **params = prototype->params;
unsigned param_count = vec_size(prototype->params);
if (param_count)
{
ABIArgInfo **args = MALLOC(sizeof(ABIArgInfo) * param_count);
for (unsigned i = 0; i < param_count; i++)
{
args[i] = x86_classify_argument(prototype->call_abi, &regs, params[i]);
}
prototype->abi_args = args;
}
}
prototype->abi_args = x86_create_params(prototype->call_abi, prototype->params, &regs);
prototype->abi_varargs = x86_create_params(prototype->call_abi, prototype->params, &regs);
}

View File

@@ -309,11 +309,11 @@ static LLVMMetadataRef llvm_debug_enum_type(GenContext *c, Type *type, LLVMMetad
VECEACH(enums, i)
{
Decl *enum_constant = enums[i];
uint64_t val = int_to_u64(enum_constant->enum_constant.expr->const_expr.ixx);
int64_t val = enum_constant->enum_constant.ordinal;
LLVMMetadataRef debug_info = LLVMDIBuilderCreateEnumerator(
c->debug.builder,
enum_constant->name, strlen(enum_constant->name),
(int64_t)val,
val,
is_unsigned);
vec_add(elements, debug_info);
}
@@ -489,6 +489,15 @@ static LLVMMetadataRef llvm_debug_vector_type(GenContext *c, Type *type)
static LLVMMetadataRef llvm_debug_func_type(GenContext *c, Type *type)
{
FunctionPrototype *prototype = type->func.prototype;
// 1. Generate all the parameter types, this may cause this function to be called again!
VECEACH(prototype->params, i)
{
llvm_get_debug_type(c, prototype->params[i]);
}
// 2. We might be done!
if (type->backend_debug_type) return type->backend_debug_type;
// 3. Otherwise generate:
static LLVMMetadataRef *buffer = NULL;
vec_resize(buffer, 0);
vec_add(buffer, llvm_get_debug_type(c, prototype->rtype));

File diff suppressed because it is too large Load Diff

View File

@@ -348,8 +348,8 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl
LLVMTypeRef unpadded_type = llvm_get_twostruct(c, lo_type, hi_type);
LLVMValueRef composite = LLVMGetUndef(unpadded_type);
composite = LLVMBuildInsertValue(c->builder, composite, lo_val, 0, "");
composite = LLVMBuildInsertValue(c->builder, composite, hi_val, 1, "");
composite = llvm_emit_insert_value(c, composite, lo_val, 0);
composite = llvm_emit_insert_value(c, composite, hi_val, 1);
// And return that unpadded result
llvm_emit_return_value(c, composite);
@@ -422,7 +422,7 @@ void llvm_emit_function_body(GenContext *c, Decl *decl)
if (c->debug.enable_stacktrace)
{
scratch_buffer_clear();
scratch_buffer_append(decl->module->name->module);
scratch_buffer_append(decl->unit->module->name->module);
scratch_buffer_append("::");
scratch_buffer_append(decl->name ? decl->name : "anon");
c->debug.func_name = llvm_emit_zstring(c, scratch_buffer_to_string());
@@ -437,7 +437,6 @@ void llvm_emit_function_body(GenContext *c, Decl *decl)
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, c->function, "entry");
c->current_block = entry;
c->current_block_is_target = true;
c->block_return_exit = NULL;
c->in_block = 0;
c->builder = LLVMCreateBuilderInContext(c->context);
LLVMPositionBuilderAtEnd(c->builder, entry);
@@ -457,11 +456,11 @@ void llvm_emit_function_body(GenContext *c, Decl *decl)
LLVMTypeRef ptr_to_slot_type = LLVMPointerType(slot_type, 0);
if (!c->debug.last_ptr)
{
LLVMValueRef last_stack = c->debug.last_ptr = LLVMAddGlobal(c->module, ptr_to_slot_type, ".$last_stack");
const char *name = ".$last_stack";
LLVMValueRef last_stack = c->debug.last_ptr = llvm_add_global_type(c, name, ptr_to_slot_type, 0);
LLVMSetThreadLocal(last_stack, true);
LLVMSetInitializer(last_stack, LLVMConstNull(ptr_to_slot_type));
LLVMSetVisibility(last_stack, LLVMDefaultVisibility);
LLVMSetLinkage(last_stack, LLVMWeakODRLinkage);
llvm_set_weak(c, last_stack);
}
AlignSize alignment = llvm_abi_alignment(c, slot_type);
c->debug.stack_slot = llvm_emit_alloca(c, slot_type, alignment, ".$stackslot");
@@ -651,7 +650,15 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl)
switch (visibility)
{
case VISIBLE_EXTERN:
LLVMSetLinkage(function, decl->func_decl.attr_weak ? LLVMExternalWeakLinkage : LLVMExternalLinkage);
if (decl->is_weak)
{
LLVMSetLinkage(function, LLVMExternalWeakLinkage);
llvm_set_comdat(c, function);
}
else
{
LLVMSetLinkage(function, LLVMExternalLinkage);
}
LLVMSetVisibility(function, LLVMDefaultVisibility);
if (prototype->call_abi == CALL_X86_STD && platform_target.os == OS_TYPE_WIN32)
{
@@ -660,11 +667,10 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl)
break;
case VISIBLE_PUBLIC:
case VISIBLE_MODULE:
if (decl->func_decl.attr_weak) LLVMSetLinkage(function, LLVMWeakAnyLinkage);
LLVMSetVisibility(function, LLVMDefaultVisibility);
if (decl->is_weak) llvm_set_weak(c, function);
break;
case VISIBLE_LOCAL:
LLVMSetLinkage(function, decl->func_decl.attr_weak ? LLVMLinkerPrivateWeakLinkage : LLVMInternalLinkage);
LLVMSetLinkage(function, decl->is_weak ? LLVMLinkerPrivateWeakLinkage : LLVMInternalLinkage);
LLVMSetVisibility(function, LLVMDefaultVisibility);
break;;
}
@@ -675,13 +681,3 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl)
}
void llvm_emit_methods(GenContext *c, Decl **methods)
{
VECEACH(methods, i)
{
Decl *decl = methods[i];
if (decl->decl_kind == DECL_MACRO) continue;
llvm_emit_function_decl(c, decl);
}
}

View File

@@ -79,12 +79,15 @@ typedef struct
LLVMBuilderRef builder;
LLVMBasicBlockRef current_block;
LLVMBasicBlockRef catch_block;
char *ir_filename;
char *object_filename;
const char *ir_filename;
const char *object_filename;
// The recipient of the error value in a catch(err = ...) expression.
LLVMValueRef error_var;
LLVMTypeRef bool_type;
LLVMTypeRef byte_type;
LLVMTypeRef introspect_type;
LLVMTypeRef fault_type;
LLVMTypeRef size_type;
Decl *panicfn;
Decl *cur_code_decl;
Decl *cur_func_decl;
@@ -99,9 +102,6 @@ typedef struct
Module *code_module;
LLVMValueRef return_out;
LLVMValueRef failable_out;
LLVMBasicBlockRef block_return_exit;
LLVMBasicBlockRef block_failable_exit;
LLVMValueRef block_error_var;
BEValue retval;
int in_block;
bool current_block_is_target : 1;
@@ -211,6 +211,7 @@ void gencontext_init_file_emit(GenContext *c, CompilationUnit *unit);
void gencontext_end_file_emit(GenContext *c, CompilationUnit *ast);
void gencontext_end_module(GenContext *context);
void LLVMEnableOpaquePointers(LLVMContextRef ctx);
LLVMValueRef LLVMConstBswap(LLVMValueRef ConstantVal);
#ifndef LLVMCreateTypeAttribute
LLVMAttributeRef LLVMCreateTypeAttribute(LLVMContextRef C, unsigned KindID,
@@ -234,6 +235,7 @@ void llvm_value_set_decl(GenContext *c, BEValue *value, Decl *decl);
void llvm_value_fold_failable(GenContext *c, BEValue *value);
void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index);
LLVMValueRef llvm_get_typeid(GenContext *context, Type *type);
LLVMTypeRef llvm_abi_type(GenContext *c, AbiType type);
TypeSize llvm_abi_size(GenContext *c, LLVMTypeRef type);
BitSize llvm_bitsize(GenContext *c, LLVMTypeRef type);
@@ -262,7 +264,9 @@ void llvm_emit_convert_value_from_coerced(GenContext *c, BEValue *result, LLVMTy
void llvm_emit_coerce_store(GenContext *c, LLVMValueRef addr, AlignSize alignment, LLVMTypeRef coerced, LLVMValueRef value, LLVMTypeRef target_type);
void llvm_emit_function_body(GenContext *context, Decl *decl);
void llvm_emit_function_decl(GenContext *c, Decl *decl);
void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl);
void llvm_set_weak(GenContext *c, LLVMValueRef global);
void llvm_set_linkonce(GenContext *c, LLVMValueRef global);
void llvm_set_comdat(GenContext *c, LLVMValueRef global);
LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count);
void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type);
@@ -282,7 +286,7 @@ INLINE void llvm_emit_exprid(GenContext *c, BEValue *value, ExprId expr)
assert(expr);
llvm_emit_expr(c, value, exprptr(expr));
}
void llvm_emit_local_global_variable_definition(GenContext *c, Decl *decl);
void llvm_emit_typeid(GenContext *c, BEValue *be_value, Type *type);
void llvm_emit_global_variable_init(GenContext *c, Decl *decl);
void llvm_set_private_linkage(LLVMValueRef alloc);
@@ -296,6 +300,7 @@ void llvm_emit_comparison(GenContext *c, BEValue *be_value, BEValue *lhs, BEValu
void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len);
// -- type ---
LLVMTypeRef llvm_func_type(GenContext *context, FunctionPrototype *prototype);
LLVMTypeRef llvm_update_prototype_abi(GenContext *context, FunctionPrototype *prototype, LLVMTypeRef **params);
// -- instr ---
void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block);
@@ -317,14 +322,18 @@ LLVMValueRef llvm_emit_lshr_fixed(GenContext *c, LLVMValueRef data, int shift);
// -- general --
void llvm_emit_local_var_alloca(GenContext *c, Decl *decl);
LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl);
LLVMValueRef llvm_emit_aggregate_value(GenContext *c, Type *type, ...);
void llvm_emit_local_decl(GenContext *c, Decl *decl, BEValue *value);
void llvm_set_aggregate_two(GenContext *c, BEValue *value, Type *type, LLVMValueRef value1, LLVMValueRef value2);
LLVMValueRef llvm_emit_aggregate_two(GenContext *c, Type *type, LLVMValueRef value1, LLVMValueRef value2);
LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint64_t size, AlignSize align, bool bitcast);
void llvm_store_zero(GenContext *c, BEValue *ref);
void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len);
void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment);
void llvm_emit_stmt(GenContext *c, Ast *ast);
LLVMValueRef llvm_emit_zstring(GenContext *c, const char *str);
LLVMValueRef llvm_emit_zstring_named(GenContext *c, const char *str, const char *extname);
static inline LLVMValueRef llvm_emit_store(GenContext *c, Decl *decl, LLVMValueRef value);
void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name, SourceSpan loc);
void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc);
@@ -337,7 +346,7 @@ LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLV
unsigned struct_alignment, AlignSize *alignment);
LLVMValueRef llvm_emit_array_gep_raw(GenContext *c, LLVMValueRef ptr, LLVMTypeRef array_type, unsigned index, AlignSize array_alignment, AlignSize *alignment);
LLVMValueRef llvm_emit_array_gep_raw_index(GenContext *c, LLVMValueRef ptr, LLVMTypeRef array_type, LLVMValueRef index, AlignSize array_alignment, AlignSize *alignment);
LLVMValueRef llvm_emit_array_load(GenContext *c, LLVMValueRef ptr, LLVMTypeRef array_type, unsigned index, AlignSize array_alignment);
LLVMValueRef llvm_emit_pointer_gep_raw(GenContext *c, LLVMTypeRef pointee_type, LLVMValueRef ptr, LLVMValueRef offset);
LLVMValueRef llvm_emit_pointer_inbounds_gep_raw(GenContext *c, LLVMTypeRef pointee_type, LLVMValueRef ptr, LLVMValueRef offset);
@@ -374,13 +383,6 @@ void llvm_store_decl_raw(GenContext *context, Decl *decl, LLVMValueRef value);
LLVMTypeRef llvm_get_twostruct(GenContext *context, LLVMTypeRef lo, LLVMTypeRef hi);
LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value, Type *original_type);
static inline LLVMValueRef gencontext_emit_load(GenContext *c, Type *type, LLVMValueRef value)
{
assert(llvm_get_type(c, type) == LLVMGetElementType(LLVMTypeOf(value)));
return LLVMBuildLoad2(c->builder, llvm_get_type(c, type), value, "");
}
static inline LLVMValueRef decl_failable_ref(Decl *decl)
{
assert(decl->decl_kind == DECL_VAR);
@@ -390,6 +392,15 @@ static inline LLVMValueRef decl_failable_ref(Decl *decl)
}
static inline LLVMValueRef llvm_emit_insert_value(GenContext *c, LLVMValueRef agg, LLVMValueRef new_value, ArraySize index)
{
if (LLVMGetTypeKind(LLVMTypeOf(agg)) == LLVMVectorTypeKind)
{
LLVMValueRef index_val = llvm_const_int(c, type_usize, index);
return LLVMBuildInsertElement(c->builder, agg, new_value, index_val, "");
}
return LLVMBuildInsertValue(c->builder, agg, new_value, index, "");
}
static inline LLVMValueRef llvm_emit_store(GenContext *c, Decl *decl, LLVMValueRef value)
{
@@ -489,6 +500,21 @@ static inline LLVMValueRef llvm_const_int(GenContext *c, Type *type, uint64_t va
return LLVMConstInt(llvm_get_type(c, type), val, type_is_integer_signed(type));
}
static inline LLVMValueRef llvm_add_global_var(GenContext *c, const char *name, Type *type, AlignSize alignment)
{
type = type_lowering(type_no_fail(type));
LLVMValueRef ref = LLVMAddGlobal(c->module, llvm_get_type(c, type), name);
LLVMSetAlignment(ref, (unsigned)alignment ? alignment : type_alloca_alignment(type));
return ref;
}
static inline LLVMValueRef llvm_add_global_type(GenContext *c, const char *name, LLVMTypeRef type, AlignSize alignment)
{
LLVMValueRef ref = LLVMAddGlobal(c->module, type, name);
LLVMSetAlignment(ref, (unsigned)alignment ? alignment : LLVMPreferredAlignmentOfGlobal(c->target_data, ref));
return ref;
}
static inline void llvm_set_alignment(LLVMValueRef alloca, AlignSize alignment)
{
assert(alignment > 0);

View File

@@ -5,14 +5,46 @@
#include "llvm_codegen_internal.h"
static inline LLVMTypeRef create_introspection_type(GenContext *c)
{
LLVMTypeRef type = LLVMStructCreateNamed(c->context, ".introspect");
LLVMTypeRef typeid_type = llvm_get_type(c, type_typeid);
LLVMTypeRef kind_type = llvm_get_type(c, type_char);
LLVMTypeRef usize_type = llvm_get_type(c, type_usize);
LLVMTypeRef introspect_type[INTROSPECT_INDEX_TOTAL] = {
[INTROSPECT_INDEX_KIND] = kind_type,
[INTROSPECT_INDEX_SIZEOF] = usize_type,
[INTROSPECT_INDEX_INNER] = typeid_type,
[INTROSPECT_INDEX_LEN] = usize_type,
[INTROSPECT_INDEX_ADDITIONAL] = LLVMArrayType(typeid_type, 0),
};
LLVMStructSetBody(type, introspect_type, INTROSPECT_INDEX_TOTAL, false);
return type;
}
static inline LLVMTypeRef create_fault_type(GenContext *c)
{
LLVMTypeRef type = LLVMStructCreateNamed(c->context, ".fault");
LLVMTypeRef typeid_type = llvm_get_type(c, type_typeid);
LLVMTypeRef chars_type = llvm_get_type(c, type_chars);
LLVMTypeRef fault_type[] = {
[0] = typeid_type,
[1] = chars_type,
};
LLVMStructSetBody(type, fault_type, 2, false);
return type;
}
void gencontext_begin_module(GenContext *c)
{
assert(!c->module && "Expected no module");
const char *result = module_create_object_file_name(c->code_module);
c->ir_filename = strformat("%s.ll", result);
c->object_filename = strformat("%s%s", result, get_object_extension());
c->ir_filename = str_printf("%s.ll", result);
if (active_target.llvm_file_dir) c->ir_filename = file_append_path(active_target.llvm_file_dir, c->ir_filename);
c->object_filename = str_printf("%s%s", result, get_object_extension());
if (active_target.object_file_dir) c->object_filename = file_append_path(active_target.object_file_dir, c->object_filename);
c->panicfn = global_context.panic_fn;
c->module = LLVMModuleCreateWithNameInContext(c->code_module->name->module, c->context);
c->machine = llvm_target_machine_create();
@@ -54,13 +86,40 @@ void gencontext_begin_module(GenContext *c)
c->block_global_unique_count = 0;
c->ast_alloca_addr_space = target_alloca_addr_space();
VECEACH(global_context.type, i)
{
global_context.type[i]->backend_type = NULL;
global_context.type[i]->backend_debug_type = NULL;
global_context.type[i]->backend_typeid = NULL;
Type *type = global_context.type[i];
type->backend_type = NULL;
type->backend_debug_type = NULL;
type->backend_typeid = NULL;
switch (type->type_kind)
{
case TYPE_ENUM:
case TYPE_FAULTTYPE:
{
Decl **values = type->decl->enums.values;
VECEACH(values, j)
{
values[j]->backend_ref = NULL;
}
FALLTHROUGH;
}
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_DISTINCT:
type->decl->backend_ref = NULL;
break;
case TYPE_FUNC:
// TODO
default:
break;
}
}
c->bool_type = LLVMInt1TypeInContext(c->context);
c->byte_type = LLVMInt8TypeInContext(c->context);
c->introspect_type = create_introspection_type(c);
c->fault_type = create_fault_type(c);
c->size_type = llvm_get_type(c, type_usize);
if (c->panicfn) c->panicfn->backend_ref = NULL;
if (active_target.debug_info != DEBUG_INFO_NONE)
@@ -105,6 +164,11 @@ void gencontext_init_file_emit(GenContext *c, CompilationUnit *unit)
const char *sysroot = "";
const char *sdk = "";
unsigned dwo_id = 0;
if (c->debug.compile_unit)
{
unit->llvm.debug_compile_unit = c->debug.compile_unit;
return;
}
unit->llvm.debug_compile_unit = LLVMDIBuilderCreateCompileUnit(c->debug.builder,
LLVMDWARFSourceLanguageC,
unit->llvm.debug_file,
@@ -119,15 +183,13 @@ void gencontext_init_file_emit(GenContext *c, CompilationUnit *unit)
emission_kind,
dwo_id,
split_debug_inlining,
emit_debug_info_for_profiling
#if LLVM_VERSION_MAJOR >= 11
,
emit_debug_info_for_profiling,
sysroot,
strlen(sysroot),
sdk,
strlen(sdk)
#endif
);
}
}
@@ -138,9 +200,5 @@ void gencontext_end_file_emit(GenContext *c, CompilationUnit *ast)
void gencontext_end_module(GenContext *context)
{
if (llvm_use_debug(context))
{
LLVMDIBuilderFinalize(context->debug.builder);
}
LLVMDisposeModule(context->module);
}

View File

@@ -24,7 +24,7 @@ void llvm_emit_compound_stmt(GenContext *c, Ast *ast)
/**
* This emits a local declaration.
*/
LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl)
void llvm_emit_local_decl(GenContext *c, Decl *decl, BEValue *value)
{
// 1. Get the declaration and the LLVM type.
Type *var_type = type_lowering(type_no_fail(decl->type));
@@ -35,25 +35,31 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl)
if (decl->var.is_static)
{
// In defers we might already have generated this variable.
if (decl->backend_ref) return decl->backend_ref;
if (decl->backend_ref)
{
llvm_value_set_decl(c, value, decl);
return;
}
void *builder = c->builder;
c->builder = NULL;
decl->backend_ref = LLVMAddGlobal(c->module, alloc_type, "tempglobal");
decl->backend_ref = llvm_add_global_var(c, "tempglobal", var_type, decl->alignment);
if (IS_FAILABLE(decl))
{
scratch_buffer_clear();
scratch_buffer_append(decl->extname);
scratch_buffer_append(".f");
decl->var.failable_ref = LLVMAddGlobal(c->module, llvm_get_type(c, type_anyerr), scratch_buffer_to_string());
scratch_buffer_append("$f");
decl->var.failable_ref = llvm_add_global_var(c, scratch_buffer_to_string(), type_anyerr, 0);
}
llvm_emit_global_variable_init(c, decl);
c->builder = builder;
return decl->backend_ref;
llvm_value_set_decl(c, value, decl);
return;
}
assert(!decl->backend_ref);
llvm_emit_local_var_alloca(c, decl);
Expr *init = decl->var.init_expr;
if (IS_FAILABLE(decl))
bool is_failable = IS_FAILABLE(decl);
if (is_failable)
{
scratch_buffer_clear();
scratch_buffer_append(decl->name);
@@ -64,15 +70,18 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl)
if (init)
{
// If we don't have undef, then make an assign.
if (!decl->var.no_init)
llvm_value_set_decl_address(c, value, decl);
value->kind = BE_ADDRESS;
BEValue res = llvm_emit_assign_expr(c, value, decl->var.init_expr, decl->var.failable_ref);
if (!is_failable) *value = res;
}
else if (decl->var.no_init)
{
llvm_value_set(value, LLVMGetUndef(alloc_type), decl->type);
if (decl->var.failable_ref)
{
BEValue value;
llvm_value_set_decl_address(c, &value, decl);
value.kind = BE_ADDRESS;
llvm_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.failable_ref);
LLVMBuildStore(c->builder, LLVMGetUndef(llvm_get_type(c, type_anyerr)), decl->var.failable_ref);
}
// TODO trap on undef in debug mode.
}
else
{
@@ -86,16 +95,16 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl)
if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER)
{
llvm_emit_store(c, decl, LLVMConstNull(alloc_type));
llvm_value_set(value, LLVMConstNull(alloc_type), type);
}
else
{
BEValue value;
llvm_value_set_decl_address(c, &value, decl);
value.kind = BE_ADDRESS;
llvm_store_zero(c, &value);
llvm_value_set_decl_address(c, value, decl);
value->kind = BE_ADDRESS;
llvm_store_zero(c, value);
llvm_value_set(value, llvm_get_zero(c, type), type);
}
}
return decl->backend_ref;
}
void llvm_emit_decl_expr_list_ignore_result(GenContext *context, Expr *expr)
@@ -124,7 +133,8 @@ void llvm_emit_decl_expr_list(GenContext *context, BEValue *be_value, Expr *expr
if (last->expr_kind == EXPR_DECL)
{
type = last->decl_expr->var.type_info->type;
LLVMValueRef decl_value = llvm_emit_local_decl(context, last->decl_expr);
LLVMValueRef decl_value = llvm_get_ref(context, last->decl_expr);
if (bool_cast && last->decl_expr->var.unwrap)
{
llvm_value_set_bool(be_value, LLVMConstInt(context->bool_type, 1, false));
@@ -218,8 +228,9 @@ static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast)
LLVMBasicBlockRef error_return_block = NULL;
LLVMValueRef error_out = NULL;
c->error_var = c->block_error_var;
c->catch_block = c->block_failable_exit;
BlockExit *exit = *ast->return_stmt.block_exit_ref;
c->error_var = exit->block_error_var;
c->catch_block = exit->block_failable_exit;
LLVMBasicBlockRef err_cleanup_block = NULL;
Expr *ret_expr = ast->return_stmt.expr;
@@ -240,21 +251,21 @@ static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast)
POP_ERROR();
llvm_emit_statement_chain(c, ast->return_stmt.cleanup);
if (c->return_out && return_value.value)
if (exit->block_return_out && return_value.value)
{
llvm_store_value_aligned(c, c->return_out, &return_value, type_alloca_alignment(return_value.type));
llvm_store_value_aligned(c, exit->block_return_out, &return_value, type_alloca_alignment(return_value.type));
}
if (err_cleanup_block)
{
llvm_emit_br(c, c->block_return_exit);
llvm_emit_br(c, exit->block_return_exit);
llvm_emit_block(c, err_cleanup_block);
llvm_emit_statement_chain(c, ast->return_stmt.cleanup);
llvm_emit_jmp(c, c->block_failable_exit);
llvm_emit_jmp(c, exit->block_failable_exit);
}
else
{
llvm_emit_jmp(c, c->block_return_exit);
llvm_emit_jmp(c, exit->block_return_exit);
}
}
@@ -817,14 +828,16 @@ static void gencontext_emit_switch_body(GenContext *c, BEValue *switch_value, As
llvm_value_rvalue(c, &be_value);
case_value = be_value.value;
LLVMAddCase(switch_stmt, case_value, block);
Expr *to = case_stmt->case_stmt.to_expr;
if (to)
Expr *to_expr =case_stmt->case_stmt.to_expr;
if (to_expr)
{
BEValue to_value;
llvm_emit_expr(c, &to_value, case_stmt->case_stmt.to_expr);
assert(LLVMIsAConstant(to_value.value));
llvm_emit_expr(c, &to_value, to_expr);
llvm_value_rvalue(c, &to_value);
LLVMValueRef to = to_value.value;
assert(LLVMIsAConstant(to));
LLVMValueRef one = llvm_const_int(c, to_value.type, 1);
while (LLVMConstIntGetZExtValue(LLVMConstICmp(LLVMIntEQ, to_value.value, case_value)) != 1)
while (LLVMConstIntGetZExtValue(LLVMConstICmp(LLVMIntEQ, to, case_value)) != 1)
{
case_value = LLVMConstAdd(case_value, one);
LLVMAddCase(switch_stmt, case_value, block);
@@ -1018,7 +1031,7 @@ static inline void llvm_emit_asm_stmt(GenContext *c, Ast *ast)
LLVMValueRef asm_fn = LLVMGetInlineAsm(asm_fn_type,
(char *)ast->asm_stmt.body->const_expr.string.chars,
ast->asm_stmt.body->const_expr.string.len,
scratch_buffer_to_string(), global_context.scratch_buffer_len,
scratch_buffer_to_string(), scratch_buffer.len,
ast->asm_stmt.is_volatile,
true,
LLVMInlineAsmDialectIntel
@@ -1051,11 +1064,16 @@ void gencontext_emit_expr_stmt(GenContext *c, Ast *ast)
}
LLVMValueRef llvm_emit_zstring(GenContext *c, const char *str)
{
return llvm_emit_zstring_named(c, str, ".zstr");
}
LLVMValueRef llvm_emit_zstring_named(GenContext *c, const char *str, const char *extname)
{
LLVMTypeRef char_type = llvm_get_type(c, type_char);
unsigned len = (unsigned)strlen(str);
LLVMTypeRef char_array_type = LLVMArrayType(char_type, len + 1);
LLVMValueRef global_string = LLVMAddGlobal(c->module, char_array_type, "");
LLVMValueRef global_string = llvm_add_global_type(c, extname, char_array_type, 0);
llvm_set_internal_linkage(global_string);
LLVMSetGlobalConstant(global_string, 1);
LLVMSetInitializer(global_string, LLVMConstStringInContext(c->context, str, len, 0));
@@ -1134,8 +1152,11 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
gencontext_emit_expr_stmt(c, ast);
break;
case AST_DECLARE_STMT:
llvm_emit_local_decl(c, ast->declare_stmt);
{
BEValue value;
llvm_emit_local_decl(c, ast->declare_stmt, &value);
break;
}
case AST_BREAK_STMT:
llvm_emit_break(c, ast);
break;

View File

@@ -222,12 +222,9 @@ static inline void add_func_type_param(GenContext *context, Type *param_type, AB
arg_info->param_index_end = (MemberIndex)vec_size(*params);
}
LLVMTypeRef llvm_func_type(GenContext *context, FunctionPrototype *prototype)
LLVMTypeRef llvm_update_prototype_abi(GenContext *context, FunctionPrototype *prototype, LLVMTypeRef **params)
{
LLVMTypeRef *params = NULL;
LLVMTypeRef return_type = NULL;
LLVMTypeRef retval = NULL;
Type *call_return_type = prototype->abi_ret_type;
ABIArgInfo *ret_arg_info = prototype->ret_abi_info;
@@ -239,62 +236,68 @@ LLVMTypeRef llvm_func_type(GenContext *context, FunctionPrototype *prototype)
case ABI_ARG_EXPAND:
UNREACHABLE;
case ABI_ARG_INDIRECT:
vec_add(params, llvm_get_ptr_type(context, call_return_type));
return_type = llvm_get_type(context, type_void);
vec_add(*params, llvm_get_ptr_type(context, call_return_type));
retval = llvm_get_type(context, type_void);
break;
case ABI_ARG_EXPAND_COERCE:
{
LLVMTypeRef lo = llvm_abi_type(context, ret_arg_info->direct_pair.lo);
if (!abi_type_is_valid(ret_arg_info->direct_pair.hi))
{
return_type = lo;
retval = lo;
break;
}
LLVMTypeRef hi = llvm_abi_type(context, ret_arg_info->direct_pair.hi);
return_type = llvm_get_twostruct(context, lo, hi);
retval = llvm_get_twostruct(context, lo, hi);
break;
}
case ABI_ARG_IGNORE:
return_type = llvm_get_type(context, type_void);
retval = llvm_get_type(context, type_void);
break;
case ABI_ARG_DIRECT_PAIR:
{
LLVMTypeRef lo = llvm_abi_type(context, ret_arg_info->direct_pair.lo);
LLVMTypeRef hi = llvm_abi_type(context, ret_arg_info->direct_pair.hi);
return_type = llvm_get_twostruct(context, lo, hi);
retval = llvm_get_twostruct(context, lo, hi);
break;
}
case ABI_ARG_DIRECT:
return_type = llvm_get_type(context, call_return_type);
retval = llvm_get_type(context, call_return_type);
break;
case ABI_ARG_DIRECT_SPLIT_STRUCT:
UNREACHABLE
case ABI_ARG_DIRECT_COERCE_INT:
return_type = LLVMIntTypeInContext(context->context, type_size(call_return_type) * 8);
retval = LLVMIntTypeInContext(context->context, type_size(call_return_type) * 8);
break;
case ABI_ARG_DIRECT_COERCE:
return_type = llvm_get_type(context, ret_arg_info->direct_coerce_type);
retval = llvm_get_type(context, ret_arg_info->direct_coerce_type);
break;
}
// If it's failable and it's not void (meaning ret_abi_info will be NULL)
if (prototype->ret_by_ref)
{
add_func_type_param(context, type_get_ptr(type_lowering(prototype->ret_by_ref_type)), prototype->ret_by_ref_abi_info, &params);
add_func_type_param(context, type_get_ptr(type_lowering(prototype->ret_by_ref_type)), prototype->ret_by_ref_abi_info, params);
}
// Add in all of the required arguments.
VECEACH(prototype->params, i)
{
add_func_type_param(context, prototype->params[i], prototype->abi_args[i], &params);
add_func_type_param(context, prototype->params[i], prototype->abi_args[i], params);
}
VECEACH(prototype->varargs, i)
{
add_func_type_param(context, prototype->varargs[i], prototype->abi_varargs[i], &params);
add_func_type_param(context, prototype->varargs[i], prototype->abi_varargs[i], params);
}
return retval;
}
return LLVMFunctionType(return_type, params, vec_size(params), prototype->variadic == VARIADIC_RAW);
LLVMTypeRef llvm_func_type(GenContext *context, FunctionPrototype *prototype)
{
LLVMTypeRef *params = NULL;
LLVMTypeRef ret = llvm_update_prototype_abi(context, prototype, &params);
return LLVMFunctionType(ret, params, vec_size(params), prototype->variadic == VARIADIC_RAW);
}
@@ -447,3 +450,334 @@ LLVMTypeRef llvm_abi_type(GenContext *c, AbiType type)
if (abi_type_is_type(type)) return llvm_get_type(c, type.type);
return LLVMIntTypeInContext(c->context, type.int_bits_plus_1 - 1);
}
static inline Module *type_base_module(Type *type)
{
RETRY:
switch (type->type_kind)
{
case TYPE_POISONED:
case TYPE_VOID:
case ALL_INTS:
case ALL_FLOATS:
case TYPE_BOOL:
case TYPE_ANY:
case TYPE_ANYERR:
case TYPE_TYPEID:
return NULL;
case TYPE_POINTER:
type = type->pointer;
goto RETRY;
case TYPE_ENUM:
case TYPE_FUNC:
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_BITSTRUCT:
case TYPE_FAULTTYPE:
case TYPE_DISTINCT:
return type->decl->unit->module;
case TYPE_TYPEDEF:
type = type->canonical;
goto RETRY;
case TYPE_ARRAY:
case TYPE_SUBARRAY:
case TYPE_INFERRED_ARRAY:
case TYPE_FLEXIBLE_ARRAY:
case TYPE_VECTOR:
type = type->array.base;
goto RETRY;
case TYPE_FAILABLE:
type = type->failable;
goto RETRY;
case TYPE_UNTYPED_LIST:
case TYPE_FAILABLE_ANY:
case TYPE_TYPEINFO:
UNREACHABLE
}
UNREACHABLE
}
static inline LLVMValueRef llvm_generate_temp_introspection_global(GenContext *c, Type *type)
{
assert(!type->backend_typeid);
LLVMValueRef temp = LLVMAddGlobal(c->module, c->introspect_type, "tempid");
type->backend_typeid = LLVMConstPointerCast(temp, llvm_get_type(c, type_typeid));
return temp;
}
static inline LLVMValueRef llvm_generate_introspection_global(GenContext *c, LLVMValueRef original_global, Type *type, IntrospectType introspect_type,
Type *inner, size_t len, LLVMValueRef additional, bool is_external)
{
if (original_global)
{
assert(type->backend_typeid);
}
LLVMValueRef values[INTROSPECT_INDEX_TOTAL] = {
[INTROSPECT_INDEX_KIND] = llvm_const_int(c, type_char, introspect_type),
[INTROSPECT_INDEX_SIZEOF] = llvm_const_int(c, type_usize, type_size(type)),
[INTROSPECT_INDEX_INNER] = inner ? llvm_get_typeid(c, inner) : llvm_get_zero(c, type_typeid),
[INTROSPECT_INDEX_LEN] = llvm_const_int(c, type_usize, len),
[INTROSPECT_INDEX_ADDITIONAL] = additional ? additional : LLVMConstArray(llvm_get_type(c, type_typeid), NULL, 0)
};
LLVMValueRef global_name;
scratch_buffer_clear();
scratch_buffer_append("ct$");
type_mangle_introspect_name_to_buffer(type);
if (additional)
{
LLVMValueRef constant = LLVMConstStructInContext(c->context, values, INTROSPECT_INDEX_TOTAL, false);
global_name = LLVMAddGlobal(c->module, LLVMTypeOf(constant), scratch_buffer_to_string());
LLVMSetInitializer(global_name, constant);
}
else
{
LLVMValueRef strukt = LLVMConstNamedStruct(c->introspect_type, values, INTROSPECT_INDEX_TOTAL);
global_name = LLVMAddGlobal(c->module, c->introspect_type, scratch_buffer_to_string());
LLVMSetInitializer(global_name, strukt);
}
LLVMSetAlignment(global_name, llvm_abi_alignment(c, c->introspect_type));
LLVMSetGlobalConstant(global_name, 1);
if (is_external)
{
LLVMSetLinkage(global_name, LLVMExternalLinkage);
}
else
{
llvm_set_linkonce(c, global_name);
}
if (original_global)
{
LLVMReplaceAllUsesWith(original_global, global_name);
LLVMDeleteGlobal(original_global);
}
else
{
type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(c, type_typeid));
}
return type->backend_typeid;
}
static LLVMValueRef llvm_get_introspection_for_builtin_type(GenContext *c, Type *type, IntrospectType introspect_type, int bits)
{
return llvm_generate_introspection_global(c, NULL, type, introspect_type, NULL, 0, NULL, false);
}
static LLVMValueRef llvm_get_introspection_for_enum(GenContext *c, Type *type)
{
Decl *decl = type->decl;
bool is_external = decl->unit->module != c->code_module;
bool is_dynamic = decl->is_dynamic;
Decl **enum_vals = decl->enums.values;
unsigned elements = vec_size(enum_vals);
Decl **associated_values = decl->enums.parameters;
unsigned associated_value_count = vec_size(associated_values);
if (is_external && is_dynamic)
{
elements = 0;
}
if (!is_dynamic) is_external = false;
LLVMTypeRef subarray = llvm_get_type(c, type_chars);
LLVMValueRef *values = elements ? malloc_arena(elements * sizeof(LLVMValueRef)) : NULL;
bool obfuscate = decl->obfuscate;
for (unsigned i = 0; i < elements; i++)
{
BEValue value;
const char *name = enum_vals[i]->name;
size_t len = strlen(name);
scratch_buffer_clear();
scratch_buffer_append(".enum.");
scratch_buffer_append_unsigned_int(i);
const char *name_desc = scratch_buffer_to_string();
if (obfuscate)
{
len = strlen(name_desc);
name = name_desc;
}
LLVMValueRef name_ref = llvm_emit_zstring_named(c, name, scratch_buffer_to_string());
LLVMValueRef data[2] = { name_ref, llvm_const_int(c, type_usize, len) };
values[i] = LLVMConstNamedStruct(subarray, data, 2);
}
LLVMValueRef names = LLVMConstArray(subarray, values, elements);
LLVMValueRef val = llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_ENUM, type_flatten(type), elements, names, is_external);
LLVMTypeRef val_type;
VECEACH(associated_values, ai)
{
val_type = NULL;
bool mixed = false;
for (unsigned i = 0; i < elements; i++)
{
BEValue value;
llvm_emit_expr(c, &value, enum_vals[i]->enum_constant.args[ai]);
assert(!llvm_value_is_addr(&value));
LLVMValueRef llvm_value = llvm_value_is_bool(&value) ? LLVMConstZExt(value.value, c->byte_type)
: value.value;
values[i] = llvm_value;
if (!val_type)
{
val_type = LLVMTypeOf(llvm_value);
continue;
}
if (val_type != LLVMTypeOf(llvm_value)) mixed = true;
}
Decl *associated_value = associated_values[ai];
LLVMValueRef associated_value_arr = mixed ? LLVMConstStruct(values, elements, true) : LLVMConstArray(val_type,
values,
elements);
scratch_buffer_clear();
scratch_buffer_append(decl->extname);
scratch_buffer_append("$");
scratch_buffer_append(associated_value->name);
LLVMValueRef global_ref = llvm_add_global_type(c,
scratch_buffer_to_string(),
LLVMTypeOf(associated_value_arr),
0);
llvm_set_linkonce(c, global_ref);
LLVMSetInitializer(global_ref, associated_value_arr);
LLVMSetGlobalConstant(global_ref, true);
if (mixed)
{
LLVMTypeRef cast_type = llvm_get_ptr_type(c, type_get_array(associated_value->type, elements));
associated_value->backend_ref = LLVMConstBitCast(global_ref, cast_type);
}
else
{
associated_value->backend_ref = global_ref;
}
}
return val;
}
static LLVMValueRef llvm_get_introspection_for_struct_union(GenContext *c, Type *type)
{
Decl *decl = type->decl;
Decl **decls = decl->strukt.members;
LLVMValueRef ref = llvm_generate_temp_introspection_global(c, type);
VECEACH(decls, i)
{
Decl *member_decl = decls[i];
if (decl_is_struct_type(member_decl))
{
llvm_get_typeid(c, member_decl->type);
}
}
return llvm_generate_introspection_global(c, ref, type, decl->decl_kind == DECL_UNION ? INTROSPECT_TYPE_UNION : INTROSPECT_TYPE_STRUCT,
NULL,
vec_size(decls), NULL, false);
}
static LLVMValueRef llvm_get_introspection_for_fault(GenContext *c, Type *type)
{
Decl *decl = type->decl;
Decl **fault_vals = decl->enums.values;
unsigned elements = vec_size(fault_vals);
LLVMValueRef ref = llvm_generate_temp_introspection_global(c, type);
AlignSize store_align;
for (unsigned i = 0; i < elements; i++)
{
scratch_buffer_clear();
scratch_buffer_append(decl_get_extname(decl));
scratch_buffer_append_char('$');
Decl *val = fault_vals[i];
scratch_buffer_append(val->name);
LLVMValueRef global_name = LLVMAddGlobal(c->module, c->fault_type, scratch_buffer_to_string());
LLVMSetAlignment(global_name, LLVMPreferredAlignmentOfGlobal(c->target_data, global_name));
LLVMSetGlobalConstant(global_name, 1);
LLVMValueRef vals[2] = { LLVMConstPtrToInt(ref, llvm_get_type(c, type_typeid)),
llvm_emit_aggregate_two(c, type_chars, llvm_emit_zstring_named(c, val->name, ".fault"),
llvm_const_int(c, type_usize, strlen(val->name))) };
LLVMSetInitializer(global_name, LLVMConstNamedStruct(c->fault_type, vals, 2));
llvm_set_linkonce(c, global_name);
val->backend_ref = LLVMConstPointerCast(global_name, llvm_get_type(c, type_typeid));
}
LLVMTypeRef element_type = llvm_get_type(c, type_typeid);
LLVMValueRef* values = elements ? MALLOC(sizeof(LLVMValueRef) * elements) : NULL;
for (unsigned i = 0; i < elements; i++)
{
values[i] = LLVMConstBitCast(fault_vals[i]->backend_ref, element_type);
}
return llvm_generate_introspection_global(c, ref, type, INTROSPECT_TYPE_FAULT, NULL, elements, NULL, false);
}
LLVMValueRef llvm_get_typeid(GenContext *c, Type *type)
{
if (type->backend_typeid) return type->backend_typeid;
switch (type->type_kind)
{
case TYPE_FAILABLE:
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_FAILABLE, type->failable, 0, NULL, false);
case TYPE_FLEXIBLE_ARRAY:
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_ARRAY, type->array.base, 0, NULL, false);
case TYPE_VECTOR:
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_VECTOR, type->array.base, type->array.len, NULL, false);
case TYPE_ARRAY:
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_ARRAY, type->array.base, type->array.len, NULL, false);
case TYPE_SUBARRAY:
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_SUBARRAY, type->array.base, 0, NULL, false);
case TYPE_POINTER:
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_POINTER, type->pointer, 0, NULL, false);
case TYPE_DISTINCT:
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_DISTINCT, type->decl->distinct_decl.base_type, 0, NULL, false);
case TYPE_ENUM:
return llvm_get_introspection_for_enum(c, type);
case TYPE_FAULTTYPE:
return llvm_get_introspection_for_fault(c, type);
case TYPE_STRUCT:
case TYPE_UNION:
return llvm_get_introspection_for_struct_union(c, type);
case TYPE_FUNC:
{
LLVMValueRef ref = llvm_generate_temp_introspection_global(c, type);
return llvm_generate_introspection_global(c, ref, type, INTROSPECT_TYPE_FUNC, NULL, 0, NULL, false);
}
case TYPE_BITSTRUCT:
{
LLVMValueRef ref = llvm_generate_temp_introspection_global(c, type);
return llvm_generate_introspection_global(c, ref, type, INTROSPECT_TYPE_BITSTRUCT, NULL, 0, NULL, false);
}
case TYPE_TYPEDEF:
return llvm_get_typeid(c, type->canonical);
case TYPE_INFERRED_ARRAY:
case TYPE_UNTYPED_LIST:
case TYPE_FAILABLE_ANY:
case TYPE_TYPEINFO:
UNREACHABLE
case TYPE_VOID:
return llvm_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_VOID, 0);
case TYPE_BOOL:
return llvm_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_BOOL, 0);
case ALL_SIGNED_INTS:
return llvm_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_SIGNED_INT,
type_kind_bitsize(type->type_kind));
case ALL_UNSIGNED_INTS:
return llvm_get_introspection_for_builtin_type(c,
type,
INTROSPECT_TYPE_UNSIGNED_INT,
type_kind_bitsize(type->type_kind));
case ALL_FLOATS:
return llvm_get_introspection_for_builtin_type(c,
type,
INTROSPECT_TYPE_FLOAT,
type_kind_bitsize(type->type_kind));
case TYPE_ANYERR:
return llvm_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_ANYERR, 0);
case TYPE_ANY:
return llvm_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_ANY, 0);
case TYPE_TYPEID:
return llvm_get_introspection_for_builtin_type(c, type, INTROSPECT_TYPE_TYPEID, 0);
case TYPE_POISONED:
UNREACHABLE
}
UNREACHABLE
}

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