rust > c++
This commit is contained in:
parent
578b5f7fbd
commit
cfdfc42918
@ -1,8 +0,0 @@
|
||||
BasedOnStyle: WebKit
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 80
|
||||
IndentCaseLabels: true
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
SplitEmptyFunction: false
|
45
.clangd
45
.clangd
@ -1,45 +0,0 @@
|
||||
CompileFlags:
|
||||
CompilationDatabase: ./builddir
|
||||
Diagnostics:
|
||||
ClangTidy:
|
||||
Add:
|
||||
- bugprone-*
|
||||
- clang-analyzer-*
|
||||
- cppcoreguidelines-*
|
||||
- modernize-*
|
||||
- readability-*
|
||||
Remove:
|
||||
- readability-braces-around-statements
|
||||
- cppcoreguidelines-avoid-magic-numbers
|
||||
- readability-magic-numbers
|
||||
- readability-identifier-length
|
||||
- bugprone-easily-swappable-parameters
|
||||
- readability-convert-member-functions-to-static
|
||||
- bugprone-exception-escape
|
||||
- modernize-use-nodiscard
|
||||
- readability-else-after-return
|
||||
|
||||
CheckOptions:
|
||||
UnusedIncludes: Strict
|
||||
cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor: true
|
||||
|
||||
# typecase rules
|
||||
readability-identifier-naming.ClassCase: CamelCase
|
||||
readability-identifier-naming.ConstantCase: lower_case
|
||||
readability-identifier-naming.EnumCase: CamelCase
|
||||
readability-identifier-naming.EnumConstantCase: CamelCase
|
||||
readability-identifier-naming.FunctionCase: lower_case
|
||||
readability-identifier-naming.InlineNamespaceCase: lower_case
|
||||
readability-identifier-naming.MacroDefinitionCase: UPPER_CASE
|
||||
readability-identifier-naming.MemberCase: lower_case
|
||||
readability-identifier-naming.MethodCase: lower_case
|
||||
readability-identifier-naming.NamespaceCase: lower_case
|
||||
readability-identifier-naming.ScopedEnumConstantCase: CamelCase
|
||||
readability-identifier-naming.StructCase: CamelCase
|
||||
readability-identifier-naming.TemplateParameterCase: CamelCase
|
||||
readability-identifier-naming.TemplateTemplateParameterCase: CamelCase
|
||||
readability-identifier-naming.TypeAliasCase: CamelCase
|
||||
readability-identifier-naming.TypedefCase: CamelCase
|
||||
readability-identifier-naming.UnionCase: CamelCase
|
||||
readability-identifier-naming.ValueTemplateParameterCase: lower_case
|
||||
readability-identifier-naming.VariableCase: lower_case
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
target/
|
||||
|
16
.vscode/launch.json
vendored
16
.vscode/launch.json
vendored
@ -1,16 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug Browser",
|
||||
"program": "${workspaceFolder}/builddir/browser",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/builddir"
|
||||
}
|
||||
]
|
||||
}
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "web-stack-project"
|
||||
version = "0.1.0"
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "web-stack-project"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
42
README.md
42
README.md
@ -1,45 +1,3 @@
|
||||
|
||||
# web-stack-project
|
||||
|
||||
## Build project
|
||||
|
||||
- Open project in terminal
|
||||
- Run `meson setup builddir`, or `CXX=clang-<version> meson setup builddir` if clang and clang-version is different
|
||||
- Navigate into builddir, `cd builddir`
|
||||
- Compile `meson compile`
|
||||
- Run, `./browser` or `./server`
|
||||
|
||||
## Toolchain
|
||||
|
||||
## Linux
|
||||
|
||||
Install clang preferably `clang-15` or `clang-14`;
|
||||
|
||||
### Windows
|
||||
|
||||
- Install Visual Studio, with `Desktop development with C++` enabled
|
||||
- Install a newer LLVM
|
||||
|
||||
## VS Code development setup
|
||||
|
||||
### Install Linux
|
||||
- LLVM and clang, `clang-15` recommended, `clang-15` based, check with `clang --version`
|
||||
- [Meson](https://mesonbuild.com/), build system
|
||||
- [Meson VS Code extension](https://marketplace.visualstudio.com/items?itemName=mesonbuild.mesonbuild)
|
||||
- [VSCode - clangd](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd), linter
|
||||
- [VSCode - CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb), debugger
|
||||
- [VSCode - Better C++ Syntax](https://marketplace.visualstudio.com/items?itemName=jeff-hykin.better-cpp-syntax), syntax higlighting
|
||||
|
||||
|
||||
### Config
|
||||
|
||||
Add these to `settings.json` if necessary
|
||||
```json
|
||||
{
|
||||
// probably necessary
|
||||
"clangd.onConfigChanged": "restart",
|
||||
|
||||
// only necessary if default `/usr/bin/clangd` isn't the correct version
|
||||
"clangd.path": "/usr/bin/clangd-<version>", // <version> is a place holder, eg, `clangd-15`
|
||||
}
|
||||
```
|
||||
|
34
meson.build
34
meson.build
@ -1,34 +0,0 @@
|
||||
project(
|
||||
'web-stack-project',
|
||||
'cpp',
|
||||
version: '0.1',
|
||||
default_options: [
|
||||
'warning_level=3',
|
||||
'cpp_std=c++20',
|
||||
],
|
||||
)
|
||||
|
||||
fmt_dep = dependency('fmt')
|
||||
sdl2_dep = dependency('sdl2')
|
||||
|
||||
add_project_arguments(
|
||||
'-Wno-gnu-statement-expression-from-macro-expansion',
|
||||
language: 'cpp',
|
||||
)
|
||||
|
||||
sources = []
|
||||
subdir('src')
|
||||
|
||||
executable(
|
||||
'browser',
|
||||
sources,
|
||||
win_subsystem: 'console',
|
||||
include_directories: [],
|
||||
override_options: [
|
||||
'werror=true'
|
||||
],
|
||||
dependencies: [
|
||||
fmt_dep,
|
||||
sdl2_dep,
|
||||
],
|
||||
)
|
448
src/bong.cpp
448
src/bong.cpp
@ -1,448 +0,0 @@
|
||||
#include "bong.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <cctype>
|
||||
#include <fmt/core.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
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>
|
||||
{
|
||||
if (done())
|
||||
return Token { Tokens::Eof, location(), 0, text };
|
||||
auto c = current();
|
||||
if (std::isspace(c) != 0)
|
||||
return make_whitespace();
|
||||
else if (std::isdigit(c) != 0)
|
||||
return make_number();
|
||||
else if (std::isalpha(c) != 0)
|
||||
return make_name();
|
||||
else
|
||||
return make_static();
|
||||
}
|
||||
|
||||
auto Lexer::make_whitespace() noexcept -> Result<Token, Error>
|
||||
{
|
||||
auto begin = location();
|
||||
while (!done() and std::isspace(current()) != 0 and current() != '\n') {
|
||||
step();
|
||||
}
|
||||
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 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_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_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_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_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_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_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_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 Error { 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 (auto result = lexer.next(); !result)
|
||||
return Error { result.unwrap_error() };
|
||||
return Result<std::unique_ptr<Node>, Error>::create_ok(
|
||||
std::make_unique<Element>(
|
||||
Element { std::string { name.value() }, {}, {}, {}, {} }));
|
||||
}
|
||||
|
||||
auto remove_first_char(std::string_view value) noexcept -> std::string_view
|
||||
{
|
||||
return value.substr(1, value.size() - 1);
|
||||
}
|
||||
|
||||
auto Parser::parse_single_line_fields(
|
||||
Element::Initializer& initializer) noexcept -> Result<void, Error>
|
||||
{
|
||||
if (lexer.peek()->type == Tokens::Id)
|
||||
return parse_single_line_fields_starts_id(initializer);
|
||||
else if (lexer.peek()->type == Tokens::Class)
|
||||
return parse_single_line_fields_starts_class(initializer);
|
||||
else
|
||||
return parse_single_line_fields_starts_with_property_or_value(
|
||||
initializer);
|
||||
}
|
||||
|
||||
auto Parser::parse_single_line_fields_starts_id(
|
||||
Element::Initializer& initializer) noexcept -> Result<void, Error>
|
||||
{
|
||||
initializer.ids.push_back(std::string {
|
||||
remove_first_char(lexer.peek()->value()),
|
||||
});
|
||||
if (auto result = lexer.next(); !result)
|
||||
return Error { result.unwrap_error() };
|
||||
return parse_single_line_fields_tail(initializer);
|
||||
}
|
||||
|
||||
auto Parser::parse_single_line_fields_starts_class(
|
||||
Element::Initializer& initializer) noexcept -> Result<void, Error>
|
||||
{
|
||||
initializer.classes.push_back(std::string {
|
||||
remove_first_char(lexer.peek()->value()),
|
||||
});
|
||||
if (auto result = lexer.next(); !result)
|
||||
return Error { result.unwrap_error() };
|
||||
return parse_single_line_fields_tail(initializer);
|
||||
}
|
||||
|
||||
auto Parser::parse_single_line_fields_starts_with_property_or_value(
|
||||
Element::Initializer& initializer) noexcept -> Result<void, Error>
|
||||
{
|
||||
if (auto result = parse_mandatory_same_line_whitespace(); !result)
|
||||
return result;
|
||||
if (lexer.peek()->type == Tokens::Name) {
|
||||
auto key = lexer.peek();
|
||||
if (auto result = lexer.next(); !result)
|
||||
return Error { result.unwrap_error() };
|
||||
if (auto result = parse_optional_whitespace(); !result)
|
||||
return result;
|
||||
if (lexer.peek()->type != Tokens::Equal
|
||||
and lexer.peek()->type != Tokens::Colon)
|
||||
return Error {
|
||||
fmt::format(
|
||||
"expected '=' or ':', got {}", lexer.peek()->to_string()),
|
||||
lexer.peek()->location,
|
||||
};
|
||||
if (auto result = lexer.next(); !result)
|
||||
return Error { result.unwrap_error() };
|
||||
if (auto result = parse_optional_same_line_whitespace(); !result)
|
||||
return result;
|
||||
auto value = parse_single_line_value();
|
||||
if (!value)
|
||||
return value.transform<void>();
|
||||
initializer.properties.insert_or_assign(
|
||||
std::string { key->value() }, *value);
|
||||
return parse_single_line_fields_tail(initializer);
|
||||
} else {
|
||||
auto value = parse_single_line_value();
|
||||
if (!value)
|
||||
return value.transform<void>();
|
||||
initializer.values.push_back(*value);
|
||||
return parse_single_line_fields_tail(initializer);
|
||||
}
|
||||
}
|
||||
|
||||
auto Parser::parse_single_line_fields_tail(
|
||||
Element::Initializer& initializer) noexcept -> Result<void, Error>
|
||||
{ }
|
||||
|
||||
}
|
266
src/bong.hpp
266
src/bong.hpp
@ -1,266 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/result.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace bong {
|
||||
|
||||
enum class Tokens {
|
||||
Eof,
|
||||
SingleLineWhitespace,
|
||||
MultiLineWhitespace,
|
||||
SingleLineComment,
|
||||
MultiLineComment,
|
||||
|
||||
Name,
|
||||
Id,
|
||||
Class,
|
||||
|
||||
Int,
|
||||
Float,
|
||||
String,
|
||||
Null,
|
||||
False,
|
||||
True,
|
||||
|
||||
LBrace,
|
||||
RBrace,
|
||||
LBracket,
|
||||
RBracket,
|
||||
|
||||
Equal,
|
||||
Colon,
|
||||
SemiColon,
|
||||
Comma,
|
||||
};
|
||||
|
||||
auto token_type_to_string(Tokens type) noexcept -> std::string_view;
|
||||
|
||||
struct Location {
|
||||
size_t index;
|
||||
int line, col;
|
||||
};
|
||||
|
||||
struct Token {
|
||||
Tokens type;
|
||||
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 {
|
||||
public:
|
||||
struct Error {
|
||||
std::string message;
|
||||
Location location;
|
||||
};
|
||||
|
||||
inline Lexer(std::string_view text)
|
||||
: current_token { make_token() }
|
||||
, text { text }
|
||||
{
|
||||
next();
|
||||
}
|
||||
|
||||
inline auto next() noexcept -> Result<Token, Error>
|
||||
{
|
||||
return current_token = make_token();
|
||||
}
|
||||
[[nodiscard]] inline auto peek() const noexcept -> Result<Token, Error>
|
||||
{
|
||||
return current_token;
|
||||
}
|
||||
[[nodiscard]] auto collect() noexcept -> Result<std::vector<Token>, Error>;
|
||||
|
||||
private:
|
||||
auto make_token() noexcept -> Result<Token, Error>;
|
||||
auto make_whitespace() noexcept -> Result<Token, Error>;
|
||||
auto make_name() noexcept -> Result<Token, Error>;
|
||||
auto make_number() noexcept -> Result<Token, Error>;
|
||||
auto make_static() noexcept -> Result<Token, Error>;
|
||||
auto make_comment() noexcept -> Result<Token, Error>;
|
||||
auto make_single_line_comment(Location begin) noexcept
|
||||
-> Result<Token, Error>;
|
||||
auto make_multi_line_comment(Location begin) noexcept
|
||||
-> Result<Token, Error>;
|
||||
auto make_string() noexcept -> Result<Token, Error>;
|
||||
auto make_id() noexcept -> Result<Token, Error>;
|
||||
auto make_class() noexcept -> Result<Token, Error>;
|
||||
auto make_single_char_token(Tokens type) noexcept -> Result<Token, Error>;
|
||||
|
||||
inline auto current() const noexcept -> char { return text.at(index); }
|
||||
inline auto done() const noexcept -> bool { return index >= text.size(); }
|
||||
auto step() noexcept -> void;
|
||||
|
||||
auto location() const noexcept -> Location { return { index, line, col }; }
|
||||
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;
|
||||
size_t index { 0 };
|
||||
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 }
|
||||
{ }
|
||||
Error(std::string message, Location location)
|
||||
: message { std::move(message) }
|
||||
, location { 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_element_property() noexcept -> Result<void, Error>;
|
||||
auto parse_single_line_fields(Element::Initializer& initializer) noexcept
|
||||
-> Result<void, Error>;
|
||||
auto parse_single_line_fields_starts_id(
|
||||
Element::Initializer& initializer) noexcept -> Result<void, Error>;
|
||||
auto parse_single_line_fields_starts_class(
|
||||
Element::Initializer& initializer) noexcept -> Result<void, Error>;
|
||||
auto parse_single_line_fields_starts_with_property_or_value(
|
||||
Element::Initializer& initializer) noexcept -> Result<void, Error>;
|
||||
auto parse_single_line_fields_tail(
|
||||
Element::Initializer& initializer) noexcept -> Result<void, Error>;
|
||||
auto parse_single_line_value() noexcept
|
||||
-> Result<std::unique_ptr<Node>, 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;
|
||||
};
|
||||
|
||||
}
|
30
src/dom.cpp
30
src/dom.cpp
@ -1,30 +0,0 @@
|
||||
#include "dom.hpp"
|
||||
|
||||
namespace dom {
|
||||
|
||||
auto Box::size() const noexcept -> Size
|
||||
{
|
||||
auto new_size = Size {};
|
||||
for (const auto& child : children)
|
||||
new_size += child->size() + Size { 5, 5 };
|
||||
new_size += { 5, 5 };
|
||||
return new_size;
|
||||
}
|
||||
|
||||
auto Box::render(Renderer& renderer, Position position) const noexcept -> void
|
||||
{
|
||||
renderer.draw_rectangle(position, size(), color);
|
||||
auto child_position = position + Position { 5, 5 };
|
||||
for (const auto& child : children) {
|
||||
child->render(renderer, child_position);
|
||||
child_position.y += child->size().height + 5;
|
||||
}
|
||||
}
|
||||
|
||||
auto Rectangle::render(Renderer& renderer, Position position) const noexcept
|
||||
-> void
|
||||
{
|
||||
renderer.draw_rectangle(position, size(), color);
|
||||
}
|
||||
|
||||
}
|
92
src/dom.hpp
92
src/dom.hpp
@ -1,92 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace dom {
|
||||
|
||||
template <typename T> using Ref = std::shared_ptr<T>;
|
||||
|
||||
struct Position {
|
||||
auto operator+=(const Position& other) noexcept -> Position&
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
int x, y;
|
||||
};
|
||||
|
||||
auto operator+(const Position& a, const Position& b) noexcept -> Position
|
||||
{
|
||||
return { a.x + b.x, a.y + b.y };
|
||||
}
|
||||
|
||||
struct Size {
|
||||
auto operator+=(const Size& other) noexcept -> Size&
|
||||
{
|
||||
width += other.width;
|
||||
height += other.height;
|
||||
return *this;
|
||||
}
|
||||
friend auto operator+(const Size& a, const Size& b) noexcept -> Size;
|
||||
|
||||
int width, height;
|
||||
};
|
||||
|
||||
auto operator+(const Size& a, const Size& b) noexcept -> Size
|
||||
{
|
||||
return { a.width + b.width, a.height + b.height };
|
||||
}
|
||||
|
||||
struct Color {
|
||||
static const auto opaque = uint8_t { 255 };
|
||||
static const auto transparent = uint8_t { 0 };
|
||||
|
||||
uint8_t red, green, blue, alpha { opaque };
|
||||
};
|
||||
|
||||
struct Renderer {
|
||||
Renderer() = default;
|
||||
virtual ~Renderer() = default;
|
||||
virtual auto draw_rectangle(
|
||||
Position position, Size size, Color color) const noexcept -> void
|
||||
= 0;
|
||||
};
|
||||
|
||||
struct Element {
|
||||
Element() = default;
|
||||
virtual ~Element() = default;
|
||||
[[nodiscard]] virtual auto size() const noexcept -> Size = 0;
|
||||
virtual auto render(Renderer& renderer, Position position) const noexcept
|
||||
-> void
|
||||
= 0;
|
||||
};
|
||||
|
||||
struct Box final : public Element {
|
||||
[[nodiscard]] auto size() const noexcept -> Size override;
|
||||
auto render(Renderer& renderer, Position position) const noexcept
|
||||
-> void override;
|
||||
auto add_child(Ref<Element> element) noexcept
|
||||
{
|
||||
children.push_back(element);
|
||||
}
|
||||
|
||||
std::vector<Ref<Element>> children;
|
||||
Color color {};
|
||||
};
|
||||
|
||||
struct Rectangle final : public Element {
|
||||
Rectangle(Size size)
|
||||
: m_size { size }
|
||||
{ }
|
||||
[[nodiscard]] auto size() const noexcept -> Size override { return m_size; }
|
||||
auto render(Renderer& renderer, Position position) const noexcept
|
||||
-> void override;
|
||||
|
||||
Color color {};
|
||||
Size m_size { 50, 50 };
|
||||
};
|
||||
|
||||
}
|
108
src/main.cpp
108
src/main.cpp
@ -1,108 +0,0 @@
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_pixels.h"
|
||||
#include "SDL_rect.h"
|
||||
#include "SDL_render.h"
|
||||
#include "SDL_video.h"
|
||||
#include "src/bong.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <SDL.h>
|
||||
#include <fmt/core.h>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class GUI {
|
||||
public:
|
||||
GUI(const GUI&) = delete;
|
||||
auto operator=(const GUI&) -> GUI& = delete;
|
||||
GUI(GUI&&) = delete;
|
||||
auto operator=(GUI&&) -> GUI& = delete;
|
||||
~GUI()
|
||||
{
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_Quit();
|
||||
};
|
||||
friend auto std::make_unique<GUI>(SDL_Window*&, SDL_Renderer*&)
|
||||
-> std::unique_ptr<GUI>;
|
||||
static auto create() noexcept -> Result<std::unique_ptr<GUI>, void>
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0)
|
||||
return {};
|
||||
SDL_Window* window = nullptr;
|
||||
SDL_Renderer* renderer = nullptr;
|
||||
if (SDL_CreateWindowAndRenderer(1280, 720, 0, &window, &renderer) != 0)
|
||||
return {};
|
||||
return std::make_unique<GUI>(window, renderer);
|
||||
}
|
||||
auto should_exit() noexcept -> bool
|
||||
{
|
||||
SDL_Event event;
|
||||
bool should_exit = false;
|
||||
while (SDL_PollEvent(&event) != 0) {
|
||||
if (event.type == SDL_QUIT)
|
||||
should_exit = true;
|
||||
}
|
||||
return should_exit;
|
||||
}
|
||||
auto set_background_color(uint8_t r, uint8_t g, uint8_t b) noexcept -> void
|
||||
{
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, SDL_ALPHA_OPAQUE);
|
||||
SDL_RenderClear(renderer);
|
||||
}
|
||||
|
||||
auto create_rect(SDL_Rect rect, uint8_t r, uint8_t g, uint8_t b) noexcept
|
||||
-> void
|
||||
{
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, SDL_ALPHA_OPAQUE);
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
}
|
||||
|
||||
auto update_gui() noexcept -> void { SDL_RenderPresent(this->renderer); }
|
||||
|
||||
private:
|
||||
GUI(SDL_Window* window, SDL_Renderer* renderer)
|
||||
: window { window }
|
||||
, renderer { renderer }
|
||||
{ }
|
||||
SDL_Window* window;
|
||||
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;
|
||||
}
|
||||
|
||||
auto main() -> int
|
||||
{
|
||||
|
||||
auto text = read_file_into_string("../examples/helloworld/main.bong");
|
||||
auto tokens = bong::Lexer { text }.collect();
|
||||
if (tokens) {
|
||||
fmt::print("tokens:\n");
|
||||
for (const auto& token : *tokens)
|
||||
fmt::print(" {}\n", token.to_string());
|
||||
} else {
|
||||
fmt::print("lexer error: {}\n at {}:{}\n",
|
||||
tokens.unwrap_error().message, tokens.unwrap_error().location.line,
|
||||
tokens.unwrap_error().location.col);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
3
src/main.rs
Normal file
3
src/main.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
|
||||
sources = files(
|
||||
'main.cpp',
|
||||
'bong.cpp',
|
||||
)
|
658
src/result.hpp
658
src/result.hpp
@ -1,658 +0,0 @@
|
||||
#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 ErrorV> struct Error {
|
||||
ErrorV value;
|
||||
};
|
||||
|
||||
template <typename InnerValue> struct Extracter {
|
||||
using Value = void;
|
||||
using Error = void;
|
||||
};
|
||||
|
||||
template <typename Value, typename Error> class 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();
|
||||
}
|
||||
|
||||
// Transforms `Result<T, E>` into `Result<Y, E>`.
|
||||
// Requries result to be an error.
|
||||
template <typename NewValue>
|
||||
[[nodiscard]] constexpr auto transform() const noexcept
|
||||
-> Result<NewValue, Error>
|
||||
{
|
||||
return Result<NewValue, Error>::create_error(unwrap_error());
|
||||
}
|
||||
|
||||
[[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 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 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 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;
|
||||
};
|
||||
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "result.hpp"
|
||||
|
||||
namespace utils {
|
||||
using result::Result;
|
||||
}
|
||||
|
||||
using utils::Result;
|
3
subprojects/.gitignore
vendored
3
subprojects/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
packagecache/
|
||||
fmt-*
|
||||
SDL2-*
|
@ -1,12 +0,0 @@
|
||||
[wrap-file]
|
||||
directory = fmt-9.1.0
|
||||
source_url = https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz
|
||||
source_filename = fmt-9.1.0.tar.gz
|
||||
source_hash = 5dea48d1fcddc3ec571ce2058e13910a0d4a6bab4cc09a809d8b1dd1c88ae6f2
|
||||
patch_filename = fmt_9.1.0-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_9.1.0-1/get_patch
|
||||
patch_hash = 4557b9ba87b3eb63694ed9b21d1a2117d4a97ca56b91085b10288e9a5294adf8
|
||||
wrapdb_version = 9.1.0-1
|
||||
|
||||
[provide]
|
||||
fmt = fmt_dep
|
@ -1,12 +0,0 @@
|
||||
[wrap-file]
|
||||
directory = SDL2-2.26.0
|
||||
source_url = https://github.com/libsdl-org/SDL/releases/download/release-2.26.0/SDL2-2.26.0.tar.gz
|
||||
source_filename = SDL2-2.26.0.tar.gz
|
||||
source_hash = 8000d7169febce93c84b6bdf376631f8179132fd69f7015d4dadb8b9c2bdb295
|
||||
patch_filename = sdl2_2.26.0-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.26.0-1/get_patch
|
||||
patch_hash = 6fcfd727d71cf7837332723518d5e47ffd64f1e7630681cf4b50e99f2bf7676f
|
||||
wrapdb_version = 2.26.0-1
|
||||
|
||||
[provide]
|
||||
sdl2 = sdl2_dep
|
Loading…
Reference in New Issue
Block a user