mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04695489b4 | ||
|
|
331f9b23f8 | ||
|
|
9886d381c0 | ||
|
|
12c17b62cf | ||
|
|
2698ba1a94 | ||
|
|
6f5f5feb97 | ||
|
|
64d883cb99 | ||
|
|
1adc8b8264 | ||
|
|
c02ce5ce2a | ||
|
|
e36e4b60e4 | ||
|
|
1d808be4b9 | ||
|
|
7065c28a08 | ||
|
|
da4df9d626 | ||
|
|
a7e4dda360 | ||
|
|
4471ccff13 | ||
|
|
cdff5c3e26 | ||
|
|
cc1bc58ed0 | ||
|
|
812bd8b3d0 | ||
|
|
7e0a29ef40 | ||
|
|
c1de3f059e | ||
|
|
62c1d2ddb5 | ||
|
|
b313bec69d | ||
|
|
036859c0c8 | ||
|
|
56a6e0b112 | ||
|
|
18f7f35e80 | ||
|
|
1d572f3e7c | ||
|
|
002ee006c1 | ||
|
|
8afbccd3fe | ||
|
|
6576725ed8 | ||
|
|
d3a053e049 | ||
|
|
4afec24434 | ||
|
|
29edd6e54e | ||
|
|
547d30eb1e | ||
|
|
6cf3c9f46b | ||
|
|
4beb7eff8f | ||
|
|
48a31cfa48 | ||
|
|
cd1138447e | ||
|
|
c29ad77cdb | ||
|
|
1c15ebe6d2 | ||
|
|
a68efec5e8 | ||
|
|
3f6b0646b3 | ||
|
|
28a8e17690 | ||
|
|
2a7d46844a | ||
|
|
92542ac1f9 | ||
|
|
59b41f8deb | ||
|
|
abfccb5576 | ||
|
|
ea5d7cd2e7 | ||
|
|
ca21b1daac | ||
|
|
9fdd66af42 | ||
|
|
d403912ec7 | ||
|
|
05f222616e | ||
|
|
253dbf3603 | ||
|
|
cfbfc29e84 | ||
|
|
bb020a1752 | ||
|
|
c8a614e43f | ||
|
|
bb28f6e61c | ||
|
|
b1d83e2ccd | ||
|
|
df41caabdd | ||
|
|
2f5d51c92c | ||
|
|
224390ce5a | ||
|
|
09d50ebf6c | ||
|
|
2d608a4d51 | ||
|
|
d511f150a7 | ||
|
|
f4dc4f64f2 | ||
|
|
6035cb4600 | ||
|
|
3d1eaad6b9 | ||
|
|
ca2bb505b6 | ||
|
|
8b4b4273cc | ||
|
|
7c91c56f3d | ||
|
|
5edafc5b2f | ||
|
|
dbb0dc302d | ||
|
|
e09e5c06d3 | ||
|
|
b0c55ff777 | ||
|
|
60832019bd | ||
|
|
42b5445225 | ||
|
|
9691d50a6f | ||
|
|
29a9769651 | ||
|
|
15e1db78a7 | ||
|
|
2f23d56a12 | ||
|
|
22ee082d00 | ||
|
|
212bc7d9af | ||
|
|
890c4bc435 | ||
|
|
7df7dd2933 | ||
|
|
ada8652209 | ||
|
|
bf8288ed1c | ||
|
|
9b6e4f9d11 | ||
|
|
ecdcd8f959 | ||
|
|
828724f593 | ||
|
|
f6eb20f725 | ||
|
|
ade4065480 | ||
|
|
b19cef4bc1 | ||
|
|
151cbfd706 | ||
|
|
b99db4be24 |
90
.github/workflows/main.yml
vendored
90
.github/workflows/main.yml
vendored
@@ -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: |
|
||||
|
||||
151
CMakeLists.txt
151
CMakeLists.txt
@@ -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)
|
||||
|
||||
49
README.md
49
README.md
@@ -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.
|
||||
|
||||
@@ -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)];
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
*/
|
||||
182
lib/std/core/allocators/dynamic_arena.c3
Normal file
182
lib/std/core/allocators/dynamic_arena.c3
Normal 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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
82
lib/std/core/allocators/memory_arena.c3
Normal file
82
lib/std/core/allocators/memory_arena.c3
Normal 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
115
lib/std/core/builtin.c3
Normal 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!;
|
||||
}
|
||||
76
lib/std/core/builtin_comparison.c3
Normal file
76
lib/std/core/builtin_comparison.c3
Normal 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;
|
||||
}
|
||||
@@ -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
404
lib/std/core/conv.c3
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
151
lib/std/core/mem_allocator.c3
Normal file
151
lib/std/core/mem_allocator.c3
Normal 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
22
lib/std/core/mem_array.c3
Normal 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];
|
||||
}
|
||||
@@ -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;
|
||||
@@ -1,5 +1,4 @@
|
||||
module std::os::linux;
|
||||
import std::env;
|
||||
module std::core::os::linux;
|
||||
|
||||
$if (env::OS_TYPE == OsType.LINUX):
|
||||
|
||||
@@ -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();
|
||||
@@ -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
160
lib/std/core/str.c3
Normal 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
299
lib/std/core/string.c3
Normal 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)!!;
|
||||
}
|
||||
25
lib/std/core/string_iterator.c3
Normal file
25
lib/std/core/string_iterator.c3
Normal 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
159
lib/std/core/types.c3
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
379
lib/std/io.c3
379
lib/std/io.c3
@@ -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
902
lib/std/io_printf.c3
Normal 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(¶m, float_from_variant(current))?;
|
||||
continue;
|
||||
case 'E':
|
||||
param.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'e':
|
||||
etoa(¶m, float_from_variant(current))?;
|
||||
continue;
|
||||
case 'G':
|
||||
param.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'g':
|
||||
param.flags.adapt_exp = true;
|
||||
etoa(¶m, float_from_variant(current))?;
|
||||
continue;
|
||||
case 'c':
|
||||
out_char(¶m, current)?;
|
||||
continue;
|
||||
case 's':
|
||||
out_str(¶m, 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(¶m, v, is_neg, base)?;
|
||||
}
|
||||
// termination
|
||||
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
return param.idx;
|
||||
}
|
||||
|
||||
285
lib/std/libc.c3
285
lib/std/libc.c3
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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
391
lib/std/math.matrix.c3
Normal 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,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
105
lib/std/priorityqueue.c3
Normal 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();
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
25
resources/examples/binarydigits.c3
Normal file
25
resources/examples/binarydigits.c3
Normal 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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
28
resources/examples/contextfree/dynscope.c3
Normal file
28
resources/examples/contextfree/dynscope.c3
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
27
resources/examples/contextfree/multi.c3
Normal file
27
resources/examples/contextfree/multi.c3
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
313
resources/examples/raylib/raylib_arkanoid.c3
Normal file
313
resources/examples/raylib/raylib_arkanoid.c3
Normal 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();
|
||||
}
|
||||
251
resources/examples/raylib/raylib_snake.c3
Normal file
251
resources/examples/raylib/raylib_snake.c3
Normal 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();
|
||||
}
|
||||
|
||||
793
resources/examples/raylib/raylib_tetris.c3
Normal file
793
resources/examples/raylib/raylib_tetris.c3
Normal 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;
|
||||
}
|
||||
|
||||
44
resources/examples/retry.c3
Normal file
44
resources/examples/retry.c3
Normal 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");
|
||||
}
|
||||
}
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
20
resources/examples/swap.c3
Normal file
20
resources/examples/swap.c3
Normal 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);
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
792
resources/testfragments/raylibtest.c3
Normal file
792
resources/testfragments/raylibtest.c3
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -16,12 +16,16 @@
|
||||
// c compiler
|
||||
"cc": "cc",
|
||||
// c sources
|
||||
"csources": [
|
||||
"./csource/**"
|
||||
],
|
||||
"targets": {
|
||||
"hello_world": {
|
||||
"type": "executable",
|
||||
"csources": [
|
||||
"./csource/**"
|
||||
]
|
||||
},
|
||||
"hello_world_win32": {
|
||||
"type": "executable",
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
@@ -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/**"]
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -148,6 +148,13 @@ Ast *ast_macro_copy(Ast *source_ast)
|
||||
return ast_copy_deep(©_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(©_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, ©->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, ©->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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
190
src/compiler/libraries.c
Normal 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]));
|
||||
}
|
||||
}
|
||||
@@ -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++)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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(®s, params[i], false, is_vector_call, is_reg_call);
|
||||
}
|
||||
prototype->abi_args = args;
|
||||
}
|
||||
prototype->abi_args = win64_create_params(prototype->params, ®s, is_vector_call, is_reg_call);
|
||||
prototype->abi_varargs = win64_create_params(prototype->varargs, ®s, is_vector_call, is_reg_call);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ®s, params[i]);
|
||||
}
|
||||
prototype->abi_args = args;
|
||||
}
|
||||
}
|
||||
prototype->abi_args = x86_create_params(prototype->call_abi, prototype->params, ®s);
|
||||
prototype->abi_varargs = x86_create_params(prototype->call_abi, prototype->params, ®s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ¶ms);
|
||||
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], ¶ms);
|
||||
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], ¶ms);
|
||||
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, ¶ms);
|
||||
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
Reference in New Issue
Block a user