scriptlang: lexer done
This commit is contained in:
parent
b838611fa0
commit
f7077274fe
1
.clangd
1
.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
|
||||
|
@ -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,
|
||||
],
|
||||
)
|
||||
|
27
markup/meson.build
Normal file
27
markup/meson.build
Normal 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
3
markup/parser.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "parser.hpp"
|
||||
|
||||
namespace languages { }
|
7
markup/parser.hpp
Normal file
7
markup/parser.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace markup {
|
||||
|
||||
class Parser final { };
|
||||
|
||||
}
|
37
meson.build
37
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,
|
||||
],
|
||||
)
|
||||
|
217
scriptlang/lexer.cpp
Normal file
217
scriptlang/lexer.cpp
Normal 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
141
scriptlang/lexer.hpp
Normal 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
28
scriptlang/meson.build
Normal 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
3
scriptlang/parser.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "parser.hpp"
|
||||
|
||||
namespace scriptlang { }
|
7
scriptlang/parser.hpp
Normal file
7
scriptlang/parser.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace scriptlang {
|
||||
|
||||
class Parser final { };
|
||||
|
||||
}
|
@ -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,
|
||||
],
|
||||
)
|
||||
|
24
utils/meson.build
Normal file
24
utils/meson.build
Normal 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
645
utils/result.hpp
Normal 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
7
utils/utils.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#include "result.hpp"
|
||||
|
||||
namespace utils {
|
||||
using result::Result;
|
||||
}
|
||||
|
||||
using utils::Result;
|
Loading…
Reference in New Issue
Block a user