Initial commit
This commit is contained in:
commit
91d54c58d5
42 changed files with 2212 additions and 0 deletions
22
.clang-format
Normal file
22
.clang-format
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
BasedOnStyle: Google
|
||||
Language: Cpp
|
||||
Standard: c++20
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 120
|
||||
InsertNewlineAtEOF: True
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortCaseLabelsOnASingleLine: False
|
||||
AllowShortCompoundRequirementOnASingleLine: True
|
||||
AllowShortEnumsOnASingleLine: True
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
UseTab: Always
|
||||
FixNamespaceComments: True
|
||||
BreakAfterAttributes: Always
|
||||
BreakTemplateDeclarations: Yes
|
||||
PackConstructorInitializers: NextLineOnly
|
||||
SeparateDefinitionBlocks: Always
|
||||
ReflowComments: Always
|
||||
RequiresExpressionIndentation: OuterScope
|
||||
...
|
||||
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
*
|
||||
!/.gitignore
|
||||
|
||||
!/README.md
|
||||
!/CMakeLists.txt
|
||||
!/.clang-format
|
||||
|
||||
!/src/
|
||||
!/src/**/
|
||||
!/src/**/*.cppm
|
||||
|
||||
!/test/
|
||||
!/test/**/
|
||||
!/test/**/*.cpp
|
||||
94
CMakeLists.txt
Normal file
94
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
cmake_minimum_required(VERSION 3.28)
|
||||
project(bedrock VERSION 0.1 LANGUAGES CXX)
|
||||
|
||||
find_program(
|
||||
CLANG_TIDY_COMMAND
|
||||
NAMES
|
||||
clang-tidy
|
||||
HINTS
|
||||
/opt/homebrew/opt/llvm/bin/
|
||||
NO_CACHE
|
||||
REQUIRED
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
add_compile_options(
|
||||
-Weverything
|
||||
-Werror
|
||||
-Wno-c++98-compat
|
||||
-Wno-c++98-compat-pedantic
|
||||
-Wno-poison-system-directories
|
||||
-Wno-explicit-specialization-storage-class
|
||||
-Wno-unsafe-buffer-usage
|
||||
-Wno-padded
|
||||
-Wno-global-constructors
|
||||
-Wno-exit-time-destructors
|
||||
-fno-exceptions
|
||||
-fcheck-new
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE BEDROCK_NUMBERS "src/numbers.cppm")
|
||||
file(GLOB_RECURSE BEDROCK_FOUNDATION "src/foundation/*.cppm" "src/foundation.cppm")
|
||||
file(GLOB_RECURSE BEDROCK_SEMANTICS "src/semantics/*.cppm" "src/semantics.cppm")
|
||||
file(GLOB_RECURSE BEDROCK_COLLECTIONS "src/collections/*.cppm" "src/collections.cppm")
|
||||
file(GLOB_RECURSE BEDROCK_IO "src/io/*.cppm" "src/io.cppm")
|
||||
file(GLOB_RECURSE BEDROCK_CLI "src/cli/*.cppm" "src/cli.cppm")
|
||||
file(GLOB_RECURSE BEDROCK_TEST "src/test/*.cppm" "src/test.cppm")
|
||||
add_library(bedrock)
|
||||
target_sources(bedrock
|
||||
PUBLIC
|
||||
FILE_SET CXX_MODULES FILES
|
||||
${BEDROCK_NUMBERS}
|
||||
${BEDROCK_SEMANTICS}
|
||||
${BEDROCK_FOUNDATION}
|
||||
${BEDROCK_COLLECTIONS}
|
||||
${BEDROCK_IO}
|
||||
${BEDROCK_CLI}
|
||||
${BEDROCK_TEST}
|
||||
src/bedrock.cppm
|
||||
)
|
||||
set_target_properties(bedrock PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}"
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE BEDROCK_TEST_SRCS "src/*.test.cpp")
|
||||
add_executable(bedrock-tests ${BEDROCK_TEST_SRCS})
|
||||
target_link_libraries(bedrock-tests PUBLIC bedrock)
|
||||
|
||||
#include(FetchContent)
|
||||
#
|
||||
#fetchcontent_declare(
|
||||
# Catch2
|
||||
# GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||
# GIT_TAG v3.4.0
|
||||
# SYSTEM
|
||||
#)
|
||||
#fetchcontent_makeavailable(Catch2)
|
||||
#list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
||||
#
|
||||
#include(CTest)
|
||||
#include(Catch)
|
||||
#
|
||||
#file(GLOB_RECURSE BEDROCK_TESTS "src/*.test.cpp")
|
||||
#add_executable(bedrock-tests
|
||||
# ${BEDROCK_TESTS}
|
||||
#)
|
||||
#target_compile_options(bedrock-tests PUBLIC
|
||||
# -Weverything
|
||||
# -Werror
|
||||
# -Wno-c++98-compat
|
||||
# -Wno-c++98-compat-pedantic
|
||||
# -Wno-poison-system-directories
|
||||
# -Wno-unused-member-function
|
||||
# -Wno-explicit-specialization-storage-class
|
||||
#)
|
||||
#target_link_libraries(bedrock-tests
|
||||
# PUBLIC bedrock
|
||||
# PRIVATE Catch2::Catch2WithMain
|
||||
#)
|
||||
#catch_discover_tests(bedrock-tests)
|
||||
12
README.md
Normal file
12
README.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Bedrock: A Modern C++ Stdlib Alternative
|
||||
|
||||
The C++ stdlib sacrifices readability and ease of use for flexibility and performance. Bedrock takes a different approach: it stresses ease of use and minimalistic abstractions for common use cases, resulting in code that is easier to read, understand, and test.
|
||||
|
||||
Explicit goals for Bedrock are:
|
||||
|
||||
1. **No implicit copies:** move semantics are given first-party support in Bedrock, and copies should never be done implicitly except for extremely simple structures where the performance cost is minimal.
|
||||
2. **Easy to understand semantics:** concepts are heavily used throughout Bedrock to enforce high-level semantics, such as "Moveable", "Iterable", etc., and those concepts encompass all related behavior and best practices (e.g. "Movable" implies both move-constructable and move-assignable).
|
||||
3. **Modern data structures & abstractions:** includes optional types, result types, and a robust iterator library.
|
||||
4. **Everything is a buffer or view:** C++ is all about giving developers low-level access to memory, and Bedrock doesn't try to hide the fact that all data structures represent some kind of memory view or buffer.
|
||||
|
||||
Bedrock is currently in a HIGHLY experimental state and is being built by someone with limited C++ and stdlib experience, so USE AT YOUR OWN RISK! Changes in the 0.x version range can and WILL break existing code.
|
||||
7
src/bedrock.cppm
Normal file
7
src/bedrock.cppm
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export module bedrock;
|
||||
|
||||
export import bedrock.numbers;
|
||||
export import bedrock.semantics;
|
||||
export import bedrock.foundation;
|
||||
export import bedrock.cli;
|
||||
export import bedrock.io;
|
||||
3
src/cli.cppm
Normal file
3
src/cli.cppm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export module bedrock.cli;
|
||||
|
||||
export import :program;
|
||||
46
src/cli/program.cppm
Normal file
46
src/cli/program.cppm
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
export module bedrock.cli:program;
|
||||
|
||||
import bedrock.semantics;
|
||||
import bedrock.foundation;
|
||||
import bedrock.collections;
|
||||
import bedrock.io;
|
||||
|
||||
namespace br {
|
||||
|
||||
export struct ProgramArg final {
|
||||
String name;
|
||||
String short_option;
|
||||
bool positional;
|
||||
};
|
||||
|
||||
export struct ProgramOptions final {
|
||||
Sequence<ProgramArg> args;
|
||||
};
|
||||
|
||||
export template <typename... Args>
|
||||
class ProgramArgs final {
|
||||
private:
|
||||
Tuple<Args...> m_args;
|
||||
};
|
||||
|
||||
export class Program final {
|
||||
private:
|
||||
String m_program_name;
|
||||
Sequence<ProgramArg> m_args;
|
||||
|
||||
public:
|
||||
Program(const String program_name, ProgramOptions&& options)
|
||||
: m_program_name(program_name), m_args(move(options.args)) {}
|
||||
|
||||
template <Callable<Result<void>, const Hashmap<String, String>&> C>
|
||||
auto run(int, char**, C op) -> int {
|
||||
auto args = Hashmap<String, String>::withCapacity(0);
|
||||
auto result = op(args);
|
||||
if (result.isFailure()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
4
src/collections.cppm
Normal file
4
src/collections.cppm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export module bedrock.collections;
|
||||
|
||||
export import :hashmap;
|
||||
export import :sequence;
|
||||
87
src/collections/hashmap.cppm
Normal file
87
src/collections/hashmap.cppm
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
export module bedrock.collections:hashmap;
|
||||
|
||||
import bedrock.foundation;
|
||||
import :sequence;
|
||||
|
||||
namespace br {
|
||||
|
||||
inline namespace {
|
||||
|
||||
constexpr float32 max_load_factor = 0.7F;
|
||||
constexpr usize fnv_offset_basis = sizeof(usize) == 8 ? 0xcbf29ce484222325 : 0x811c9dc5;
|
||||
constexpr usize fnv_prime = sizeof(usize) == 8 ? 0x100000001b3 : 0x1000193;
|
||||
|
||||
} // namespace
|
||||
|
||||
inline auto hashFnv1a(const String& key) noexcept -> usize {
|
||||
usize hash = fnv_offset_basis;
|
||||
for (auto byte : static_cast<const BufferView&>(key).bytes()) {
|
||||
hash ^= static_cast<usize>(byte);
|
||||
hash *= fnv_prime;
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
export template <typename Key, typename Value>
|
||||
class Hashmap final {
|
||||
private:
|
||||
struct Slot {
|
||||
Key key;
|
||||
Optional<Value> value;
|
||||
|
||||
[[nodiscard]]
|
||||
auto isUnoccupied() const noexcept -> bool {
|
||||
return key.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
Sequence<Slot> m_slots;
|
||||
|
||||
explicit Hashmap(usize capacity)
|
||||
: m_slots(Sequence<Slot>::withCapacity(capacity * sizeof(Slot))) {}
|
||||
|
||||
[[nodiscard]]
|
||||
auto loadFactor() -> float32 {
|
||||
return static_cast<float32>(m_slots.usedCapacity()) / m_slots.capacity();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto findSlot(const Key& key) const noexcept -> Optional<Slot&> {
|
||||
auto index = hashFnv1a(key) % m_slots.capacity();
|
||||
auto slot = m_slots.lookup(index);
|
||||
auto iteration = 1;
|
||||
while (slot.isPresent()) {
|
||||
slot = m_slots[index + (iteration * iteration)];
|
||||
++iteration;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static auto withCapacity(usize capacity) noexcept -> Hashmap {
|
||||
return Hashmap(capacity);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto lookup(const Key& key) const noexcept -> Optional<const Value&> {
|
||||
return findSlot(key).map([](Slot& slot) -> Value { return slot.value; });
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto insert(const Key& key, const Value&& value) noexcept -> bool {
|
||||
if (loadFactor() >= max_load_factor) {
|
||||
return false;
|
||||
}
|
||||
findSlot(key).tap([=](Slot slot) {
|
||||
slot.key = key;
|
||||
slot.value = value;
|
||||
});
|
||||
}
|
||||
|
||||
auto remove(const Key& key) -> void {
|
||||
auto slot = findSlot(key);
|
||||
slot.key = "";
|
||||
slot.value = Optional<Key>::absent();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
53
src/collections/sequence.cppm
Normal file
53
src/collections/sequence.cppm
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
export module bedrock.collections:sequence;
|
||||
|
||||
import bedrock.numbers;
|
||||
import bedrock.foundation;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename T>
|
||||
class Sequence final {
|
||||
private:
|
||||
Buffer m_items;
|
||||
|
||||
Sequence(usize capacity)
|
||||
: m_items(Buffer::withCapacity(capacity * sizeof(T))) {}
|
||||
|
||||
public:
|
||||
static auto withCapacity(usize capacity) noexcept -> Sequence {
|
||||
return {capacity};
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Sequence(Args&&... items)
|
||||
: m_items(Buffer::withCapacity(sizeof...(items) * sizeof(T))) {
|
||||
(push(forward<T>(items)), ...);
|
||||
}
|
||||
|
||||
auto push(T&& item) noexcept -> bool {
|
||||
return m_items.write(forward<T>(item), m_items.usedSize());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator[](usize index) const noexcept -> Optional<const T&> {
|
||||
return m_items.slice(sizeof(T), index * sizeof(T)).map([](BufferView view) -> const T& {
|
||||
return view.reinterpret<T>();
|
||||
});
|
||||
}
|
||||
|
||||
auto insert(usize index, T&& value) const noexcept -> bool {
|
||||
return m_items.write<T>(value, index * sizeof(T));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto size() const noexcept -> usize {
|
||||
return m_items.usedSize() / sizeof(T);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto capacity() const noexcept -> usize {
|
||||
return m_items.capacity() / sizeof(T);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
14
src/foundation.cppm
Normal file
14
src/foundation.cppm
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export module bedrock.foundation;
|
||||
|
||||
export import :buffer_view;
|
||||
export import :buffer;
|
||||
export import :byte;
|
||||
export import :bytes_iterator;
|
||||
export import :optional;
|
||||
export import :result;
|
||||
export import :string_buffer;
|
||||
export import :string;
|
||||
export import :tuple;
|
||||
export import :iterators.adapter;
|
||||
export import :iterators.enumerate;
|
||||
export import :iterators.reverse;
|
||||
193
src/foundation/buffer.cppm
Normal file
193
src/foundation/buffer.cppm
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
export module bedrock.foundation:buffer;
|
||||
|
||||
import bedrock.numbers;
|
||||
import bedrock.semantics;
|
||||
|
||||
import :byte;
|
||||
import :bytes_iterator;
|
||||
import :iterators.enumerate;
|
||||
import :buffer_view;
|
||||
import :utility;
|
||||
|
||||
namespace br {
|
||||
|
||||
export class Buffer final {
|
||||
private:
|
||||
byte* m_data;
|
||||
usize m_capacity;
|
||||
usize m_used;
|
||||
|
||||
// ====================================================================
|
||||
// Helpers
|
||||
// ====================================================================
|
||||
Buffer(const byte* data, usize size, usize capacity_hint)
|
||||
: m_data(new byte[size]), m_capacity(nearestPowerOf2(capacity_hint)), m_used(size) {
|
||||
for (usize i = 0; i < m_used; i++) {
|
||||
m_data[i] = data == nullptr ? byte{} : data[i];
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto resize(usize size_hint) noexcept -> bool {
|
||||
const usize size = nearestPowerOf2(size_hint);
|
||||
auto* new_buffer = new byte[size];
|
||||
if (new_buffer == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto [byte, i] : bytes() | enumerate()) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
new_buffer[i] = byte;
|
||||
}
|
||||
delete[] m_data;
|
||||
m_data = new_buffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
// ====================================================================
|
||||
// Static Constructors
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
static auto withCapacity(usize capacity) noexcept -> Buffer {
|
||||
return {nullptr, capacity, 0};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static auto copyFrom(const BufferView& view) noexcept -> Buffer {
|
||||
return {view.m_data, view.m_size, view.m_size};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]]
|
||||
auto write(T&& object, usize start_at = 0) noexcept -> bool {
|
||||
if (start_at + sizeof(T) > m_capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* bytes = reinterpret_cast<byte*>(&object);
|
||||
for (usize i = 0; i < sizeof(T); ++i) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
m_data[start_at + i] = bytes[i];
|
||||
}
|
||||
m_used = start_at + sizeof(T);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]]
|
||||
auto write<BufferView>(BufferView&& view, usize start_at) noexcept -> bool {
|
||||
if (start_at + view.size() > m_capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto [byte, i] : view.bytes() | enumerate()) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
m_data[start_at + i] = byte;
|
||||
}
|
||||
m_used = start_at + view.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto slice(usize size, usize offset = 0) const noexcept -> Optional<BufferView> {
|
||||
return static_cast<BufferView>(*this).slice(size, offset);
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Introspection
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto usedSize() const noexcept -> usize {
|
||||
return m_used;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto capacity() const noexcept -> usize {
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto copy() const noexcept -> Buffer {
|
||||
return {m_data, m_used, m_capacity};
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Conversions
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto operator->() const noexcept -> BufferView {
|
||||
return {m_data, m_capacity};
|
||||
}
|
||||
|
||||
operator BufferView() const noexcept {
|
||||
return {m_data, m_used};
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Iterators
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto bytes() const noexcept -> BytesIterator {
|
||||
return {m_data, 0, m_used};
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Moveable
|
||||
// ====================================================================
|
||||
Buffer(Buffer&& other) noexcept
|
||||
: m_data(move(other.m_data)), m_capacity(other.m_capacity), m_used(other.m_used) {}
|
||||
|
||||
auto operator=(Buffer&& other) noexcept -> Buffer& {
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
delete[] m_data;
|
||||
m_capacity = other.m_capacity;
|
||||
m_data = other.m_data;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Buffer() noexcept {
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// !Copyable
|
||||
// ====================================================================
|
||||
Buffer(const Buffer&) = delete;
|
||||
|
||||
auto operator=(const Buffer&) -> Buffer& = delete;
|
||||
|
||||
// ====================================================================
|
||||
// PartialEquivalence
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto operator==(const Buffer& other) const noexcept -> bool {
|
||||
if (m_capacity != other.m_capacity || m_used != other.m_used) {
|
||||
return false;
|
||||
}
|
||||
for (usize i = 0; i < m_capacity; ++i) {
|
||||
if (m_data[i] != other.m_data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator!=(const Buffer& other) const noexcept -> bool {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(Moveable<Buffer>);
|
||||
static_assert(!Copyable<Buffer>);
|
||||
static_assert(PartialEquivalence<Buffer>);
|
||||
|
||||
} // namespace br
|
||||
131
src/foundation/buffer_view.cppm
Normal file
131
src/foundation/buffer_view.cppm
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
export module bedrock.foundation:buffer_view;
|
||||
|
||||
import bedrock.semantics;
|
||||
import :byte;
|
||||
import :optional;
|
||||
import :bytes_iterator;
|
||||
|
||||
namespace br {
|
||||
|
||||
export class BufferView final {
|
||||
private:
|
||||
byte* m_data;
|
||||
usize m_size;
|
||||
|
||||
public:
|
||||
friend class Buffer;
|
||||
|
||||
BufferView(const byte* data, usize size)
|
||||
: m_data(const_cast<byte*>(data)), m_size(size) {}
|
||||
|
||||
template <usize Size>
|
||||
constexpr BufferView(byte const (&bytes)[Size])
|
||||
: BufferView(static_cast<const byte (&)[Size]>(bytes), Size) {}
|
||||
|
||||
[[nodiscard]]
|
||||
auto bytes() const noexcept -> BytesIterator {
|
||||
return {m_data, m_size, 0};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto ptr() const noexcept -> const byte* {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Introspection
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto size() const noexcept -> usize {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
auto operator[](usize index) const noexcept -> Optional<byte> {
|
||||
if (index >= m_size) {
|
||||
return Optional<byte>::absent();
|
||||
}
|
||||
return Optional<byte>::present(m_data[index]);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto slice(usize size, usize offset = 0) const noexcept -> Optional<BufferView> {
|
||||
if (offset + size >= m_size) {
|
||||
return Optional<BufferView>::absent();
|
||||
}
|
||||
return Optional<BufferView>::present({m_data + offset, size});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]]
|
||||
auto reinterpret() const noexcept -> Optional<const T&> {
|
||||
if (sizeof(T) >= m_size) {
|
||||
return Optional<const T&>::absent();
|
||||
}
|
||||
return Optional<const T&>::present(reinterpret_cast<const T&>(m_data));
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Moveable
|
||||
// ====================================================================
|
||||
BufferView(BufferView&& other) noexcept
|
||||
: m_data(other.m_data), m_size(other.m_size) {}
|
||||
|
||||
auto operator=(BufferView&& other) noexcept -> BufferView& {
|
||||
if (&other != this) {
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Copyable
|
||||
// ====================================================================
|
||||
BufferView(const BufferView& other) = default;
|
||||
|
||||
auto operator=(const BufferView& other) noexcept -> BufferView& {
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~BufferView() = default;
|
||||
|
||||
// ====================================================================
|
||||
// Equivalence
|
||||
// ====================================================================
|
||||
auto operator==(const BufferView& other) const noexcept -> bool {
|
||||
return m_data == other.m_data && m_size == other.m_size;
|
||||
}
|
||||
|
||||
auto operator!=(const BufferView& other) const noexcept -> bool {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Iterable
|
||||
// ====================================================================
|
||||
using Iterator = BytesIterator;
|
||||
|
||||
[[nodiscard]]
|
||||
auto begin() const noexcept -> BytesIterator {
|
||||
return {m_data, m_size, 0};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto end() const noexcept -> BytesIterator {
|
||||
return {m_data, m_size, m_size};
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(Moveable<BufferView>);
|
||||
static_assert(Copyable<BufferView>);
|
||||
static_assert(PartialEquivalence<BufferView>);
|
||||
static_assert(Iterable<BufferView>);
|
||||
|
||||
} // namespace br
|
||||
62
src/foundation/byte.cppm
Normal file
62
src/foundation/byte.cppm
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
export module bedrock.foundation:byte;
|
||||
|
||||
import bedrock.numbers;
|
||||
import bedrock.semantics;
|
||||
|
||||
namespace br {
|
||||
|
||||
export enum class byte : uint8 {};
|
||||
|
||||
export constexpr auto operator""_b(unsigned long long value) noexcept -> byte {
|
||||
return static_cast<byte>(static_cast<uint8>(value));
|
||||
}
|
||||
|
||||
export template <Integral IntegerType>
|
||||
constexpr auto operator<<(byte datum, IntegerType shift) noexcept -> byte {
|
||||
return static_cast<byte>(static_cast<uint8>(datum) << shift);
|
||||
}
|
||||
|
||||
export template <Integral IntegerType>
|
||||
constexpr auto operator>>(byte datum, IntegerType shift) noexcept -> byte {
|
||||
return static_cast<byte>(static_cast<uint8>(datum) >> shift);
|
||||
}
|
||||
|
||||
export template <Integral IntegerType>
|
||||
constexpr auto operator<<=(byte& datum, IntegerType shift) noexcept -> byte& {
|
||||
return datum = datum << shift;
|
||||
}
|
||||
|
||||
export template <Integral IntegerType>
|
||||
constexpr auto operator>>=(byte& datum, IntegerType shift) noexcept -> byte& {
|
||||
return datum = datum >> shift;
|
||||
}
|
||||
|
||||
export constexpr auto operator|(byte left, byte right) noexcept -> byte {
|
||||
return static_cast<byte>(static_cast<uint8>(left) | static_cast<uint8>(right));
|
||||
}
|
||||
|
||||
export constexpr auto operator&(byte left, byte right) noexcept -> byte {
|
||||
return static_cast<byte>(static_cast<uint8>(left) & static_cast<uint8>(right));
|
||||
}
|
||||
|
||||
export constexpr auto operator^(byte left, byte right) noexcept -> byte {
|
||||
return static_cast<byte>(static_cast<uint8>(left) ^ static_cast<uint8>(right));
|
||||
}
|
||||
|
||||
export constexpr auto operator~(byte datum) noexcept -> byte {
|
||||
return static_cast<byte>(~static_cast<uint8>(datum));
|
||||
}
|
||||
|
||||
export constexpr auto operator|=(byte& left, byte right) noexcept -> byte& {
|
||||
return left = left | right;
|
||||
}
|
||||
|
||||
export constexpr auto operator&=(byte& left, byte right) noexcept -> byte& {
|
||||
return left = left & right;
|
||||
}
|
||||
|
||||
export constexpr auto operator^=(byte& left, byte right) noexcept -> byte& {
|
||||
return left = left ^ right;
|
||||
}
|
||||
|
||||
} // namespace br
|
||||
90
src/foundation/bytes_iterator.cppm
Normal file
90
src/foundation/bytes_iterator.cppm
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
export module bedrock.foundation:bytes_iterator;
|
||||
|
||||
import bedrock.numbers;
|
||||
import bedrock.semantics;
|
||||
import :byte;
|
||||
|
||||
namespace br {
|
||||
|
||||
export class BytesIterator final {
|
||||
private:
|
||||
byte* m_data;
|
||||
usize m_current_index;
|
||||
usize m_end_index;
|
||||
|
||||
public:
|
||||
BytesIterator(byte* data, usize n_bytes, usize current_index)
|
||||
: m_data(data), m_current_index(current_index), m_end_index(n_bytes) {}
|
||||
|
||||
// ====================================================================
|
||||
// Moveable
|
||||
// ====================================================================
|
||||
BytesIterator(BytesIterator&& other)
|
||||
: BytesIterator(other.m_data, other.m_end_index, other.m_current_index) {}
|
||||
|
||||
auto operator=(BytesIterator&& other) noexcept -> BytesIterator& {
|
||||
m_data = other.m_data;
|
||||
m_end_index = other.m_end_index;
|
||||
m_current_index = other.m_current_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~BytesIterator() = default;
|
||||
|
||||
// ====================================================================
|
||||
// !Copyable
|
||||
// ====================================================================
|
||||
BytesIterator(const BytesIterator& other) = delete;
|
||||
|
||||
auto operator=(const BytesIterator&) = delete;
|
||||
|
||||
// ====================================================================
|
||||
// Iterator
|
||||
// ====================================================================
|
||||
using Iterator = BytesIterator;
|
||||
using Item = byte;
|
||||
|
||||
[[nodiscard]]
|
||||
auto begin() const noexcept -> BytesIterator {
|
||||
return {m_data, m_end_index, 0};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto end() const noexcept -> BytesIterator {
|
||||
return {m_data, m_end_index, m_end_index};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator*() const -> byte {
|
||||
return m_data[m_current_index];
|
||||
}
|
||||
|
||||
auto operator++() noexcept -> BytesIterator& {
|
||||
if (m_current_index < m_end_index) {
|
||||
++m_current_index;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator--() noexcept -> BytesIterator& {
|
||||
if (m_current_index > 0) {
|
||||
--m_current_index;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator==(const BytesIterator& b) noexcept -> bool {
|
||||
return m_data == b.m_data && m_end_index == b.m_end_index && m_current_index == b.m_current_index;
|
||||
}
|
||||
|
||||
auto operator!=(const BytesIterator& b) noexcept -> bool {
|
||||
return !(*this == b);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(Moveable<BytesIterator>);
|
||||
static_assert(!Copyable<BytesIterator>);
|
||||
static_assert(BidirectionalIterator<BytesIterator>);
|
||||
|
||||
} // namespace br
|
||||
20
src/foundation/iterators/adapter.cppm
Normal file
20
src/foundation/iterators/adapter.cppm
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
export module bedrock.foundation:iterators.adapter;
|
||||
|
||||
import bedrock.semantics;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename Adapter, typename It>
|
||||
concept IteratorAdapter = Defaultable<Adapter> && requires(Adapter adapter, It &&iter) { adapter(forward<It>(iter)); };
|
||||
|
||||
export template <Iterator It, IteratorAdapter<It> Adapter>
|
||||
auto operator|(It &&iter, Adapter &&adapter) {
|
||||
return adapter(forward<It>(iter));
|
||||
};
|
||||
|
||||
export template <Iterable I, IteratorAdapter<typename Unref<I>::Iterator> Adapter>
|
||||
auto operator|(I &iter, Adapter &&adapter) {
|
||||
return adapter(iter.begin());
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
105
src/foundation/iterators/enumerate.cppm
Normal file
105
src/foundation/iterators/enumerate.cppm
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
export module bedrock.foundation:iterators.enumerate;
|
||||
|
||||
import bedrock.numbers;
|
||||
import bedrock.semantics;
|
||||
import :iterators.adapter;
|
||||
import :tuple;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <ForwardIterator It>
|
||||
class EnumerateIterator final {
|
||||
private:
|
||||
It m_iter;
|
||||
usize m_current_index = 0;
|
||||
|
||||
public:
|
||||
EnumerateIterator(It&& i)
|
||||
: m_iter(move(i)) {}
|
||||
|
||||
// ====================================================================
|
||||
// Moveable
|
||||
// ====================================================================
|
||||
EnumerateIterator(EnumerateIterator&& other)
|
||||
requires(Moveable<It>)
|
||||
: m_iter(move(other.m_iter)) {}
|
||||
|
||||
auto operator=(EnumerateIterator&& other) noexcept -> EnumerateIterator&
|
||||
requires(Moveable<It>)
|
||||
{
|
||||
if (&other != this) {
|
||||
m_iter = move(other.m_iter);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~EnumerateIterator() = default;
|
||||
|
||||
// ====================================================================
|
||||
// Copyable
|
||||
// ====================================================================
|
||||
EnumerateIterator(const EnumerateIterator& other)
|
||||
requires(Copyable<It>)
|
||||
: m_iter(other.m_iter) {}
|
||||
|
||||
auto operator=(const EnumerateIterator& other) noexcept -> EnumerateIterator&
|
||||
requires(Copyable<It>)
|
||||
{
|
||||
if (&other != this) {
|
||||
m_iter = other.m_iter;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Iterator
|
||||
// ====================================================================
|
||||
using Item = Tuple<typename It::Item, usize>;
|
||||
|
||||
[[nodiscard]]
|
||||
auto begin() const noexcept -> EnumerateIterator {
|
||||
return EnumerateIterator(m_iter.begin());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto end() const noexcept -> EnumerateIterator {
|
||||
return EnumerateIterator(m_iter.end());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator*() const -> Item {
|
||||
return {*m_iter, m_current_index};
|
||||
}
|
||||
|
||||
auto operator++() noexcept -> EnumerateIterator& {
|
||||
++m_iter;
|
||||
++m_current_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator--() noexcept -> EnumerateIterator&
|
||||
requires BidirectionalIterator<It>
|
||||
{
|
||||
--m_iter;
|
||||
--m_current_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator==(const EnumerateIterator& other) noexcept -> bool {
|
||||
return m_iter == other.m_iter;
|
||||
}
|
||||
|
||||
auto operator!=(const EnumerateIterator& other) noexcept -> bool {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
export struct enumerate final {
|
||||
template <ForwardIterator It>
|
||||
auto operator()(It&& it) noexcept -> EnumerateIterator<It> {
|
||||
return EnumerateIterator<It>(move(it));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
101
src/foundation/iterators/reverse.cppm
Normal file
101
src/foundation/iterators/reverse.cppm
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
export module bedrock.foundation:iterators.reverse;
|
||||
|
||||
import bedrock.semantics;
|
||||
import :iterators.adapter;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <BidirectionalIterator It>
|
||||
class ReverseIterator {
|
||||
private:
|
||||
It m_iter;
|
||||
|
||||
public:
|
||||
ReverseIterator(It&& i)
|
||||
: m_iter(move(i)) {}
|
||||
|
||||
// ====================================================================
|
||||
// Moveable
|
||||
// ====================================================================
|
||||
ReverseIterator(ReverseIterator&& other)
|
||||
requires(Moveable<It>)
|
||||
: m_iter(move(other.m_iter)) {}
|
||||
|
||||
auto operator=(ReverseIterator&& other) noexcept -> ReverseIterator&
|
||||
requires(Moveable<It>)
|
||||
{
|
||||
if (&other != this) {
|
||||
m_iter = move(other.m_iter);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ReverseIterator() = default;
|
||||
|
||||
// ====================================================================
|
||||
// Copyable
|
||||
// ====================================================================
|
||||
ReverseIterator(const ReverseIterator& other)
|
||||
requires(Copyable<It>)
|
||||
: m_iter(other.m_iter) {}
|
||||
|
||||
auto operator=(const ReverseIterator& other) noexcept -> ReverseIterator&
|
||||
requires(Copyable<It>)
|
||||
{
|
||||
if (&other != this) {
|
||||
m_iter = other.m_iter;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Iterator
|
||||
// ====================================================================
|
||||
using Iterator = ReverseIterator<It>;
|
||||
|
||||
using Item = typename It::Item;
|
||||
|
||||
[[nodiscard]]
|
||||
auto begin() const noexcept -> ReverseIterator {
|
||||
return ReverseIterator(m_iter.end());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto end() const noexcept -> ReverseIterator {
|
||||
return ReverseIterator(m_iter.begin());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator*() const -> Item {
|
||||
return *m_iter;
|
||||
}
|
||||
|
||||
auto operator++() noexcept -> ReverseIterator& {
|
||||
--m_iter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator--() noexcept -> ReverseIterator& {
|
||||
++m_iter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator==(const ReverseIterator& other) noexcept -> bool {
|
||||
return m_iter == other.m_iter;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator!=(const ReverseIterator& other) noexcept -> bool {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
export struct reverse final {
|
||||
template <BidirectionalIterator It>
|
||||
auto operator()(It&& it) noexcept -> ReverseIterator<It> {
|
||||
return ReverseIterator<It>(move(it));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
178
src/foundation/optional.cppm
Normal file
178
src/foundation/optional.cppm
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
export module bedrock.foundation:optional;
|
||||
|
||||
import bedrock.numbers;
|
||||
import bedrock.semantics;
|
||||
|
||||
namespace br {
|
||||
|
||||
enum class OptionalVariant : uint8 {
|
||||
Absent = 0,
|
||||
Present = 1,
|
||||
};
|
||||
|
||||
export template <typename T>
|
||||
union Optional final {
|
||||
private:
|
||||
struct Absent {
|
||||
OptionalVariant tag;
|
||||
} m_absent;
|
||||
|
||||
struct Present {
|
||||
T value;
|
||||
OptionalVariant tag;
|
||||
} m_present;
|
||||
|
||||
public:
|
||||
Optional()
|
||||
: m_absent{.tag = OptionalVariant::Absent} {}
|
||||
|
||||
explicit Optional(const T& value)
|
||||
: m_present{.value = value, .tag = OptionalVariant::Present} {}
|
||||
|
||||
[[nodiscard]]
|
||||
static auto present(const T& value) noexcept -> Optional {
|
||||
return Optional(value);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static auto absent() noexcept -> Optional {
|
||||
return Optional();
|
||||
}
|
||||
|
||||
auto operator=(const T& value) -> Optional&
|
||||
requires Copyable<T>
|
||||
{
|
||||
this->m_present = {.value = value, .tag = OptionalVariant::Present};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U, Callable<U, T&&> M>
|
||||
auto map(M mapper) -> Optional<U> {
|
||||
if (isAbsent()) {
|
||||
return Optional<U>::absent();
|
||||
}
|
||||
return Optional<U>::present(mapper(move(m_present.value)));
|
||||
}
|
||||
|
||||
template <typename U, Callable<Optional<U>, T&&> M>
|
||||
auto map(M mapper) -> Optional<U> {
|
||||
if (isAbsent()) {
|
||||
return Optional<U>::absent();
|
||||
}
|
||||
return mapper(move(m_present.value));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto valueOr(T& default_value) noexcept -> T& {
|
||||
if (isAbsent()) {
|
||||
return default_value;
|
||||
}
|
||||
return m_present.value;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto isAbsent() const noexcept -> bool {
|
||||
return m_absent.tag == OptionalVariant::Absent;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto isPresent() const noexcept -> bool {
|
||||
return !isAbsent();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator==(const Optional<T>& rhs) const noexcept -> bool {
|
||||
if (isAbsent() && rhs.isAbsent()) {
|
||||
return true;
|
||||
}
|
||||
if (isPresent() && rhs.isPresent()) {
|
||||
return m_present.value == rhs.m_present.value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator!=(const Optional<T>& rhs) const noexcept -> bool {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator==(const T& rhs) const noexcept -> bool {
|
||||
if (isAbsent()) {
|
||||
return false;
|
||||
}
|
||||
return m_present.value == rhs;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator!=(const T& rhs) const noexcept -> bool {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
template <Callable<void, T> P, Callable<void> A>
|
||||
void match(P&& present, A&& absent) const {
|
||||
if (isPresent()) {
|
||||
present(m_present.value);
|
||||
} else {
|
||||
absent();
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Moveable
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
Optional(Optional&& other)
|
||||
requires Moveable<T>
|
||||
{
|
||||
if (other.isAbsent()) {
|
||||
m_absent = move(other.m_absent);
|
||||
} else {
|
||||
m_present = move(other.m_present);
|
||||
}
|
||||
}
|
||||
|
||||
auto operator=(Optional&& other) -> Optional&
|
||||
requires Moveable<T>
|
||||
{
|
||||
if (&other != this) {
|
||||
if (other.isAbsent()) {
|
||||
m_absent = move(other.m_absent);
|
||||
} else {
|
||||
m_present = move(other.m_present);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Optional() {}
|
||||
|
||||
// ====================================================================
|
||||
// Copyable
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
Optional(const Optional& other)
|
||||
requires Copyable<T>
|
||||
{
|
||||
if (other.isAbsent()) {
|
||||
m_absent = other.m_absent;
|
||||
} else {
|
||||
m_present = other.m_present;
|
||||
}
|
||||
}
|
||||
|
||||
auto operator=(const Optional& other) -> Optional&
|
||||
requires Copyable<T>
|
||||
{
|
||||
if (&other != this) {
|
||||
if (other.isAbsent()) {
|
||||
m_absent = other.m_absent;
|
||||
} else {
|
||||
m_present = other.m_present;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
205
src/foundation/result.cppm
Normal file
205
src/foundation/result.cppm
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
export module bedrock.foundation:result;
|
||||
|
||||
import bedrock.semantics;
|
||||
import :string_buffer;
|
||||
import :string;
|
||||
|
||||
namespace br {
|
||||
|
||||
export class Error final {
|
||||
private:
|
||||
StringBuffer m_message;
|
||||
|
||||
public:
|
||||
explicit Error(const String& message) noexcept
|
||||
: m_message(StringBuffer::copyFrom(message)) {}
|
||||
|
||||
[[nodiscard]]
|
||||
auto copy() const noexcept -> Error {
|
||||
return Error(m_message);
|
||||
}
|
||||
|
||||
explicit operator String() const noexcept {
|
||||
return m_message;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// !Moveable
|
||||
// ====================================================================
|
||||
Error(Error&&) = default;
|
||||
|
||||
auto operator=(Error&&) -> Error& = default;
|
||||
|
||||
~Error() = default;
|
||||
|
||||
// ====================================================================
|
||||
// !Copyable
|
||||
// ====================================================================
|
||||
Error(const Error&) = delete;
|
||||
|
||||
auto operator=(const Error&) -> Error& = delete;
|
||||
|
||||
// ====================================================================
|
||||
// Equivalence
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto operator==(const Error& other) const noexcept -> bool {
|
||||
return m_message == other.m_message;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator!=(const Error& other) const noexcept -> bool {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
export template <>
|
||||
struct EquivalenceType<Error> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
static_assert(Moveable<Error>);
|
||||
static_assert(!Copyable<Error>);
|
||||
static_assert(Equivalence<Error>);
|
||||
|
||||
enum class ResultVariant : uint8 {
|
||||
Success = 0,
|
||||
Failure = 1,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Success {
|
||||
T value;
|
||||
ResultVariant tag;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Success<void> {
|
||||
ResultVariant tag;
|
||||
};
|
||||
|
||||
struct Failure {
|
||||
Error error;
|
||||
ResultVariant tag;
|
||||
};
|
||||
|
||||
static_assert(Moveable<Failure>);
|
||||
|
||||
export template <typename T>
|
||||
union [[nodiscard]]
|
||||
Result final {
|
||||
private:
|
||||
Success<T> m_success;
|
||||
Failure m_failure;
|
||||
|
||||
template <typename U = T>
|
||||
requires(!SameAs<U, void>)
|
||||
explicit Result(const U& value)
|
||||
: m_success{.value = value, .tag = ResultVariant::Success} {}
|
||||
|
||||
template <typename U = T>
|
||||
requires(SameAs<U, void>)
|
||||
explicit Result()
|
||||
: m_success{.tag = ResultVariant::Success} {}
|
||||
|
||||
explicit Result(Error&& error)
|
||||
: m_failure{.error = move(error), .tag = ResultVariant::Failure} {}
|
||||
|
||||
public:
|
||||
// ====================================================================
|
||||
// Static Constructors
|
||||
// ====================================================================
|
||||
template <typename U = T>
|
||||
requires(!SameAs<U, void>)
|
||||
[[nodiscard]]
|
||||
static auto success(const U& value) noexcept -> Result {
|
||||
return Result(value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
requires(SameAs<U, void>)
|
||||
[[nodiscard]]
|
||||
static auto success() noexcept -> Result {
|
||||
return Result();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static auto failure(const String& message) noexcept -> Result {
|
||||
return Result(Error(message));
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Introspection Methods
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto isSuccess() const noexcept -> bool {
|
||||
return m_success.tag == ResultVariant::Success;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto isFailure() const noexcept -> bool {
|
||||
return !isSuccess();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Moveable
|
||||
// ====================================================================
|
||||
Result(Result&& other) noexcept
|
||||
requires Moveable<T>
|
||||
{
|
||||
if (&other != this) {
|
||||
if (other.isFailure()) {
|
||||
m_failure = move(other.m_failure);
|
||||
} else {
|
||||
m_success = move(other.m_success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto operator=(Result&& other) noexcept -> Result&
|
||||
requires Moveable<T>
|
||||
{
|
||||
if (&other != this) {
|
||||
if (other.isFailure()) {
|
||||
m_failure = move(other.m_failure);
|
||||
} else {
|
||||
m_success = move(other.m_success);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Result() {}
|
||||
|
||||
// ====================================================================
|
||||
// Copyable
|
||||
// ====================================================================
|
||||
Result(const Result&) = delete;
|
||||
|
||||
auto operator=(const Result& other) -> Result& = delete;
|
||||
|
||||
// ====================================================================
|
||||
// Equivalence
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto operator==(const Result& rhs) const noexcept -> bool
|
||||
requires(Equivalence<T>)
|
||||
{
|
||||
if (isFailure() && rhs.isFailure()) {
|
||||
return m_failure.error == rhs.m_failure.error;
|
||||
}
|
||||
if (isSuccess() && rhs.isSuccess()) {
|
||||
return m_success.value == rhs.m_success.value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator!=(const Result& rhs) const noexcept -> bool
|
||||
requires(Equivalence<T>)
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
130
src/foundation/string.cppm
Normal file
130
src/foundation/string.cppm
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
export module bedrock.foundation:string;
|
||||
|
||||
import bedrock.numbers;
|
||||
import bedrock.semantics;
|
||||
import :byte;
|
||||
import :buffer;
|
||||
import :bytes_iterator;
|
||||
import :iterators.enumerate;
|
||||
import :iterators.reverse;
|
||||
|
||||
namespace br {
|
||||
|
||||
export class String final {
|
||||
private:
|
||||
BufferView m_data;
|
||||
|
||||
explicit String(BufferView&& view)
|
||||
: m_data(view) {}
|
||||
|
||||
public:
|
||||
friend class StringBuffer;
|
||||
|
||||
template <usize Size>
|
||||
constexpr String(const char (&str)[Size])
|
||||
: m_data(reinterpret_cast<const byte*>(&str), Size - 1) {}
|
||||
|
||||
[[nodiscard]]
|
||||
auto byteSize() const noexcept -> usize {
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto characterLength() const noexcept -> usize {
|
||||
usize nCharacters = 0;
|
||||
for (auto byte : m_data) {
|
||||
if ((byte & 0xc0_b) != 0x80_b) {
|
||||
++nCharacters;
|
||||
}
|
||||
}
|
||||
return nCharacters;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto isEmpty() const noexcept -> bool {
|
||||
return *this == "";
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto startsWith(const String& str) noexcept -> bool {
|
||||
for (auto [byte, i] : str.bytes() | enumerate()) {
|
||||
if (m_data[i] != byte) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto endsWith(const String& str) noexcept -> bool {
|
||||
for (auto [byte, i] : str.bytes() | reverse() | enumerate()) {
|
||||
if (m_data[i] != byte) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Implicit conversions
|
||||
// ====================================================================
|
||||
[[nodiscard]] operator BufferView() const noexcept {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto bytes() const noexcept -> BytesIterator {
|
||||
return m_data.bytes();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Moveable
|
||||
// ====================================================================
|
||||
String(String&& other) noexcept
|
||||
: m_data(move(other.m_data)) {}
|
||||
|
||||
auto operator=(String&& other) noexcept -> String& {
|
||||
if (this == &other) {
|
||||
m_data = move(other.m_data);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~String() = default;
|
||||
|
||||
// ====================================================================
|
||||
// Copyable
|
||||
// ====================================================================
|
||||
String(const String& other) = default;
|
||||
|
||||
auto operator=(const String& other) noexcept -> String& {
|
||||
if (&other != this) {
|
||||
m_data = other.m_data;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Equivalence
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto operator==(const String& other) const noexcept -> bool {
|
||||
return m_data == other.m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator!=(const String& other) const noexcept -> bool {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct EquivalenceType<String> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
static_assert(Moveable<String>);
|
||||
static_assert(Copyable<String>);
|
||||
static_assert(Equivalence<String>);
|
||||
|
||||
} // namespace br
|
||||
79
src/foundation/string_buffer.cppm
Normal file
79
src/foundation/string_buffer.cppm
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
export module bedrock.foundation:string_buffer;
|
||||
|
||||
import bedrock.numbers;
|
||||
import bedrock.semantics;
|
||||
import :string;
|
||||
import :buffer;
|
||||
|
||||
namespace br {
|
||||
|
||||
export class StringBuffer final {
|
||||
private:
|
||||
Buffer m_data;
|
||||
|
||||
explicit StringBuffer(Buffer&& buffer)
|
||||
: m_data(move(buffer)) {}
|
||||
|
||||
explicit StringBuffer(const String& str)
|
||||
: StringBuffer(Buffer::copyFrom(str)) {}
|
||||
|
||||
public:
|
||||
[[nodiscard]]
|
||||
static auto withCapacity(usize capacity) noexcept -> StringBuffer {
|
||||
return StringBuffer(Buffer::withCapacity(capacity));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static auto copyFrom(const String& str) noexcept -> StringBuffer {
|
||||
return StringBuffer(str);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto capacityBytes() const noexcept -> usize {
|
||||
return m_data.capacity();
|
||||
}
|
||||
|
||||
operator String() const noexcept {
|
||||
return String(m_data);
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Moveable
|
||||
// ====================================================================
|
||||
StringBuffer(StringBuffer&&) = default;
|
||||
|
||||
auto operator=(StringBuffer&&) -> StringBuffer& = default;
|
||||
|
||||
~StringBuffer() = default;
|
||||
|
||||
// ====================================================================
|
||||
// Copyable
|
||||
// ====================================================================
|
||||
StringBuffer(const StringBuffer& other) = delete;
|
||||
|
||||
auto operator=(const StringBuffer&) -> StringBuffer& = delete;
|
||||
|
||||
// ====================================================================
|
||||
// Equivalence
|
||||
// ====================================================================
|
||||
[[nodiscard]]
|
||||
auto operator==(const StringBuffer& other) const noexcept -> bool {
|
||||
return m_data == other.m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator!=(const StringBuffer& other) const noexcept -> bool {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
export template <>
|
||||
struct EquivalenceType<StringBuffer> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
static_assert(Moveable<StringBuffer>);
|
||||
static_assert(!Copyable<StringBuffer>);
|
||||
static_assert(Equivalence<StringBuffer>);
|
||||
|
||||
} // namespace br
|
||||
33
src/foundation/tuple.cppm
Normal file
33
src/foundation/tuple.cppm
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
export module bedrock.foundation:tuple;
|
||||
|
||||
import bedrock.numbers;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename... Types>
|
||||
class Tuple;
|
||||
|
||||
export template <typename T1, typename T2>
|
||||
class Tuple<T1, T2> {
|
||||
public:
|
||||
Tuple(T1 t1, T2 t2)
|
||||
: m_t1(t1), m_t2(t2) {}
|
||||
|
||||
template <usize I>
|
||||
auto get() const -> auto {
|
||||
if constexpr (I == 0) {
|
||||
return m_t1;
|
||||
}
|
||||
if constexpr (I == 1) {
|
||||
return m_t2;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wpadded"
|
||||
const T1 m_t1;
|
||||
const T2 m_t2;
|
||||
#pragma clang diagnostic pop
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
19
src/foundation/utility.cppm
Normal file
19
src/foundation/utility.cppm
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
export module bedrock.foundation:utility;
|
||||
|
||||
import bedrock.semantics;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <Integral T>
|
||||
[[nodiscard]]
|
||||
constexpr auto nearestPowerOf2(T i) noexcept -> T {
|
||||
--i;
|
||||
i |= i >> 1;
|
||||
i |= i >> 2;
|
||||
i |= i >> 4;
|
||||
i |= i >> 8;
|
||||
i |= i >> 16;
|
||||
return ++i;
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
3
src/io.cppm
Normal file
3
src/io.cppm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export module bedrock.io;
|
||||
|
||||
export import :channels;
|
||||
42
src/io/channels.cppm
Normal file
42
src/io/channels.cppm
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
module;
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
export module bedrock.io:channels;
|
||||
|
||||
import bedrock.foundation;
|
||||
|
||||
namespace br::io {
|
||||
|
||||
class CommunicationChannel final {
|
||||
private:
|
||||
FILE* m_channel;
|
||||
|
||||
public:
|
||||
explicit constexpr CommunicationChannel(FILE* channel)
|
||||
: m_channel(channel) {}
|
||||
|
||||
auto write(const String& message) -> Result<void> {
|
||||
if (fflush(m_channel) == EOF) {
|
||||
return Result<void>::failure("failed to flush channel");
|
||||
}
|
||||
auto bytes_written =
|
||||
fwrite(static_cast<BufferView>(message).ptr(), sizeof(byte), message.byteSize(), m_channel);
|
||||
if (bytes_written < message.byteSize()) {
|
||||
return Result<void>::failure("failed to write to channel");
|
||||
}
|
||||
return Result<void>::success();
|
||||
}
|
||||
};
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
|
||||
#pragma clang diagnostic ignored "-Wuninitialized"
|
||||
export CommunicationChannel out(stdout);
|
||||
|
||||
export CommunicationChannel stderr(stderr);
|
||||
|
||||
export CommunicationChannel stdin(stdin);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace br::io
|
||||
31
src/numbers.cppm
Normal file
31
src/numbers.cppm
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
export module bedrock.numbers;
|
||||
|
||||
namespace br {
|
||||
|
||||
export using usize = decltype(sizeof 0);
|
||||
export using isize = decltype(static_cast<usize*>(nullptr) - static_cast<usize*>(nullptr));
|
||||
|
||||
export using uint8 = unsigned char;
|
||||
static_assert(sizeof(uint8) == 1);
|
||||
export using uint16 = unsigned short;
|
||||
static_assert(sizeof(uint16) == 2);
|
||||
export using uint32 = unsigned int;
|
||||
static_assert(sizeof(uint32) == 4);
|
||||
export using uint64 = unsigned long;
|
||||
static_assert(sizeof(uint64) == 8);
|
||||
|
||||
export using int8 = signed char;
|
||||
static_assert(sizeof(int8) == 1);
|
||||
export using int16 = signed short;
|
||||
static_assert(sizeof(int16) == 2);
|
||||
export using int32 = signed int;
|
||||
static_assert(sizeof(int32) == 4);
|
||||
export using int64 = signed long;
|
||||
static_assert(sizeof(int64) == 8);
|
||||
|
||||
export using float32 = float;
|
||||
static_assert(sizeof(float32) == 4);
|
||||
export using float64 = double;
|
||||
static_assert(sizeof(float64) == 8);
|
||||
|
||||
} // namespace br
|
||||
13
src/semantics.cppm
Normal file
13
src/semantics.cppm
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export module bedrock.semantics;
|
||||
|
||||
export import :callable;
|
||||
export import :copyable;
|
||||
export import :defaultable;
|
||||
export import :equivalence;
|
||||
export import :floating_point;
|
||||
export import :integral;
|
||||
export import :iterable;
|
||||
export import :moveable;
|
||||
export import :ordered;
|
||||
export import :referenceable;
|
||||
export import :utility;
|
||||
13
src/semantics/callable.cppm
Normal file
13
src/semantics/callable.cppm
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export module bedrock.semantics:callable;
|
||||
|
||||
import :moveable;
|
||||
import :utility;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename T, typename R = void, typename... Args>
|
||||
concept Callable = requires(T t, Args... args) {
|
||||
{ t(args...) } -> SameAs<R>;
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
13
src/semantics/copyable.cppm
Normal file
13
src/semantics/copyable.cppm
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export module bedrock.semantics:copyable;
|
||||
|
||||
import :utility;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename T>
|
||||
concept Copyable = requires(T u, const T v) {
|
||||
T(v);
|
||||
{ u = v } -> SameAs<T &>;
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
12
src/semantics/defaultable.cppm
Normal file
12
src/semantics/defaultable.cppm
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export module bedrock.semantics:defaultable;
|
||||
|
||||
import :utility;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename T>
|
||||
concept Defaultable = requires() {
|
||||
{ T() } -> SameAs<T>;
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
25
src/semantics/equivalence.cppm
Normal file
25
src/semantics/equivalence.cppm
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
export module bedrock.semantics:equivalence;
|
||||
|
||||
import :utility;
|
||||
import :referenceable;
|
||||
import :integral;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename T, typename U = T>
|
||||
concept PartialEquivalence = requires(Unref<T> a, Unref<U> b) {
|
||||
{ a == b } -> SameAs<bool>;
|
||||
{ a != b } -> SameAs<bool>;
|
||||
{ b == a } -> SameAs<bool>;
|
||||
{ b != a } -> SameAs<bool>;
|
||||
};
|
||||
|
||||
export template <PartialEquivalence T>
|
||||
struct EquivalenceType {
|
||||
static constexpr bool is_valid = false;
|
||||
};
|
||||
|
||||
export template <typename T>
|
||||
concept Equivalence = PartialEquivalence<T> && (EquivalenceType<T>::is_valid || Integral<T>);
|
||||
|
||||
} // namespace br
|
||||
28
src/semantics/floating_point.cppm
Normal file
28
src/semantics/floating_point.cppm
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
export module bedrock.semantics:floating_point;
|
||||
|
||||
namespace br {
|
||||
|
||||
template <typename T>
|
||||
struct FloatingPointTraits {
|
||||
static constexpr bool is_valid = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct FloatingPointTraits<float> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct FloatingPointTraits<double> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct FloatingPointTraits<long double> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
export template <typename T>
|
||||
concept FloatingPoint = FloatingPointTraits<T>::is_valid;
|
||||
|
||||
} // namespace br
|
||||
58
src/semantics/integral.cppm
Normal file
58
src/semantics/integral.cppm
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
export module bedrock.semantics:integral;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename T>
|
||||
struct SignedIntegralTraits {
|
||||
static constexpr bool is_valid = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SignedIntegralTraits<signed char> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SignedIntegralTraits<signed short> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SignedIntegralTraits<signed int> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SignedIntegralTraits<signed long> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct UnsignedIntegralTraits {
|
||||
static constexpr bool is_valid = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct UnsignedIntegralTraits<unsigned char> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct UnsignedIntegralTraits<unsigned short> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct UnsignedIntegralTraits<unsigned int> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct UnsignedIntegralTraits<unsigned long> {
|
||||
static constexpr bool is_valid = true;
|
||||
};
|
||||
|
||||
export template <typename T>
|
||||
concept Integral = SignedIntegralTraits<T>::is_valid || UnsignedIntegralTraits<T>::is_valid;
|
||||
|
||||
} // namespace br
|
||||
44
src/semantics/iterable.cppm
Normal file
44
src/semantics/iterable.cppm
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
export module bedrock.semantics:iterable;
|
||||
|
||||
import bedrock.numbers;
|
||||
import :ordered;
|
||||
import :equivalence;
|
||||
import :referenceable;
|
||||
import :utility;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename It>
|
||||
concept Iterator = PartialEquivalence<It> && requires(It r) {
|
||||
typename UnreferencedType<It>::Type::Item;
|
||||
{ ++r } -> SameAs<It &>;
|
||||
{ *r } -> SameAs<typename UnreferencedType<It>::Type::Item>;
|
||||
};
|
||||
|
||||
export template <typename I>
|
||||
concept Iterable = requires(I &r) {
|
||||
requires(Iterator<typename Unref<I>::Iterator>);
|
||||
{ r.begin() } -> SameAs<typename Unref<I>::Iterator>;
|
||||
{ r.end() } -> SameAs<typename Unref<I>::Iterator>;
|
||||
};
|
||||
|
||||
export template <typename It>
|
||||
concept ForwardIterator = Iterable<It> && Iterator<It>;
|
||||
|
||||
export template <typename It>
|
||||
concept BidirectionalIterator = ForwardIterator<It> && requires(It r) {
|
||||
{ --r } -> SameAs<It &>;
|
||||
};
|
||||
|
||||
export template <typename It>
|
||||
concept RandomAccessIterator =
|
||||
BidirectionalIterator<It> && TotallyOrdered<It> && requires(It i, const It j, const isize n) {
|
||||
{ i += n } -> SameAs<It &>;
|
||||
{ j + n } -> SameAs<It>;
|
||||
{ n + j } -> SameAs<It>;
|
||||
{ i -= n } -> SameAs<It &>;
|
||||
{ j - n } -> SameAs<It>;
|
||||
{ j[n] } -> SameAs<isize>;
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
25
src/semantics/moveable.cppm
Normal file
25
src/semantics/moveable.cppm
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
export module bedrock.semantics:moveable;
|
||||
|
||||
import :referenceable;
|
||||
import :utility;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename T>
|
||||
concept Destructible = requires(T u) {
|
||||
{ u.~T() } noexcept;
|
||||
};
|
||||
|
||||
export template <typename T>
|
||||
concept Moveable = Destructible<T> && requires(Unref<T> u, Unref<T> rv) {
|
||||
T(static_cast<T &&>(rv));
|
||||
{ u = static_cast<T &&>(rv) } -> SameAs<T&>;
|
||||
};
|
||||
|
||||
export template <Moveable T>
|
||||
[[nodiscard]]
|
||||
auto move(T& value) noexcept -> Unref<T>&& {
|
||||
return static_cast<Unref<T>&&>(value);
|
||||
}
|
||||
|
||||
} // namespace br
|
||||
47
src/semantics/ordered.cppm
Normal file
47
src/semantics/ordered.cppm
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
export module bedrock.semantics:ordered;
|
||||
|
||||
import bedrock.numbers;
|
||||
import :utility;
|
||||
import :equivalence;
|
||||
import :referenceable;
|
||||
|
||||
namespace br {
|
||||
|
||||
export enum class Ordering : int8 {
|
||||
LessThan = -1,
|
||||
EqualTo = 0,
|
||||
GreaterThan = 1,
|
||||
};
|
||||
|
||||
export template <typename T, typename U = T>
|
||||
concept PartiallyOrdered = requires(const Unref<T>& a, const Unref<U>& b) {
|
||||
{ a < b } -> SameAs<bool>;
|
||||
{ a > b } -> SameAs<bool>;
|
||||
{ a <= b } -> SameAs<bool>;
|
||||
{ a >= b } -> SameAs<bool>;
|
||||
{ b < a } -> SameAs<bool>;
|
||||
{ b > a } -> SameAs<bool>;
|
||||
{ b <= a } -> SameAs<bool>;
|
||||
{ b >= a } -> SameAs<bool>;
|
||||
};
|
||||
|
||||
export template <typename T>
|
||||
concept TotallyOrdered = Equivalence<T> && PartiallyOrdered<T>;
|
||||
|
||||
export template <typename T>
|
||||
[[nodiscard]]
|
||||
auto max(const T& a, const T& b) -> bool
|
||||
requires(TotallyOrdered<T>)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
};
|
||||
|
||||
export template <typename T>
|
||||
[[nodiscard]]
|
||||
auto min(const T& a, const T& b) -> bool
|
||||
requires(TotallyOrdered<T>)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
51
src/semantics/referenceable.cppm
Normal file
51
src/semantics/referenceable.cppm
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
export module bedrock.semantics:referenceable;
|
||||
|
||||
namespace br {
|
||||
|
||||
export template <typename T>
|
||||
concept Referenceable = requires(T& u) { u; };
|
||||
|
||||
template <Referenceable T>
|
||||
struct UnreferencedType {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <Referenceable T>
|
||||
struct UnreferencedType<T&> {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <Referenceable T>
|
||||
struct UnreferencedType<const T&> {
|
||||
using Type = const T;
|
||||
};
|
||||
|
||||
template <Referenceable T>
|
||||
struct UnreferencedType<T&&> {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <Referenceable T>
|
||||
struct UnreferencedType<const T&&> {
|
||||
using Type = const T;
|
||||
};
|
||||
|
||||
export template <Referenceable T>
|
||||
using Unref = typename UnreferencedType<T>::Type;
|
||||
|
||||
export template <Referenceable T>
|
||||
constexpr auto forward(Unref<T>& t) noexcept -> Unref<T>&& {
|
||||
return static_cast<T&&>(t);
|
||||
};
|
||||
|
||||
export template <Referenceable T>
|
||||
constexpr auto forward(Unref<T>&& t) noexcept -> Unref<T>&& {
|
||||
return static_cast<T&&>(t);
|
||||
};
|
||||
|
||||
export template <Referenceable T>
|
||||
auto declval() noexcept -> Unref<T>& {
|
||||
static_assert(false, "declval not allowed in an evaluated context");
|
||||
};
|
||||
|
||||
} // namespace br
|
||||
18
src/semantics/utility.cppm
Normal file
18
src/semantics/utility.cppm
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
export module bedrock.semantics:utility;
|
||||
|
||||
namespace br {
|
||||
|
||||
template <typename T, typename U>
|
||||
struct areSameTypes {
|
||||
static const bool same = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct areSameTypes<T, T> {
|
||||
static const bool same = true;
|
||||
};
|
||||
|
||||
export template <typename T, typename U>
|
||||
concept SameAs = areSameTypes<T, U>::same;
|
||||
|
||||
} // namespace br
|
||||
3
src/test.cppm
Normal file
3
src/test.cppm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export module bedrock.test;
|
||||
|
||||
export import :api;
|
||||
67
src/test/api.cppm
Normal file
67
src/test/api.cppm
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
export module bedrock.test:api;
|
||||
|
||||
import bedrock.semantics;
|
||||
import bedrock.foundation;
|
||||
import bedrock.collections;
|
||||
#include <utility>
|
||||
|
||||
namespace br {
|
||||
struct Precondition {
|
||||
String summary;
|
||||
void (*definition)();
|
||||
};
|
||||
|
||||
struct Expectation {
|
||||
String summary;
|
||||
void (*definition)();
|
||||
};
|
||||
|
||||
struct Scenario {
|
||||
String summary;
|
||||
Sequence<Precondition> preconditions{};
|
||||
Sequence<Expectation> expectations{};
|
||||
|
||||
explicit Scenario(String summary) noexcept;
|
||||
};
|
||||
|
||||
static auto SCENARIOS = Sequence<Scenario>::withCapacity(10000);
|
||||
|
||||
export static auto allScenarios() -> const Sequence<Scenario*>& {
|
||||
return SCENARIOS;
|
||||
}
|
||||
|
||||
static Optional<Scenario*> CURRENT_SCENARIO;
|
||||
|
||||
export struct Feature final {
|
||||
template <typename... Scenarios>
|
||||
Feature(Scenarios... scenarios) noexcept {}
|
||||
};
|
||||
|
||||
template <Callable Def>
|
||||
constexpr auto scenario(String summary, Def&& definition) noexcept -> void {
|
||||
Scenario new_scenario(summary);
|
||||
CURRENT_SCENARIO = &new_scenario;
|
||||
|
||||
definition();
|
||||
|
||||
SCENARIOS.push(move(new_scenario));
|
||||
CURRENT_SCENARIO = Optional<Scenario*>::absent();
|
||||
}
|
||||
|
||||
template <Callable Def>
|
||||
constexpr auto given(String summary, Def&& definition = []() -> void {}) noexcept -> void {
|
||||
CURRENT_SCENARIO.match(
|
||||
[&](Scenario& scn) -> void { scn.preconditions.push({.summary = summary, .definition = definition}); },
|
||||
[]() -> void {
|
||||
// TODO: halt the program or something with an error message.
|
||||
});
|
||||
}
|
||||
|
||||
export template <Callable Def>
|
||||
constexpr auto then(String summary, Def&& definition) -> void {
|
||||
CURRENT_SCENARIO.match(
|
||||
[&](Scenario& scn) -> void { scn.expectations.push({.summary = summary, .definition = definition}); },
|
||||
[]() -> void {});
|
||||
}
|
||||
|
||||
} // namespace br
|
||||
17
src/test/runner.cppm
Normal file
17
src/test/runner.cppm
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
export module bedrock.test:runner;
|
||||
|
||||
import :api;
|
||||
|
||||
using namespace br;
|
||||
|
||||
int main() {
|
||||
for (Scenario& scn : allScenarios()) {
|
||||
for (Precondition& pre : scn.preconditions) {
|
||||
pre.definition();
|
||||
}
|
||||
|
||||
for (Expectation& exp : scn.expectations) {
|
||||
exp.definition();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue