some parsing

This commit is contained in:
SimonFJ20 2023-01-17 01:21:19 +01:00
parent e18cf2f52e
commit 391f04e4cb
9 changed files with 731 additions and 70 deletions

View File

@ -16,6 +16,8 @@ Diagnostics:
- bugprone-easily-swappable-parameters - bugprone-easily-swappable-parameters
- readability-convert-member-functions-to-static - readability-convert-member-functions-to-static
- bugprone-exception-escape - bugprone-exception-escape
- modernize-use-nodiscard
- readability-else-after-return
CheckOptions: CheckOptions:
UnusedIncludes: Strict UnusedIncludes: Strict

4
.vscode/launch.json vendored
View File

@ -8,9 +8,9 @@
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Debug Browser", "name": "Debug Browser",
"program": "${workspaceFolder}/builddir/web-browser", "program": "${workspaceFolder}/builddir/browser",
"args": [], "args": [],
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}/builddir"
} }
] ]
} }

114
bong_grammar.txt Normal file
View File

@ -0,0 +1,114 @@
top_level ->
| element
| value
element -> Name single_line_fields element_body:?
element_body -> "{" element_fields "}"
element_fields -> (_ element_field (__linebreak__ element_field):*):? __linebreak__
element_field ->
| element
| Id
| Class
| element_property
| value
single_line_fields ->
(first_single_line_field
(__singleline__ single_line_field):*):? __singleline__
first_single_line_field ->
| Id
| Class
| __singleline__ element_property
| __singleline__ single_line_value
single_line_field ->
| Id
| Class
| element_property
| single_line_value
element_property -> Name _ ("=" | ":") _ value
single_line_value ->
| array
| Int
| Float
| String
| bool
| Null
value ->
| object
| array
| Int
| Float
| String
| bool
| Null
object -> "{" object_properties "}"
object_properties -> (_ object_property (_ "," _ object_property):* _ ",":?):? _
object_property -> (Name | String) _ ("=" | ":") _ value
array -> "[" array_values "]"
array_values -> (_ value (_ "," _ value):* _ ",":?):? _
bool -> True | False
__singleline__ -> mandatory_same_line_whitespace
_singleline_ -> optional_same_line_whitespace
__linebreak__ -> mandatory_linebreak
__ -> mandatory_whitespace
_ -> optional_whitespace
mandatory_same_line_whitespace -> single_line_whitespace:+
optional_same_line_whitespace -> single_line_whitespace:*
mandatory_linebreak ->
single_line_whitespace:*
line_breaker
whitespace_and_line_break:*
single_line_whitespace -> SingleLineComment | SingleLineWhitespace
line_breaker -> MultiLineComment | MultiLineWhitespace | ";"
whitespace_and_line_break -> singular_whitespace | ";"
optional_whitespace -> singular_whitespace:*
mandatory_whitespace -> singular_whitespace:+
singular_whitespace ->
| SingleLineComment
| MultiLineWhitespace
| SingleLineComment
| MultiLineComment
=== tokens ===
SingleLineWhitespace -> [ \t\r\f\v]+
MultiLineWhitespace -> [ \t\n\r\f\v]+
SingleLineComment -> \/\/[^\n]+
MultiLineComment -> \/\*.*?\*\/
Name -> [a-zA-Z_][a-zA-Z0-9_]*
Id -> #[a-zA-Z0-9_]+
Class \.[a-zA-Z0-9_]+
Int -> [0-9]+
Float -> [0-9]+\.[0-9]*
String -> "<sequence of optionally escaped characters \
and escaped special character placeholders \
terminated by an unescaped '"'>
Null -> "null"
False -> "false"
True -> "true"

View File

@ -0,0 +1,18 @@
page {
text.title {
"hello world"
}
input#answer {
placeholder="answer"
}
// text { "hello world" }
/*
input#non_answer {
placeholder="non answer"
}
*/
button#submit {
"submit"
}
text { "hello world" }; text { "hello world" }
}

View File

@ -1,48 +1,376 @@
#include "bong.hpp" #include "bong.hpp"
#include "utils.hpp" #include "utils.hpp"
#include <cctype> #include <cctype>
#include <fmt/core.h>
#include <map>
#include <memory>
#include <string_view>
namespace bong { namespace bong {
auto token_type_to_string(Tokens type) noexcept -> std::string_view
{
switch (type) {
case Tokens::Eof:
return "Eof";
case Tokens::SingleLineWhitespace:
return "SingleLineWhitespace";
case Tokens::MultiLineWhitespace:
return "MultiLineWhitespace";
case Tokens::SingleLineComment:
return "SingleLineComment";
case Tokens::MultiLineComment:
return "MultiLineComment";
case Tokens::Name:
return "Name";
case Tokens::Id:
return "Id";
case Tokens::Class:
return "Class";
case Tokens::Int:
return "Int";
case Tokens::Float:
return "Float";
case Tokens::String:
return "String";
case Tokens::Null:
return "Null";
case Tokens::False:
return "False";
case Tokens::True:
return "True";
case Tokens::LBrace:
return "LBrace";
case Tokens::RBrace:
return "RBrace";
case Tokens::LBracket:
return "LBracket";
case Tokens::RBracket:
return "RBracket";
case Tokens::Equal:
return "Equal";
case Tokens::Colon:
return "Colon";
case Tokens::SemiColon:
return "SemiColon";
case Tokens::Comma:
return "Comma";
}
}
auto Token::value() const noexcept -> std::string_view
{
return text.substr(location.index, length);
}
auto char_to_escaped_string(char c) noexcept -> std::string
{
switch (c) {
case '\n':
return "\\n";
case '\t':
return "\\t";
case '\r':
return "\\r";
case '\f':
return "\\f";
case '\v':
return "\\v";
default:
return { c };
}
}
auto escape_string(std::string_view value) noexcept -> std::string
{
auto result = std::string {};
for (auto c : value)
result += char_to_escaped_string(c);
return result;
}
auto Token::to_string() const noexcept -> std::string
{
return fmt::format(
"Token {{ [{}:{}], {}:{}, \t{}, \033[01;32m\"{}\"\033[00m }}",
location.index, length, location.line, location.col,
token_type_to_string(type), escape_string(value()));
}
auto Lexer::collect() noexcept -> Result<std::vector<Token>, Error>
{
auto tokens = std::vector<Token> {};
while (true) {
auto token = peek();
if (!token)
return token.transform<std::vector<Token>>();
else if (token->type == Tokens::Eof)
break;
else
tokens.push_back(*token);
next();
}
return tokens;
}
auto Lexer::make_token() noexcept -> Result<Token, Error> auto Lexer::make_token() noexcept -> Result<Token, Error>
{ {
if (done())
return Token { Tokens::Eof, location(), 0, text };
auto c = current(); auto c = current();
if (std::isspace(c)) if (std::isspace(c) != 0)
return make_whitespace(); return make_whitespace();
else if (std::isdigit(c)) else if (std::isdigit(c) != 0)
return make_number(); return make_number();
else if (std::isalpha(c)) else if (std::isalpha(c) != 0)
return make_name(); return make_name();
else else
return make_static(); return make_static();
} }
auto Lexer::make_name() noexcept -> Result<Token, Error> auto Lexer::make_whitespace() noexcept -> Result<Token, Error>
{ {
auto begin_index = index; auto begin = location();
auto begin = location; while (!done() and std::isspace(current()) != 0 and current() != '\n') {
while (!done()
and (std ::isalpha(current()) or std::isdigit(current())
or current() == '_' or current() == '-')) {
step(); step();
} }
return Token { Tokens::Name, begin_index, index - begin_index, begin }; if (!done() and current() == '\n') {
while (!done() and std::isspace(current()) != 0) {
step();
}
return Token {
Tokens::MultiLineWhitespace,
begin,
length_from(begin),
text,
};
} else {
return Token {
Tokens::SingleLineWhitespace,
begin,
length_from(begin),
text,
};
}
} }
auto Lexer::make_number() noexcept -> Result<Token, Error>; auto substring_matches(std::string_view text, size_t index,
std::string_view literal) noexcept -> bool
{
return literal.size() == 4
and text.substr(index, literal.size()).compare(literal) == 0;
}
auto Lexer::make_static() noexcept -> Result<Token, Error>; auto Lexer::make_name() noexcept -> Result<Token, Error>
{
auto begin = location();
while (!done()
and ((std::isalpha(current()) != 0) or (std::isdigit(current()) != 0)
or current() == '_')) {
step();
}
auto type = [&] {
if (substring_matches(text, begin.index, "null"))
return Tokens::Null;
else if (substring_matches(text, begin.index, "false"))
return Tokens::False;
else if (substring_matches(text, begin.index, "true"))
return Tokens::True;
else
return Tokens::Name;
}();
return Token { type, begin, length_from(begin), text };
}
auto Lexer::make_whitespace() noexcept -> Result<Token, Error>; auto Lexer::make_number() noexcept -> Result<Token, Error>
{
auto begin = location();
while (!done() and (std::isdigit(current()) != 0)) {
step();
}
if (!done() and current() == '.') {
step();
if (done() or std::isdigit(current()) == 0) {
return Error { "expected digits after '.'", location() };
}
while (!done() and (std::isdigit(current()) != 0)) {
step();
}
return Token { Tokens::Float, begin, length_from(begin), text };
} else {
return Token { Tokens::Int, begin, length_from(begin), text };
}
}
auto Lexer::make_singleline_comment() noexcept -> Result<Token, Error>; auto Lexer::make_static() noexcept -> Result<Token, Error>
{
switch (current()) {
case '/':
return make_comment();
case '"':
return make_string();
case '#':
return make_id();
case '.':
return make_class();
case '{':
return make_single_char_token(Tokens::LBrace);
case '}':
return make_single_char_token(Tokens::RBrace);
case '[':
return make_single_char_token(Tokens::LBracket);
case ']':
return make_single_char_token(Tokens::RBracket);
case '=':
return make_single_char_token(Tokens::Equal);
case ':':
return make_single_char_token(Tokens::Colon);
case ';':
return make_single_char_token(Tokens::SemiColon);
case ',':
return make_single_char_token(Tokens::Comma);
default:
return Error { fmt::format("unexpected character '{}'", current()),
location() };
}
}
auto Lexer::make_multiline_comment() noexcept -> Result<Token, Error>; auto Lexer::make_comment() noexcept -> Result<Token, Error>
{
auto begin = location();
step();
if (current() == '/')
return make_single_line_comment(begin);
else if (current() == '*')
return make_multi_line_comment(begin);
else
return Error {
fmt::format("expected '/' or '*', got '{}'", current()),
location(),
};
}
auto Lexer::make_string() noexcept -> Result<Token, Error>; auto Lexer::make_multi_line_comment(Location begin) noexcept
-> Result<Token, Error>
{
step();
step();
while (!done() and text.at(index - 1) != '*' and current() != '/') {
step();
}
if (done()) {
return Error { "expected \"*/\", got EOF", location() };
}
step();
return Token { Tokens::MultiLineComment, begin, length_from(begin), text };
}
auto Lexer::make_id() noexcept -> Result<Token, Error>; auto Lexer::make_single_line_comment(Location begin) noexcept
-> Result<Token, Error>
{
step();
while (!done() and current() != '\n') {
step();
}
return Token { Tokens::SingleLineComment, begin, length_from(begin), text };
}
auto Lexer::make_class() noexcept -> Result<Token, Error>; auto Lexer::make_string() noexcept -> Result<Token, Error>
{
auto begin = location();
step();
auto escaped = false;
while (!done() and (escaped || current() != '"')) {
escaped = !escaped and current() == '\\';
step();
}
if (done()) {
return Error {
fmt::format("expected '\"', got Eof"),
location(),
};
} else if (current() != '\"') {
return Error {
fmt::format("expected '\"', got '{}'", current()),
location(),
};
}
step();
return Token { Tokens::String, begin, length_from(begin), text };
}
auto Lexer::make_id() noexcept -> Result<Token, Error>
{
auto begin = location();
step();
while (!done()
and ((std::isalpha(current()) != 0) or (std::isdigit(current()) != 0)
or current() == '_')) {
step();
}
return Token { Tokens::Id, begin, length_from(begin), text };
}
auto Lexer::make_class() noexcept -> Result<Token, Error>
{
auto begin = location();
step();
while (!done()
and ((std::isalpha(current()) != 0) or (std::isdigit(current()) != 0)
or current() == '_')) {
step();
}
return Token { Tokens::Class, begin, length_from(begin), text };
}
auto Lexer::make_single_char_token(Tokens type) noexcept -> Result<Token, Error>
{
auto begin = location();
step();
return Token { type, begin, length_from(begin), text };
}
auto Lexer::step() noexcept -> void
{
index++;
col++;
if (!done() and current() == '\n') {
line++;
col = 1;
}
}
auto Parser::parse_top_level() noexcept -> Result<std::unique_ptr<Node>, Error>
{
if (!lexer.peek())
return { lexer.peek().unwrap_error() };
else if (lexer.peek()->type == Tokens::Name)
return parse_element();
else
return parse_value();
}
auto Parser::parse_element() noexcept -> Result<std::unique_ptr<Node>, Error>
{
auto name = *lexer.peek();
auto ids = Element::Ids {};
auto classes = Element::Classes {};
auto properties = Element::Properties {};
auto values = Element::Values {};
if (!lexer.next())
return { lexer.peek().unwrap_error() };
return Result<std::unique_ptr<Node>, Error>::create_ok(
std::make_unique<Element>(
Element { std::string { name.value() }, {}, {}, {}, {} }));
}
auto Parser::parse_single_line_fields(
Element::Initializer& initializer) noexcept -> Result<void, Error>
{
if (auto result = parse_first_single_line_field(initializer); !result)
return result;
return {};
}
} }

View File

@ -1,14 +1,20 @@
#pragma once #pragma once
#include "src/result.hpp"
#include "utils.hpp" #include "utils.hpp"
#include <cstdint>
#include <map>
#include <optional> #include <optional>
#include <string_view> #include <string_view>
#include <utility>
#include <vector>
namespace bong { namespace bong {
enum class Tokens { enum class Tokens {
Eof, Eof,
Whitespace, SingleLineWhitespace,
MultiLineWhitespace,
SingleLineComment, SingleLineComment,
MultiLineComment, MultiLineComment,
@ -19,7 +25,9 @@ enum class Tokens {
Int, Int,
Float, Float,
String, String,
Bool, Null,
False,
True,
LBrace, LBrace,
RBrace, RBrace,
@ -32,61 +40,218 @@ enum class Tokens {
Comma, Comma,
}; };
auto token_type_to_string(Tokens type) noexcept -> std::string_view;
struct Location { struct Location {
size_t index;
int line, col; int line, col;
}; };
struct Token { struct Token {
Tokens type; Tokens type;
size_t index, length;
Location location; Location location;
size_t length;
std::string_view text;
auto value() const noexcept -> std::string_view;
auto to_string() const noexcept -> std::string;
}; };
class Lexer { class Lexer {
public: public:
struct Error { struct Error {
std::string message; std::string message;
size_t index;
Location location; Location location;
}; };
Lexer(std::string_view text) inline Lexer(std::string_view text)
: text { text } : current_token { make_token() }
, text { text }
{ {
(void)next(); next();
} }
auto next() noexcept -> Result<Token, Error> inline auto next() noexcept -> Result<Token, Error>
{ {
return current_token = make_token(); return current_token = make_token();
} }
[[nodiscard]] auto peek() const noexcept -> Result<Token, Error> [[nodiscard]] inline auto peek() const noexcept -> Result<Token, Error>
{ {
return current_token; return current_token;
} }
[[nodiscard]] auto collect() noexcept -> Result<std::vector<Token>, Error>;
private: private:
auto make_token() noexcept -> Result<Token, Error>; auto make_token() noexcept -> Result<Token, Error>;
auto make_whitespace() noexcept -> Result<Token, Error>;
auto make_name() noexcept -> Result<Token, Error>; auto make_name() noexcept -> Result<Token, Error>;
auto make_number() noexcept -> Result<Token, Error>; auto make_number() noexcept -> Result<Token, Error>;
auto make_static() noexcept -> Result<Token, Error>; auto make_static() noexcept -> Result<Token, Error>;
auto make_whitespace() noexcept -> Result<Token, Error>; auto make_comment() noexcept -> Result<Token, Error>;
auto make_singleline_comment() noexcept -> Result<Token, Error>; auto make_single_line_comment(Location begin) noexcept
auto make_multiline_comment() noexcept -> Result<Token, Error>; -> Result<Token, Error>;
auto make_multi_line_comment(Location begin) noexcept
-> Result<Token, Error>;
auto make_string() noexcept -> Result<Token, Error>; auto make_string() noexcept -> Result<Token, Error>;
auto make_id() noexcept -> Result<Token, Error>; auto make_id() noexcept -> Result<Token, Error>;
auto make_class() noexcept -> Result<Token, Error>; auto make_class() noexcept -> Result<Token, Error>;
auto make_single_char_token(Tokens type) noexcept -> Result<Token, Error>;
auto current() const noexcept -> char { return text.at(index); } inline auto current() const noexcept -> char { return text.at(index); }
auto done() const noexcept -> bool { return index >= text.size(); } inline auto done() const noexcept -> bool { return index >= text.size(); }
auto step() noexcept -> void { index++; } auto step() noexcept -> void;
Result<Token, Error> current_token { auto location() const noexcept -> Location { return { index, line, col }; }
Error { "next() not called first", index, location }, inline auto length_from(Location begin) const noexcept -> size_t
}; {
return index - begin.index;
}
inline auto length_from(size_t begin_index) const noexcept -> size_t
{
return index - begin_index;
}
Result<Token, Error> current_token;
std::string_view text; std::string_view text;
size_t index { 0 }; size_t index { 0 };
Location location { 1, 1 }; int line { 1 }, col { 1 };
};
enum class Nodes {
Element,
Object,
Array,
Int,
Float,
Bool,
String,
};
struct Node {
Node() = default;
virtual ~Node() = default;
virtual auto type() const noexcept -> Nodes;
};
struct Element final : public Node {
using Ids = std::vector<std::string>;
using Classes = std::vector<std::string>;
using Properties = std::map<std::string, std::unique_ptr<Node>>;
using Values = std::vector<std::unique_ptr<Node>>;
struct Initializer {
Ids ids;
Classes classes;
Properties properties;
Values values;
};
Element(std::string name, std::vector<std::string> ids,
std::vector<std::string> classes,
std::map<std::string, std::unique_ptr<Node>> properties,
std::vector<std::unique_ptr<Node>> values)
: name { std::move(name) }
, ids { std::move(ids) }
, classes { std::move(classes) }
, properties { std::move(properties) }
, values { std::move(values) }
{ }
std::string name;
Ids ids;
Classes classes;
Properties properties;
Values values;
auto type() const noexcept -> Nodes override { return Nodes::Element; }
};
struct Object final : public Node {
std::map<std::string, std::unique_ptr<Node>> properties;
auto type() const noexcept -> Nodes override { return Nodes::Object; }
};
struct Array final : public Node {
std::vector<std::unique_ptr<Node>> values;
auto type() const noexcept -> Nodes override { return Nodes::Array; }
};
struct Int final : public Node {
int64_t value;
auto type() const noexcept -> Nodes override { return Nodes::Int; }
};
struct Float final : public Node {
double value;
auto type() const noexcept -> Nodes override { return Nodes::Float; }
};
struct String final : public Node {
std::string value;
auto type() const noexcept -> Nodes override { return Nodes::String; }
};
struct Bool final : public Node {
bool value;
auto type() const noexcept -> Nodes override { return Nodes::Bool; }
};
class Parser {
public:
struct Error {
Error(Lexer::Error&& error)
: message { std::move(error.message) }
, location { error.location }
{ }
std::string message;
Location location;
};
Parser(Lexer lexer)
: lexer { std::move(lexer) }
{ }
auto parse_top_level() noexcept -> Result<std::unique_ptr<Node>, Error>;
auto parse_element() noexcept -> Result<std::unique_ptr<Node>, Error>;
auto parse_element_body(Element::Initializer& initializer) noexcept
-> Result<void, Error>;
auto parse_element_fields(Element::Initializer& initializer) noexcept
-> Result<void, Error>;
auto parse_element_field(Element::Initializer& initializer) noexcept
-> Result<void, Error>;
auto parse_single_line_fields(Element::Initializer& initializer) noexcept
-> Result<void, Error>;
auto parse_first_single_line_field(
Element::Initializer& initializer) noexcept -> Result<void, Error>;
auto parse_single_line_field(Element::Initializer& initializer) noexcept
-> Result<void, Error>;
auto parse_element_property() noexcept -> Result<void, Error>;
auto parse_single_line_value() noexcept -> Result<void, Error>;
auto parse_value() noexcept -> Result<std::unique_ptr<Node>, Error>;
auto parse_object() noexcept -> Result<void, Error>;
auto parse_object_properties() noexcept -> Result<void, Error>;
auto parse_object_property() noexcept -> Result<void, Error>;
auto parse_array() noexcept -> Result<void, Error>;
auto parse_array_values() noexcept -> Result<void, Error>;
auto parse_bool() noexcept -> Result<void, Error>;
auto parse_mandatory_same_line_whitespace() noexcept -> Result<void, Error>;
auto parse_optional_same_line_whitespace() noexcept -> Result<void, Error>;
auto parse_mandatory_linebreak() noexcept -> Result<void, Error>;
auto parse_single_line_whitespace() noexcept -> Result<void, Error>;
auto parse_line_breaker() noexcept -> Result<void, Error>;
auto parse_whitespace_and_line_break() noexcept -> Result<void, Error>;
auto parse_optional_whitespace() noexcept -> Result<void, Error>;
auto parse_mandatory_whitespace() noexcept -> Result<void, Error>;
auto parse_singular_whitespace() noexcept -> Result<void, Error>;
private:
Lexer lexer;
}; };
} }

View File

@ -3,10 +3,15 @@
#include "SDL_rect.h" #include "SDL_rect.h"
#include "SDL_render.h" #include "SDL_render.h"
#include "SDL_video.h" #include "SDL_video.h"
#include "src/bong.hpp"
#include "utils.hpp" #include "utils.hpp"
#include <SDL.h> #include <SDL.h>
#include <fmt/core.h> #include <fmt/core.h>
#include <fstream>
#include <iterator>
#include <memory> #include <memory>
#include <string>
#include <string_view>
class GUI { class GUI {
public: public:
@ -66,19 +71,46 @@ private:
SDL_Renderer* renderer; SDL_Renderer* renderer;
}; };
auto read_file_into_string(const std::string& filename)
{
auto file = std::ifstream { filename };
auto contents = std::string(
std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
return contents;
}
struct Data {
int a, b;
};
auto func() -> std::unique_ptr<Data>
{
return std::make_unique<Data>(Data { 5, 3 });
}
auto main() -> int auto main() -> int
{ {
// test auto text = read_file_into_string("../examples/helloworld/main.bong");
fmt::print("browser: hello world!\n"); auto tokens = bong::Lexer { text }.collect();
auto gui = GUI::create().unwrap(); if (tokens) {
while (true) { fmt::print("tokens:\n");
bool should_exit = gui->should_exit(); for (const auto& token : *tokens)
if (should_exit) fmt::print(" {}\n", token.to_string());
break; } else {
gui->set_background_color(100, 180, 220); fmt::print("lexer error: {}\n at {}:{}\n",
SDL_Rect rect = { .x = 0, .y = 0, .w = 50, .h = 50 }; tokens.unwrap_error().message, tokens.unwrap_error().location.line,
gui->create_rect(rect, 255, 0, 0); tokens.unwrap_error().location.col);
gui->update_gui();
} }
// test
// fmt::print("browser: hello world!\n");
// auto gui = GUI::create().unwrap();
// while (true) {
// bool should_exit = gui->should_exit();
// if (should_exit)
// break;
// gui->set_background_color(100, 180, 220);
// SDL_Rect rect = { .x = 0, .y = 0, .w = 50, .h = 50 };
// gui->create_rect(rect, 255, 0, 0);
// gui->update_gui();
// }
} }

View File

@ -32,7 +32,7 @@ template <typename InnerValue> struct Extracter {
using Error = void; using Error = void;
}; };
template <typename Value, typename Error> class [[nodiscard]] Result { template <typename Value, typename Error> class Result {
static_assert(std::is_object_v<Value> && std::is_destructible_v<Value>, static_assert(std::is_object_v<Value> && std::is_destructible_v<Value>,
"incompatible Value in Result<Value, Error>"); "incompatible Value in Result<Value, Error>");
static_assert(std::is_object_v<Error> && std::is_destructible_v<Error>, static_assert(std::is_object_v<Error> && std::is_destructible_v<Error>,
@ -209,28 +209,28 @@ public:
return is_ok() ? if_ok(unwrap()) : if_error(unwrap_error()); return is_ok() ? if_ok(unwrap()) : if_error(unwrap_error());
} }
[[nodiscard]] constexpr auto flatten() noexcept requires [[nodiscard]] constexpr auto flatten() noexcept
std::same_as<Error, typename Extracter<Value>::Error> requires std::same_as<Error, typename Extracter<Value>::Error>
{ {
using InnerValue = typename Extracter<Value>::Value; using InnerValue = typename Extracter<Value>::Value;
return is_ok() ? unwrap() : Result<InnerValue, Error>(unwrap_error()); return is_ok() ? unwrap() : Result<InnerValue, Error>(unwrap_error());
} }
[[nodiscard]] constexpr auto flatten() const noexcept requires [[nodiscard]] constexpr auto flatten() const noexcept
std::same_as<Error, typename Extracter<Value>::Error> requires std::same_as<Error, typename Extracter<Value>::Error>
{ {
using InnerValue = typename Extracter<Value>::Value; using InnerValue = typename Extracter<Value>::Value;
return is_ok() ? unwrap() : Result<InnerValue, Error>(unwrap_error()); return is_ok() ? unwrap() : Result<InnerValue, Error>(unwrap_error());
} }
[[nodiscard]] constexpr auto flatten_error() noexcept requires [[nodiscard]] constexpr auto flatten_error() noexcept
std::same_as<Value, typename Extracter<Error>::Value> requires std::same_as<Value, typename Extracter<Error>::Value>
{ {
using InnerError = typename Extracter<Error>::Error; using InnerError = typename Extracter<Error>::Error;
return is_error() ? unwrap_error() return is_error() ? unwrap_error()
: Result<Value, InnerError>(unwrap()); : Result<Value, InnerError>(unwrap());
} }
[[nodiscard]] constexpr auto flatten_error() const noexcept requires [[nodiscard]] constexpr auto flatten_error() const noexcept
std::same_as<Value, typename Extracter<Error>::Value> requires std::same_as<Value, typename Extracter<Error>::Value>
{ {
using InnerError = typename Extracter<Error>::Error; using InnerError = typename Extracter<Error>::Error;
return is_error() ? unwrap_error() return is_error() ? unwrap_error()
@ -267,7 +267,7 @@ struct Extracter<Result<ValueInferer, ErrorInferer>> {
using Error = ErrorInferer; using Error = ErrorInferer;
}; };
template <typename Value> class [[nodiscard]] Result<Value, void> { template <typename Value> class Result<Value, void> {
static_assert(std::is_object_v<Value> && std::is_destructible_v<Value>, static_assert(std::is_object_v<Value> && std::is_destructible_v<Value>,
"incompatible Error in Result<Value, Error>"); "incompatible Error in Result<Value, Error>");
@ -385,14 +385,14 @@ public:
return is_ok() ? if_ok(unwrap()) : if_error(); return is_ok() ? if_ok(unwrap()) : if_error();
} }
[[nodiscard]] constexpr auto flatten() noexcept requires [[nodiscard]] constexpr auto flatten() noexcept
std::same_as<void, typename Extracter<Value>::Error> requires std::same_as<void, typename Extracter<Value>::Error>
{ {
using InnerValue = typename Extracter<Value>::Value; using InnerValue = typename Extracter<Value>::Value;
return is_ok() ? unwrap() : Result<InnerValue, void>(); return is_ok() ? unwrap() : Result<InnerValue, void>();
} }
[[nodiscard]] constexpr auto flatten() const noexcept requires [[nodiscard]] constexpr auto flatten() const noexcept
std::same_as<void, typename Extracter<Value>::Error> requires std::same_as<void, typename Extracter<Value>::Error>
{ {
using InnerValue = typename Extracter<Value>::Value; using InnerValue = typename Extracter<Value>::Value;
return is_ok() ? unwrap() : Result<InnerValue, void>(); return is_ok() ? unwrap() : Result<InnerValue, void>();
@ -422,7 +422,7 @@ private:
std::optional<Value> maybe_value; std::optional<Value> maybe_value;
}; };
template <typename Error> class [[nodiscard]] Result<void, Error> { template <typename Error> class Result<void, Error> {
static_assert(std::is_object_v<Error> && std::is_destructible_v<Error>, static_assert(std::is_object_v<Error> && std::is_destructible_v<Error>,
"incompatible Error in Result<Value, Error>"); "incompatible Error in Result<Value, Error>");
@ -528,14 +528,14 @@ public:
return is_ok() ? if_ok() : if_error(unwrap_error()); return is_ok() ? if_ok() : if_error(unwrap_error());
} }
[[nodiscard]] constexpr auto flatten_error() noexcept requires [[nodiscard]] constexpr auto flatten_error() noexcept
std::same_as<void, typename Extracter<Error>::Value> requires std::same_as<void, typename Extracter<Error>::Value>
{ {
using InnerError = typename Extracter<Error>::Error; using InnerError = typename Extracter<Error>::Error;
return is_error() ? unwrap_error() : Result<void, InnerError>(); return is_error() ? unwrap_error() : Result<void, InnerError>();
} }
[[nodiscard]] constexpr auto flatten_error() const noexcept requires [[nodiscard]] constexpr auto flatten_error() const noexcept
std::same_as<void, typename Extracter<Error>::Value> requires std::same_as<void, typename Extracter<Error>::Value>
{ {
using InnerError = typename Extracter<Error>::Error; using InnerError = typename Extracter<Error>::Error;
return is_error() ? unwrap_error() : Result<void, InnerError>(); return is_error() ? unwrap_error() : Result<void, InnerError>();
@ -565,7 +565,7 @@ private:
std::optional<Error> maybe_error; std::optional<Error> maybe_error;
}; };
template <> class [[nodiscard]] Result<void, void> { template <> class Result<void, void> {
public: public:
enum class States { Ok, Error }; enum class States { Ok, Error };

View File

@ -1,3 +1,5 @@
#pragma once
#include "result.hpp" #include "result.hpp"
namespace utils { namespace utils {