rust > c++

This commit is contained in:
SimonFJ20 2023-01-18 17:56:05 +01:00
parent 578b5f7fbd
commit cfdfc42918
20 changed files with 20 additions and 1788 deletions

View File

@ -1,8 +0,0 @@
BasedOnStyle: WebKit
IndentWidth: 4
ColumnLimit: 80
IndentCaseLabels: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
SplitEmptyFunction: false

45
.clangd
View File

@ -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
View File

@ -0,0 +1,2 @@
target/

16
.vscode/launch.json vendored
View File

@ -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
View 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
View 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]

View File

@ -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`
}
```

View File

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

View File

@ -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>
{ }
}

View File

@ -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;
};
}

View File

@ -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);
}
}

View File

@ -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 };
};
}

View File

@ -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
View File

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

View File

@ -1,5 +0,0 @@
sources = files(
'main.cpp',
'bong.cpp',
)

View File

@ -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;
};
}

View File

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

View File

@ -1,3 +0,0 @@
packagecache/
fmt-*
SDL2-*

View File

@ -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

View File

@ -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