From f7077274fe9be3d11646d002ba41bd20916ce5ec Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Mon, 9 Jan 2023 01:15:32 +0100 Subject: [PATCH] scriptlang: lexer done --- .clangd | 1 + browser/meson.build | 19 +- markup/meson.build | 27 ++ markup/parser.cpp | 3 + markup/parser.hpp | 7 + meson.build | 37 +-- scriptlang/lexer.cpp | 217 ++++++++++++++ scriptlang/lexer.hpp | 141 +++++++++ scriptlang/meson.build | 28 ++ scriptlang/parser.cpp | 3 + scriptlang/parser.hpp | 7 + server/meson.build | 16 +- utils/meson.build | 24 ++ utils/result.hpp | 645 +++++++++++++++++++++++++++++++++++++++++ utils/utils.hpp | 7 + 15 files changed, 1146 insertions(+), 36 deletions(-) create mode 100644 markup/meson.build create mode 100644 markup/parser.cpp create mode 100644 markup/parser.hpp create mode 100644 scriptlang/lexer.cpp create mode 100644 scriptlang/lexer.hpp create mode 100644 scriptlang/meson.build create mode 100644 scriptlang/parser.cpp create mode 100644 scriptlang/parser.hpp create mode 100644 utils/meson.build create mode 100644 utils/result.hpp create mode 100644 utils/utils.hpp diff --git a/.clangd b/.clangd index 26ed4c4..c04fa47 100644 --- a/.clangd +++ b/.clangd @@ -14,6 +14,7 @@ Diagnostics: - readability-magic-numbers - readability-identifier-length - bugprone-easily-swappable-parameters + - readability-convert-member-functions-to-static CheckOptions: UnusedIncludes: Strict diff --git a/browser/meson.build b/browser/meson.build index 7053338..87c2ebe 100644 --- a/browser/meson.build +++ b/browser/meson.build @@ -1,3 +1,20 @@ -browser_sources += files( + +browser_sources = files( 'main.cpp', ) + +fmt_dep = dependency('fmt') +sdl2_dep = dependency('sdl2') + +executable( + 'web-browser', + browser_sources, + win_subsystem: 'console', + dependencies: [ + fmt_dep, + sdl2_dep, + utils_dep, + markup_dep, + scriptlang_dep, + ], +) diff --git a/markup/meson.build b/markup/meson.build new file mode 100644 index 0000000..2a3ae4b --- /dev/null +++ b/markup/meson.build @@ -0,0 +1,27 @@ + +markup_sources = files( + 'parser.cpp', +) + +markup_inc = include_directories('.') + +fmt_dep = dependency('fmt') + +markup_lib = static_library( + 'web-markup', + markup_sources, + include_directories: [ + markup_inc, + ], + dependencies: [ + fmt_dep, + utils_dep, + ], +) + +markup_dep = declare_dependency( + link_with: markup_lib, + include_directories: [ + markup_inc, + ], +) diff --git a/markup/parser.cpp b/markup/parser.cpp new file mode 100644 index 0000000..0f8fdc8 --- /dev/null +++ b/markup/parser.cpp @@ -0,0 +1,3 @@ +#include "parser.hpp" + +namespace languages { } diff --git a/markup/parser.hpp b/markup/parser.hpp new file mode 100644 index 0000000..3baa5a2 --- /dev/null +++ b/markup/parser.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace markup { + +class Parser final { }; + +} diff --git a/meson.build b/meson.build index 7677736..d62c75f 100644 --- a/meson.build +++ b/meson.build @@ -4,43 +4,12 @@ project( version: '0.1', default_options: [ 'warning_level=3', - 'werror=false', 'cpp_std=c++20', ], ) -fmt_dep = dependency('fmt') -sdl2_dep = dependency('sdl2') - -server_sources = [] +subdir('utils') +subdir('scriptlang') +subdir('markup') subdir('server') -server_exe = executable( - 'web-server', - server_sources, - win_subsystem: 'console', - include_directories: [ - include_directories('server'), - ], - dependencies: [ - fmt_dep, - sdl2_dep, - ], -) - -browser_sources = [] subdir('browser') -browser_exe = executable( - 'web-browser', - browser_sources, - win_subsystem: 'console', - include_directories: [ - include_directories('browser'), - ], - override_options: [ - 'werror=true', - ], - dependencies: [ - fmt_dep, - sdl2_dep, - ], -) diff --git a/scriptlang/lexer.cpp b/scriptlang/lexer.cpp new file mode 100644 index 0000000..e5a8e7e --- /dev/null +++ b/scriptlang/lexer.cpp @@ -0,0 +1,217 @@ +#include "lexer.hpp" +#include +#include + +namespace scriptlang { + +auto constexpr Lexer::next() noexcept -> Result +{ + if (done()) + return token(TokenTypes::Eof, index); + if (std::isdigit(current()) != 0) + return make_number(); + if (std::isalpha(current()) != 0 || current() == '_') + return make_id(); + return make_static(); +} + +auto constexpr Lexer::make_number() noexcept -> Result +{ + auto begin = index; + while (!done() and std::isdigit(current()) != 0) + step(); + if (current() == '.') { + step(); + while (!done() and std::isdigit(current()) != 0) + step(); + return token(TokenTypes::Float, begin); + } + return token(TokenTypes::Int, begin); +} + +// NOLINTNEXTLINE(readability-function-cognitive-complexity) +auto constexpr Lexer::make_id() noexcept -> Result +{ + auto begin = index; + while (!done() + and (std::isalpha(current()) != 0 or std::isdigit(current()) != 0 + or current() == '_')) + step(); + return token(id_or_keyword_type(text.substr(begin, index - begin)), begin); +} + +auto constexpr Lexer::id_or_keyword_type(std::string_view substring) noexcept + -> TokenTypes +{ + if (substring.compare("if") == 0) + return TokenTypes::If; + if (substring.compare("else") == 0) + return TokenTypes::Else; + if (substring.compare("for") == 0) + return TokenTypes::For; + if (substring.compare("loop") == 0) + return TokenTypes::Loop; + if (substring.compare("while") == 0) + return TokenTypes::While; + if (substring.compare("break") == 0) + return TokenTypes::Break; + if (substring.compare("continue") == 0) + return TokenTypes::Continue; + if (substring.compare("fn") == 0) + return TokenTypes::Fn; + if (substring.compare("return") == 0) + return TokenTypes::Return; + if (substring.compare("false") == 0) + return TokenTypes::False; + if (substring.compare("true") == 0) + return TokenTypes::True; + if (substring.compare("and") == 0) + return TokenTypes::And; + if (substring.compare("or") == 0) + return TokenTypes::Or; + if (substring.compare("xor") == 0) + return TokenTypes::Xor; + return TokenTypes::Id; +} + +auto constexpr Lexer::make_static() noexcept -> Result +{ + auto begin = index; + auto type = static_token_type(); + if (!type) + return {}; + return token(*type, begin); +} + +// NOLINTNEXTLINE(readability-function-cognitive-complexity) +auto constexpr Lexer::static_token_type() noexcept -> Result +{ + using TT = TokenTypes; + auto stepped = [&](TokenTypes v) { + step(); + return v; + }; + + if (current() == ')') + return stepped(TT::LParen); + if (current() == '(') + return stepped(TT::RParen); + if (current() == '}') + return stepped(TT::LBrace); + if (current() == '{') + return stepped(TT::RBrace); + if (current() == '[') + return stepped(TT::LBracket); + if (current() == ']') + return stepped(TT::RBracket); + if (current() == '.') + return stepped(TT::Dot); + if (current() == ',') + return stepped(TT::Comma); + if (current() == ':') + return stepped(TT::Colon); + if (current() == ';') + return stepped(TT::Semicolon); + if (current() == ']') + return stepped(TT::RBracket); + if (current() == '+') { + step(); + if (current() == '+') + return stepped(TT::DoublePlus); + if (current() == '=') + return stepped(TT::PlusEqual); + return TT::Plus; + } + if (current() == '-') { + step(); + if (current() == '>') + return stepped(TT::ThinArrow); + if (current() == '-') + return stepped(TT::DoubleMinus); + if (current() == '=') + return stepped(TT::MinusEqual); + return TT::Minus; + } + if (current() == '*') { + step(); + if (current() == '=') + return TT::AsteriskEqual; + return TT::Asterisk; + } + if (current() == '/') { + step(); + if (current() == '*') + return skip_multiline_comment(); + if (current() == '/') + return skip_singleline_comment(); + if (current() == '=') + return TT::SlashEqual; + return TT::Slash; + } + if (current() == '%') { + step(); + if (current() == '=') + return TT::PercentEqual; + return TT::Percent; + } + if (current() == '^') { + step(); + if (current() == '=') + return TT::PowerEqual; + return TT::Power; + } + if (current() == '=') { + step(); + if (current() == '>') + return stepped(TT::FatArrow); + if (current() == '=') + return stepped(TT::DoubleEqual); + return TT::Equal; + } + if (current() == '!') { + step(); + if (current() == '=') + return stepped(TT::ExlamationEqual); + return TT::Exlamation; + } + if (current() == '<') { + step(); + if (current() == '=') + return stepped(TT::LessEqual); + return TT::Less; + } + if (current() == '>') { + step(); + if (current() == '=') + return stepped(TT::GreaterEqual); + return TT::Greater; + } + return {}; +} + +auto constexpr Lexer::skip_multiline_comment() noexcept + -> Result +{ + step(); + auto last = current(); + step(); + while (!done() and last != '*' and current() != '/') + step(); + if (last != '*' or current() != '/') + return {}; + step(); + return TokenTypes::MultilineComment; +} + +auto constexpr Lexer::skip_singleline_comment() noexcept + -> Result +{ + step(); + while (!done() and current() != '\n') + step(); + if (current() == '\n') + step(); + return TokenTypes::SinglelineComment; +} + +} diff --git a/scriptlang/lexer.hpp b/scriptlang/lexer.hpp new file mode 100644 index 0000000..d2a2cff --- /dev/null +++ b/scriptlang/lexer.hpp @@ -0,0 +1,141 @@ +#include "utils.hpp" +#include +#include + +namespace scriptlang { + +enum class TokenTypes { + Eof, + + MultilineComment, + SinglelineComment, + + Id, + Int, + Float, + + If, + Else, + For, + Loop, + While, + Break, + Continue, + Fn, + Return, + False, + True, + And, + Or, + Xor, + + LParen, + RParen, + LBrace, + RBrace, + LBracket, + RBracket, + Dot, + Comma, + Colon, + Semicolon, + + Plus, + DoublePlus, + PlusEqual, + + Minus, + ThinArrow, + DoubleMinus, + MinusEqual, + + Asterisk, + AsteriskEqual, + + Slash, + SlashEqual, + + Percent, + PercentEqual, + + Power, + PowerEqual, + + Equal, + FatArrow, + DoubleEqual, + + Exlamation, + ExlamationEqual, + + Less, + LessEqual, + + Greater, + GreaterEqual, +}; + +struct Token { + TokenTypes type; + size_t index, length; + int line, column; +}; + +class Lexer { +public: + Lexer(std::string_view text) + : text { text } + { } + auto constexpr next() noexcept -> Result; + auto peek() noexcept -> Result + { + if (last_token) + return Result::create_ok(*last_token); + return {}; + } + +private: + auto constexpr make_number() noexcept -> Result; + auto constexpr make_id() noexcept -> Result; + auto constexpr id_or_keyword_type(std::string_view substring) noexcept + -> TokenTypes; + auto constexpr make_static() noexcept -> Result; + auto constexpr static_token_type() noexcept -> Result; + auto constexpr skip_multiline_comment() noexcept + -> Result; + auto constexpr skip_singleline_comment() noexcept + -> Result; + + [[nodiscard]] auto constexpr inline token( + TokenTypes type, size_t begin) const noexcept -> Token + { + return Token { type, begin, index - begin, line, column }; + } + [[nodiscard]] auto constexpr inline done() const noexcept -> bool + { + return index >= text.size(); + } + [[nodiscard]] auto constexpr inline current() const noexcept -> char + { + return text.at(index); + } + auto constexpr inline step() noexcept -> void + { + if (done()) + return; + index++; + column++; + if (!done() and text.at(index) == '\n') { + column = 1; + line++; + } + } + + std::string_view text; + size_t index = 0; + int line = 1; + int column = 1; + std::optional last_token; +}; + +} diff --git a/scriptlang/meson.build b/scriptlang/meson.build new file mode 100644 index 0000000..6b2d25c --- /dev/null +++ b/scriptlang/meson.build @@ -0,0 +1,28 @@ + +scriptlang_sources = files( + 'parser.cpp', + 'lexer.cpp', +) + +scriptlang_inc = include_directories('.') + +fmt_dep = dependency('fmt') + +scriptlang_lib = static_library( + 'web-scriptlang', + scriptlang_sources, + include_directories: [ + scriptlang_inc, + ], + dependencies: [ + fmt_dep, + utils_dep, + ], +) + +scriptlang_dep = declare_dependency( + link_with: scriptlang_lib, + include_directories: [ + scriptlang_inc, + ], +) diff --git a/scriptlang/parser.cpp b/scriptlang/parser.cpp new file mode 100644 index 0000000..89283cb --- /dev/null +++ b/scriptlang/parser.cpp @@ -0,0 +1,3 @@ +#include "parser.hpp" + +namespace scriptlang { } diff --git a/scriptlang/parser.hpp b/scriptlang/parser.hpp new file mode 100644 index 0000000..ed0700f --- /dev/null +++ b/scriptlang/parser.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace scriptlang { + +class Parser final { }; + +} diff --git a/server/meson.build b/server/meson.build index 235f926..aa8b02f 100644 --- a/server/meson.build +++ b/server/meson.build @@ -1,3 +1,17 @@ -server_sources += files( + +server_sources = files( 'main.cpp', ) + +fmt_dep = dependency('fmt') + +server_exe = executable( + 'web-server', + server_sources, + win_subsystem: 'console', + dependencies: [ + fmt_dep, + utils_dep, + markup_dep, + ], +) diff --git a/utils/meson.build b/utils/meson.build new file mode 100644 index 0000000..8f2cae1 --- /dev/null +++ b/utils/meson.build @@ -0,0 +1,24 @@ + +utils_sources = files() + +utils_inc = include_directories('.') + +fmt_dep = dependency('fmt') + +utils_lib = static_library( + 'web-utils', + utils_sources, + include_directories: [ + utils_inc, + ], + dependencies: [ + fmt_dep, + ], +) + +utils_dep = declare_dependency( + link_with: utils_lib, + include_directories: [ + utils_inc, + ], +) diff --git a/utils/result.hpp b/utils/result.hpp new file mode 100644 index 0000000..d34ee47 --- /dev/null +++ b/utils/result.hpp @@ -0,0 +1,645 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace utils::result { + +class UnwrapError : public std::runtime_error { +public: + UnwrapError() + : std::runtime_error("failed to unwrap result") + { } +}; + +struct StatesValueTypes { + struct Value { }; + struct Error { }; +}; + +template struct Extracter { + using Value = void; + using Error = void; +}; + +template class [[nodiscard]] Result { + static_assert(std::is_object_v && std::is_destructible_v, + "incompatible Value in Result"); + static_assert(std::is_object_v && std::is_destructible_v, + "incompatible Error in Result"); + +public: + constexpr Result(Value&& value) noexcept + : value_or_error(std::forward(value)) + { } + constexpr Result(Error&& error) noexcept + : value_or_error(std::forward(error)) + { } + + explicit constexpr Result( + Value&& value, [[maybe_unused]] StatesValueTypes::Value svt) noexcept + : value_or_error(std::forward(value)) + { } + explicit constexpr Result( + Error&& error, [[maybe_unused]] StatesValueTypes::Error svt) noexcept + : value_or_error(std::forward(error)) + { } + + [[nodiscard]] static auto create_ok(Value value) noexcept + { + return Result( + std::move(value), StatesValueTypes::Value {}); + } + [[nodiscard]] static auto create_error(Error error) noexcept + { + return Result( + std::move(error), StatesValueTypes::Error {}); + } + + [[nodiscard]] constexpr auto is_ok() const noexcept -> bool + { + return std::holds_alternative(value_or_error); + } + [[nodiscard]] constexpr auto is_error() const noexcept -> bool + { + return std::holds_alternative(value_or_error); + } + + [[nodiscard]] constexpr auto unwrap() & -> Value& + { + if (!is_ok()) + throw UnwrapError(); + return std::get(value_or_error); + } + [[nodiscard]] constexpr auto unwrap() const& -> const Value& + { + if (!is_ok()) + throw UnwrapError(); + return std::get(value_or_error); + } + + [[nodiscard]] constexpr auto unwrap_error() & -> Error& + { + if (!is_error()) + throw UnwrapError(); + return std::get(value_or_error); + } + [[nodiscard]] constexpr auto unwrap_error() const& -> const Error& + { + if (!is_error()) + throw UnwrapError(); + return std::get(value_or_error); + } + + [[nodiscard]] constexpr auto unwrap() && -> Value&& + { + if (!is_ok()) + throw UnwrapError(); + return std::move(std::get(value_or_error)); + } + [[nodiscard]] constexpr auto unwrap() const&& -> const Value&& + { + if (!is_ok()) + throw UnwrapError(); + return std::move(std::get(value_or_error)); + } + + [[nodiscard]] constexpr auto unwrap_error() && -> Error&& + { + if (!is_error()) + throw UnwrapError(); + return std::move(std::get(value_or_error)); + } + [[nodiscard]] constexpr auto unwrap_error() const&& -> const Error&& + { + if (!is_error()) + throw UnwrapError(); + return std::move(std::get(value_or_error)); + } + + [[nodiscard]] constexpr auto ok() noexcept -> std::optional + { + return is_ok() ? unwrap() : std::nullopt; + } + [[nodiscard]] constexpr auto ok() const noexcept + -> std::optional + { + return is_ok() ? unwrap() : std::nullopt; + } + + [[nodiscard]] constexpr auto error() noexcept -> std::optional + { + return is_error() ? unwrap_error() : std::nullopt; + } + [[nodiscard]] constexpr auto error() const noexcept + -> std::optional + { + return is_error() ? unwrap_error() : std::nullopt; + } + + [[nodiscard]] constexpr operator bool() const noexcept { return is_ok(); } + + [[nodiscard]] constexpr auto operator->() -> Value* { return &unwrap(); } + [[nodiscard]] constexpr auto operator->() const -> const Value* + { + return &unwrap(); + } + + [[nodiscard]] constexpr auto operator*() -> Value& { return unwrap(); } + [[nodiscard]] constexpr auto operator*() const -> const Value& + { + return unwrap(); + } + + [[nodiscard]] constexpr auto map(auto func) noexcept + { + using NewValue = decltype(func(unwrap())); + return is_ok() ? Result(func(unwrap())) + : Result(unwrap_error()); + } + + [[nodiscard]] constexpr auto map(auto func) const noexcept + { + using NewValue = decltype(func(unwrap())); + return is_ok() ? Result(func(unwrap())) + : Result(unwrap_error()); + } + + [[nodiscard]] constexpr auto map_error(auto func) noexcept + { + using NewError = decltype(func(unwrap_error())); + return is_error() ? Result(func(unwrap_error())) + : Result(unwrap()); + } + + [[nodiscard]] constexpr auto map_error(auto func) const noexcept + { + using NewError = decltype(func(unwrap_error())); + return is_error() ? Result(func(unwrap_error())) + : Result(unwrap()); + } + + [[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept + requires std::same_as + { + return is_ok() ? if_ok(unwrap()) : if_error(unwrap_error()); + } + [[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept + requires std::same_as + { + return is_ok() ? if_ok(unwrap()) : if_error(unwrap_error()); + } + + [[nodiscard]] constexpr auto flatten() noexcept + requires std::same_as::Error> + { + using InnerValue = typename Extracter::Value; + return is_ok() ? unwrap() : Result(unwrap_error()); + } + [[nodiscard]] constexpr auto flatten() const noexcept + requires std::same_as::Error> + { + using InnerValue = typename Extracter::Value; + return is_ok() ? unwrap() : Result(unwrap_error()); + } + + [[nodiscard]] constexpr auto flatten_error() noexcept + requires std::same_as::Value> + { + using InnerError = typename Extracter::Error; + return is_error() ? unwrap_error() + : Result(unwrap()); + } + [[nodiscard]] constexpr auto flatten_error() const noexcept + requires std::same_as::Value> + { + using InnerError = typename Extracter::Error; + return is_error() ? unwrap_error() + : Result(unwrap()); + } + + [[nodiscard]] constexpr auto flat_map(auto func) noexcept + { + return map(func).flatten(); + } + + [[nodiscard]] constexpr auto flat_map(auto func) const noexcept + { + return map(func).flatten(); + } + + [[nodiscard]] constexpr auto flat_map_error(auto func) noexcept + { + return map_error(func).flatten_error(); + } + + [[nodiscard]] constexpr auto flat_map_error(auto func) const noexcept + { + return map_error(func).flatten_error(); + } + +private: + std::variant value_or_error; +}; + +template +struct Extracter> { + using Value = ValueInferer; + using Error = ErrorInferer; +}; + +template class [[nodiscard]] Result { + static_assert(std::is_object_v && std::is_destructible_v, + "incompatible Error in Result"); + +public: + constexpr Result(Value&& value) noexcept + : maybe_value { std::forward(value) } {}; + constexpr Result() noexcept + : maybe_value {} + { } + + [[nodiscard]] static auto create_ok(Value value) noexcept + { + return Result(std::move(value)); + } + [[nodiscard]] static auto create_error() noexcept + { + return Result(); + } + + [[nodiscard]] constexpr auto is_ok() const noexcept + { + return maybe_value.has_value(); + } + [[nodiscard]] constexpr auto is_error() const noexcept + { + return !maybe_value.has_value(); + } + + [[nodiscard]] constexpr auto unwrap() & -> Value& + { + if (!is_ok()) + throw UnwrapError(); + return *maybe_value; + } + [[nodiscard]] constexpr auto unwrap() const& -> const Value& + { + if (!is_ok()) + throw UnwrapError(); + return *maybe_value; + } + + [[nodiscard]] constexpr auto unwrap() && -> Value&& + { + if (!is_ok()) + throw UnwrapError(); + return std::move(*maybe_value); + } + [[nodiscard]] constexpr auto unwrap() const&& -> const Value&& + { + if (!is_ok()) + throw UnwrapError(); + return std::move(*maybe_value); + } + + [[nodiscard]] constexpr auto ok() noexcept -> std::optional + { + return is_ok() ? unwrap() : std::nullopt; + } + [[nodiscard]] constexpr auto ok() const noexcept + -> std::optional + { + return is_ok() ? unwrap() : std::nullopt; + } + + [[nodiscard]] constexpr operator bool() const noexcept { return is_ok(); } + + [[nodiscard]] constexpr auto operator->() -> Value* { return &unwrap(); } + [[nodiscard]] constexpr auto operator->() const -> const Value* + { + return &unwrap(); + } + + [[nodiscard]] constexpr auto operator*() -> Value& { return unwrap(); } + [[nodiscard]] constexpr auto operator*() const -> const Value& + { + return unwrap(); + } + + [[nodiscard]] constexpr auto map(auto func) noexcept + { + using NewValue = decltype(func(unwrap())); + return is_ok() ? Result(func(unwrap())) + : Result(); + } + + [[nodiscard]] constexpr auto map(auto func) const noexcept + { + using NewValue = decltype(func(unwrap())); + return is_ok() ? Result(func(unwrap())) + : Result(); + } + + [[nodiscard]] constexpr auto map_error(auto func) noexcept + { + using NewError = decltype(func()); + return is_error() ? Result(func()) + : Result(unwrap()); + } + + [[nodiscard]] constexpr auto map_error(auto func) const noexcept + { + using NewError = decltype(func()); + return is_error() ? Result(func()) + : Result(unwrap()); + } + + [[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept + requires std::same_as + { + return is_ok() ? if_ok(unwrap()) : if_error(); + } + [[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept + requires std::same_as + { + return is_ok() ? if_ok(unwrap()) : if_error(); + } + + [[nodiscard]] constexpr auto flatten() noexcept + requires std::same_as::Error> + { + using InnerValue = typename Extracter::Value; + return is_ok() ? unwrap() : Result(); + } + [[nodiscard]] constexpr auto flatten() const noexcept + requires std::same_as::Error> + { + using InnerValue = typename Extracter::Value; + return is_ok() ? unwrap() : Result(); + } + + [[nodiscard]] constexpr auto flat_map(auto func) noexcept + { + return map(func).flatten(); + } + + [[nodiscard]] constexpr auto flat_map(auto func) const noexcept + { + return map(func).flatten(); + } + + [[nodiscard]] constexpr auto flat_map_error(auto func) noexcept + { + return map_error(func).flatten_error(); + } + + [[nodiscard]] constexpr auto flat_map_error(auto func) const noexcept + { + return map_error(func).flatten_error(); + } + +private: + std::optional maybe_value; +}; + +template class [[nodiscard]] Result { + static_assert(std::is_object_v && std::is_destructible_v, + "incompatible Error in Result"); + +public: + constexpr Result(Error&& error) noexcept + : maybe_error { std::forward(error) } + { } + constexpr Result() noexcept + : maybe_error {} {}; + + [[nodiscard]] static auto create_ok() noexcept + { + return Result(); + } + [[nodiscard]] static auto create_error(Error error) noexcept + { + return Result(std::move(error)); + } + + [[nodiscard]] constexpr auto is_ok() const noexcept + { + return !maybe_error.has_value(); + } + [[nodiscard]] constexpr auto is_error() const noexcept + { + return maybe_error.has_value(); + } + + [[nodiscard]] constexpr auto unwrap_error() & -> Error& + { + if (!is_error()) + throw UnwrapError(); + return *maybe_error; + } + [[nodiscard]] constexpr auto unwrap_error() const& -> const Error& + { + if (!is_error()) + throw UnwrapError(); + return *maybe_error; + } + + [[nodiscard]] constexpr auto unwrap_error() && -> Error&& + { + if (!is_error()) + throw UnwrapError(); + return std::move(*maybe_error); + } + [[nodiscard]] constexpr auto unwrap_error() const&& -> const Error&& + { + if (!is_error()) + throw UnwrapError(); + return std::move(*maybe_error); + } + + [[nodiscard]] constexpr auto error() noexcept -> std::optional + { + return is_error() ? unwrap_error() : std::nullopt; + } + [[nodiscard]] constexpr auto error() const noexcept + -> std::optional + { + return is_error() ? unwrap_error() : std::nullopt; + } + + [[nodiscard]] constexpr operator bool() const noexcept { return is_ok(); } + + [[nodiscard]] constexpr auto map(auto func) noexcept + { + using NewValue = decltype(func()); + return is_ok() ? Result(func()) + : Result(unwrap_error()); + } + + [[nodiscard]] constexpr auto map(auto func) const noexcept + { + using NewValue = decltype(func()); + return is_ok() ? Result(func()) + : Result(unwrap_error()); + } + + [[nodiscard]] constexpr auto map_error(auto func) noexcept + { + using NewError = decltype(func(unwrap_error())); + return is_error() ? Result(func(unwrap_error())) + : Result(); + } + + [[nodiscard]] constexpr auto map_error(auto func) const noexcept + { + using NewError = decltype(func(unwrap_error())); + return is_error() ? Result(func(unwrap_error())) + : Result(); + } + + [[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept + requires std::same_as + { + return is_ok() ? if_ok() : if_error(unwrap_error()); + } + [[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept + requires std::same_as + { + return is_ok() ? if_ok() : if_error(unwrap_error()); + } + + [[nodiscard]] constexpr auto flatten_error() noexcept + requires std::same_as::Value> + { + using InnerError = typename Extracter::Error; + return is_error() ? unwrap_error() : Result(); + } + [[nodiscard]] constexpr auto flatten_error() const noexcept + requires std::same_as::Value> + { + using InnerError = typename Extracter::Error; + return is_error() ? unwrap_error() : Result(); + } + + [[nodiscard]] constexpr auto flat_map(auto func) noexcept + { + return map(func).flatten(); + } + + [[nodiscard]] constexpr auto flat_map(auto func) const noexcept + { + return map(func).flatten(); + } + + [[nodiscard]] constexpr auto flat_map_error(auto func) noexcept + { + return map_error(func).flatten_error(); + } + + [[nodiscard]] constexpr auto flat_map_error(auto func) const noexcept + { + return map_error(func).flatten_error(); + } + +private: + std::optional maybe_error; +}; + +template <> class [[nodiscard]] Result { +public: + enum class States { Ok, Error }; + + explicit constexpr Result(States state) noexcept + : state { state } {}; + + [[nodiscard]] static constexpr auto create_ok() noexcept + { + return Result(States::Ok); + } + [[nodiscard]] static constexpr auto create_error() noexcept + { + return Result(States::Error); + } + + [[nodiscard]] constexpr auto is_ok() const noexcept + { + return state == States::Ok; + } + [[nodiscard]] constexpr auto is_error() const noexcept + { + return state == States::Error; + } + + [[nodiscard]] constexpr operator bool() const noexcept { return is_ok(); } + + [[nodiscard]] constexpr auto map(auto func) noexcept + { + using NewValue = decltype(func()); + return is_ok() ? Result(func()) + : Result(); + } + + [[nodiscard]] constexpr auto map(auto func) const noexcept + { + using NewValue = decltype(func()); + return is_ok() ? Result(func()) + : Result(); + } + + [[nodiscard]] constexpr auto map_error(auto func) noexcept + { + using NewError = decltype(func()); + return is_error() ? Result(func()) + : Result(); + } + + [[nodiscard]] constexpr auto map_error(auto func) const noexcept + { + using NewError = decltype(func()); + return is_error() ? Result(func()) + : Result(); + } + + [[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept + requires std::same_as + { + return is_ok() ? if_ok() : if_error(); + } + [[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept + requires std::same_as + { + return is_ok() ? if_ok() : if_error(); + } + + [[nodiscard]] constexpr auto flat_map(auto func) noexcept + { + return map(func).flatten(); + } + + [[nodiscard]] constexpr auto flat_map(auto func) const noexcept + { + return map(func).flatten(); + } + + [[nodiscard]] constexpr auto flat_map_error(auto func) noexcept + { + return map_error(func).flatten_error(); + } + + [[nodiscard]] constexpr auto flat_map_error(auto func) const noexcept + { + return map_error(func).flatten_error(); + } + +private: + States state; +}; + +} diff --git a/utils/utils.hpp b/utils/utils.hpp new file mode 100644 index 0000000..e3ab7a3 --- /dev/null +++ b/utils/utils.hpp @@ -0,0 +1,7 @@ +#include "result.hpp" + +namespace utils { +using result::Result; +} + +using utils::Result;