scriptlang: lexer done

This commit is contained in:
SimonFJ20 2023-01-09 01:15:32 +01:00
parent b838611fa0
commit f7077274fe
15 changed files with 1146 additions and 36 deletions

View File

@ -14,6 +14,7 @@ Diagnostics:
- readability-magic-numbers - readability-magic-numbers
- readability-identifier-length - readability-identifier-length
- bugprone-easily-swappable-parameters - bugprone-easily-swappable-parameters
- readability-convert-member-functions-to-static
CheckOptions: CheckOptions:
UnusedIncludes: Strict UnusedIncludes: Strict

View File

@ -1,3 +1,20 @@
browser_sources += files(
browser_sources = files(
'main.cpp', '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,
],
)

27
markup/meson.build Normal file
View File

@ -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,
],
)

3
markup/parser.cpp Normal file
View File

@ -0,0 +1,3 @@
#include "parser.hpp"
namespace languages { }

7
markup/parser.hpp Normal file
View File

@ -0,0 +1,7 @@
#pragma once
namespace markup {
class Parser final { };
}

View File

@ -4,43 +4,12 @@ project(
version: '0.1', version: '0.1',
default_options: [ default_options: [
'warning_level=3', 'warning_level=3',
'werror=false',
'cpp_std=c++20', 'cpp_std=c++20',
], ],
) )
fmt_dep = dependency('fmt') subdir('utils')
sdl2_dep = dependency('sdl2') subdir('scriptlang')
subdir('markup')
server_sources = []
subdir('server') 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') 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,
],
)

217
scriptlang/lexer.cpp Normal file
View File

@ -0,0 +1,217 @@
#include "lexer.hpp"
#include <cctype>
#include <string_view>
namespace scriptlang {
auto constexpr Lexer::next() noexcept -> Result<Token, void>
{
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<Token, void>
{
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<Token, void>
{
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<Token, void>
{
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<TokenTypes, void>
{
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<TokenTypes, void>
{
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<TokenTypes, void>
{
step();
while (!done() and current() != '\n')
step();
if (current() == '\n')
step();
return TokenTypes::SinglelineComment;
}
}

141
scriptlang/lexer.hpp Normal file
View File

@ -0,0 +1,141 @@
#include "utils.hpp"
#include <optional>
#include <string_view>
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<Token, void>;
auto peek() noexcept -> Result<Token, void>
{
if (last_token)
return Result<Token, void>::create_ok(*last_token);
return {};
}
private:
auto constexpr make_number() noexcept -> Result<Token, void>;
auto constexpr make_id() noexcept -> Result<Token, void>;
auto constexpr id_or_keyword_type(std::string_view substring) noexcept
-> TokenTypes;
auto constexpr make_static() noexcept -> Result<Token, void>;
auto constexpr static_token_type() noexcept -> Result<TokenTypes, void>;
auto constexpr skip_multiline_comment() noexcept
-> Result<TokenTypes, void>;
auto constexpr skip_singleline_comment() noexcept
-> Result<TokenTypes, void>;
[[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<Token> last_token;
};
}

28
scriptlang/meson.build Normal file
View File

@ -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,
],
)

3
scriptlang/parser.cpp Normal file
View File

@ -0,0 +1,3 @@
#include "parser.hpp"
namespace scriptlang { }

7
scriptlang/parser.hpp Normal file
View File

@ -0,0 +1,7 @@
#pragma once
namespace scriptlang {
class Parser final { };
}

View File

@ -1,3 +1,17 @@
server_sources += files(
server_sources = files(
'main.cpp', 'main.cpp',
) )
fmt_dep = dependency('fmt')
server_exe = executable(
'web-server',
server_sources,
win_subsystem: 'console',
dependencies: [
fmt_dep,
utils_dep,
markup_dep,
],
)

24
utils/meson.build Normal file
View File

@ -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,
],
)

645
utils/result.hpp Normal file
View File

@ -0,0 +1,645 @@
#pragma once
#include <concepts>
#include <functional>
#include <memory>
#include <optional>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <variant>
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 <typename InnerValue> struct Extracter {
using Value = void;
using Error = void;
};
template <typename Value, typename Error> class [[nodiscard]] Result {
static_assert(std::is_object_v<Value> && std::is_destructible_v<Value>,
"incompatible Value in Result<Value, Error>");
static_assert(std::is_object_v<Error> && std::is_destructible_v<Error>,
"incompatible Error in Result<Value, Error>");
public:
constexpr Result(Value&& value) noexcept
: value_or_error(std::forward<Value>(value))
{ }
constexpr Result(Error&& error) noexcept
: value_or_error(std::forward<Error>(error))
{ }
explicit constexpr Result(
Value&& value, [[maybe_unused]] StatesValueTypes::Value svt) noexcept
: value_or_error(std::forward<Value>(value))
{ }
explicit constexpr Result(
Error&& error, [[maybe_unused]] StatesValueTypes::Error svt) noexcept
: value_or_error(std::forward<Error>(error))
{ }
[[nodiscard]] static auto create_ok(Value value) noexcept
{
return Result<Value, Error>(
std::move(value), StatesValueTypes::Value {});
}
[[nodiscard]] static auto create_error(Error error) noexcept
{
return Result<Value, Error>(
std::move(error), StatesValueTypes::Error {});
}
[[nodiscard]] constexpr auto is_ok() const noexcept -> bool
{
return std::holds_alternative<Value>(value_or_error);
}
[[nodiscard]] constexpr auto is_error() const noexcept -> bool
{
return std::holds_alternative<Error>(value_or_error);
}
[[nodiscard]] constexpr auto unwrap() & -> Value&
{
if (!is_ok())
throw UnwrapError();
return std::get<Value>(value_or_error);
}
[[nodiscard]] constexpr auto unwrap() const& -> const Value&
{
if (!is_ok())
throw UnwrapError();
return std::get<Value>(value_or_error);
}
[[nodiscard]] constexpr auto unwrap_error() & -> Error&
{
if (!is_error())
throw UnwrapError();
return std::get<Error>(value_or_error);
}
[[nodiscard]] constexpr auto unwrap_error() const& -> const Error&
{
if (!is_error())
throw UnwrapError();
return std::get<Error>(value_or_error);
}
[[nodiscard]] constexpr auto unwrap() && -> Value&&
{
if (!is_ok())
throw UnwrapError();
return std::move(std::get<Value>(value_or_error));
}
[[nodiscard]] constexpr auto unwrap() const&& -> const Value&&
{
if (!is_ok())
throw UnwrapError();
return std::move(std::get<Value>(value_or_error));
}
[[nodiscard]] constexpr auto unwrap_error() && -> Error&&
{
if (!is_error())
throw UnwrapError();
return std::move(std::get<Error>(value_or_error));
}
[[nodiscard]] constexpr auto unwrap_error() const&& -> const Error&&
{
if (!is_error())
throw UnwrapError();
return std::move(std::get<Error>(value_or_error));
}
[[nodiscard]] constexpr auto ok() noexcept -> std::optional<Value&>
{
return is_ok() ? unwrap() : std::nullopt;
}
[[nodiscard]] constexpr auto ok() const noexcept
-> std::optional<const Value&>
{
return is_ok() ? unwrap() : std::nullopt;
}
[[nodiscard]] constexpr auto error() noexcept -> std::optional<Error&>
{
return is_error() ? unwrap_error() : std::nullopt;
}
[[nodiscard]] constexpr auto error() const noexcept
-> std::optional<const Error&>
{
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<NewValue, Error>(func(unwrap()))
: Result<NewValue, Error>(unwrap_error());
}
[[nodiscard]] constexpr auto map(auto func) const noexcept
{
using NewValue = decltype(func(unwrap()));
return is_ok() ? Result<NewValue, Error>(func(unwrap()))
: Result<NewValue, Error>(unwrap_error());
}
[[nodiscard]] constexpr auto map_error(auto func) noexcept
{
using NewError = decltype(func(unwrap_error()));
return is_error() ? Result<Value, NewError>(func(unwrap_error()))
: Result<Value, NewError>(unwrap());
}
[[nodiscard]] constexpr auto map_error(auto func) const noexcept
{
using NewError = decltype(func(unwrap_error()));
return is_error() ? Result<Value, NewError>(func(unwrap_error()))
: Result<Value, NewError>(unwrap());
}
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept
requires std::same_as<decltype(if_ok()), decltype(if_error())>
{
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<decltype(if_ok()), decltype(if_error())>
{
return is_ok() ? if_ok(unwrap()) : if_error(unwrap_error());
}
[[nodiscard]] constexpr auto flatten() noexcept
requires std::same_as<Error, typename Extracter<Value>::Error>
{
using InnerValue = typename Extracter<Value>::Value;
return is_ok() ? unwrap() : Result<InnerValue, Error>(unwrap_error());
}
[[nodiscard]] constexpr auto flatten() const noexcept
requires std::same_as<Error, typename Extracter<Value>::Error>
{
using InnerValue = typename Extracter<Value>::Value;
return is_ok() ? unwrap() : Result<InnerValue, Error>(unwrap_error());
}
[[nodiscard]] constexpr auto flatten_error() noexcept
requires std::same_as<Value, typename Extracter<Error>::Value>
{
using InnerError = typename Extracter<Error>::Error;
return is_error() ? unwrap_error()
: Result<Value, InnerError>(unwrap());
}
[[nodiscard]] constexpr auto flatten_error() const noexcept
requires std::same_as<Value, typename Extracter<Error>::Value>
{
using InnerError = typename Extracter<Error>::Error;
return is_error() ? unwrap_error()
: Result<Value, InnerError>(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, Error> value_or_error;
};
template <typename ValueInferer, typename ErrorInferer>
struct Extracter<Result<ValueInferer, ErrorInferer>> {
using Value = ValueInferer;
using Error = ErrorInferer;
};
template <typename Value> class [[nodiscard]] Result<Value, void> {
static_assert(std::is_object_v<Value> && std::is_destructible_v<Value>,
"incompatible Error in Result<Value, Error>");
public:
constexpr Result(Value&& value) noexcept
: maybe_value { std::forward<Value>(value) } {};
constexpr Result() noexcept
: maybe_value {}
{ }
[[nodiscard]] static auto create_ok(Value value) noexcept
{
return Result<Value, void>(std::move(value));
}
[[nodiscard]] static auto create_error() noexcept
{
return Result<Value, void>();
}
[[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<Value&>
{
return is_ok() ? unwrap() : std::nullopt;
}
[[nodiscard]] constexpr auto ok() const noexcept
-> std::optional<const Value&>
{
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<NewValue, void>(func(unwrap()))
: Result<NewValue, void>();
}
[[nodiscard]] constexpr auto map(auto func) const noexcept
{
using NewValue = decltype(func(unwrap()));
return is_ok() ? Result<NewValue, void>(func(unwrap()))
: Result<NewValue, void>();
}
[[nodiscard]] constexpr auto map_error(auto func) noexcept
{
using NewError = decltype(func());
return is_error() ? Result<Value, NewError>(func())
: Result<Value, NewError>(unwrap());
}
[[nodiscard]] constexpr auto map_error(auto func) const noexcept
{
using NewError = decltype(func());
return is_error() ? Result<Value, NewError>(func())
: Result<Value, NewError>(unwrap());
}
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept
requires std::same_as<decltype(if_ok()), decltype(if_error())>
{
return is_ok() ? if_ok(unwrap()) : if_error();
}
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept
requires std::same_as<decltype(if_ok()), decltype(if_error())>
{
return is_ok() ? if_ok(unwrap()) : if_error();
}
[[nodiscard]] constexpr auto flatten() noexcept
requires std::same_as<void, typename Extracter<Value>::Error>
{
using InnerValue = typename Extracter<Value>::Value;
return is_ok() ? unwrap() : Result<InnerValue, void>();
}
[[nodiscard]] constexpr auto flatten() const noexcept
requires std::same_as<void, typename Extracter<Value>::Error>
{
using InnerValue = typename Extracter<Value>::Value;
return is_ok() ? unwrap() : Result<InnerValue, void>();
}
[[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<Value> maybe_value;
};
template <typename Error> class [[nodiscard]] Result<void, Error> {
static_assert(std::is_object_v<Error> && std::is_destructible_v<Error>,
"incompatible Error in Result<Value, Error>");
public:
constexpr Result(Error&& error) noexcept
: maybe_error { std::forward<Error>(error) }
{ }
constexpr Result() noexcept
: maybe_error {} {};
[[nodiscard]] static auto create_ok() noexcept
{
return Result<void, Error>();
}
[[nodiscard]] static auto create_error(Error error) noexcept
{
return Result<void, Error>(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<Error&>
{
return is_error() ? unwrap_error() : std::nullopt;
}
[[nodiscard]] constexpr auto error() const noexcept
-> std::optional<const Error&>
{
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<NewValue, Error>(func())
: Result<NewValue, Error>(unwrap_error());
}
[[nodiscard]] constexpr auto map(auto func) const noexcept
{
using NewValue = decltype(func());
return is_ok() ? Result<NewValue, Error>(func())
: Result<NewValue, Error>(unwrap_error());
}
[[nodiscard]] constexpr auto map_error(auto func) noexcept
{
using NewError = decltype(func(unwrap_error()));
return is_error() ? Result<void, NewError>(func(unwrap_error()))
: Result<void, NewError>();
}
[[nodiscard]] constexpr auto map_error(auto func) const noexcept
{
using NewError = decltype(func(unwrap_error()));
return is_error() ? Result<void, NewError>(func(unwrap_error()))
: Result<void, NewError>();
}
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept
requires std::same_as<decltype(if_ok()), decltype(if_error())>
{
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<decltype(if_ok()), decltype(if_error())>
{
return is_ok() ? if_ok() : if_error(unwrap_error());
}
[[nodiscard]] constexpr auto flatten_error() noexcept
requires std::same_as<void, typename Extracter<Error>::Value>
{
using InnerError = typename Extracter<Error>::Error;
return is_error() ? unwrap_error() : Result<void, InnerError>();
}
[[nodiscard]] constexpr auto flatten_error() const noexcept
requires std::same_as<void, typename Extracter<Error>::Value>
{
using InnerError = typename Extracter<Error>::Error;
return is_error() ? unwrap_error() : Result<void, InnerError>();
}
[[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<Error> maybe_error;
};
template <> class [[nodiscard]] Result<void, void> {
public:
enum class States { Ok, Error };
explicit constexpr Result(States state) noexcept
: state { state } {};
[[nodiscard]] static constexpr auto create_ok() noexcept
{
return Result<void, void>(States::Ok);
}
[[nodiscard]] static constexpr auto create_error() noexcept
{
return Result<void, void>(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<NewValue, void>(func())
: Result<NewValue, void>();
}
[[nodiscard]] constexpr auto map(auto func) const noexcept
{
using NewValue = decltype(func());
return is_ok() ? Result<NewValue, void>(func())
: Result<NewValue, void>();
}
[[nodiscard]] constexpr auto map_error(auto func) noexcept
{
using NewError = decltype(func());
return is_error() ? Result<void, NewError>(func())
: Result<void, NewError>();
}
[[nodiscard]] constexpr auto map_error(auto func) const noexcept
{
using NewError = decltype(func());
return is_error() ? Result<void, NewError>(func())
: Result<void, NewError>();
}
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept
requires std::same_as<decltype(if_ok()), decltype(if_error())>
{
return is_ok() ? if_ok() : if_error();
}
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept
requires std::same_as<decltype(if_ok()), decltype(if_error())>
{
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;
};
}

7
utils/utils.hpp Normal file
View File

@ -0,0 +1,7 @@
#include "result.hpp"
namespace utils {
using result::Result;
}
using utils::Result;