Moved from Result<void> to std::optional<Error>
This commit is contained in:
parent
d9c0468729
commit
038de0fc27
@ -2,7 +2,6 @@
|
||||
#define Musique_Header_HH
|
||||
|
||||
#include <chrono>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
@ -390,17 +389,85 @@ struct [[nodiscard("This value may contain critical error, so it should NOT be i
|
||||
}
|
||||
};
|
||||
|
||||
/// Abstraction over any value that are either value or error
|
||||
///
|
||||
/// Inspired by P2561R0
|
||||
template<typename = void>
|
||||
struct Try_Traits
|
||||
{
|
||||
template<typename T>
|
||||
static constexpr bool is_ok(T const& v) { return Try_Traits<T>::is_ok(v); }
|
||||
|
||||
template<typename T>
|
||||
static constexpr auto yield_value(T&& v) { return Try_Traits<T>::yield_value(std::forward<T>(v)); }
|
||||
|
||||
template<typename T>
|
||||
static constexpr auto yield_error(T&& v) { return Try_Traits<T>::yield_error(std::forward<T>(v)); }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Try_Traits<std::optional<Error>>
|
||||
{
|
||||
using Value_Type = std::nullopt_t;
|
||||
using Error_Type = Error;
|
||||
|
||||
static constexpr bool is_ok(std::optional<Error> const& o)
|
||||
{
|
||||
return not o.has_value();
|
||||
}
|
||||
|
||||
static std::nullopt_t yield_value(std::optional<Error>&& err)
|
||||
{
|
||||
assert(not err.has_value(), "Trying to yield value from optional that contains error");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static Error yield_error(std::optional<Error>&& err)
|
||||
{
|
||||
assert(err.has_value(), "Trying to yield value from optional that NOT constains error");
|
||||
return std::move(*err);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Try_Traits<Result<T>>
|
||||
{
|
||||
using Value_Type = T;
|
||||
using Error_Type = Error;
|
||||
|
||||
static constexpr bool is_ok(Result<T> const& o)
|
||||
{
|
||||
return o.has_value();
|
||||
}
|
||||
|
||||
static auto yield_value(Result<T> val)
|
||||
{
|
||||
assert(val.has_value(), "Trying to yield value from expected that contains error");
|
||||
if constexpr (std::is_void_v<T>) {
|
||||
} else {
|
||||
return std::move(*val);
|
||||
}
|
||||
}
|
||||
|
||||
static Error yield_error(Result<T>&& val)
|
||||
{
|
||||
assert(not val.has_value(), "Trying to yield error from expected with value");
|
||||
return std::move(val.error());
|
||||
}
|
||||
};
|
||||
|
||||
/// Shorthand for forwarding error values with Result type family.
|
||||
///
|
||||
/// This implementation requires C++ language extension: statement expressions
|
||||
/// It's supported by GCC and Clang, other compilers i don't know.
|
||||
/// Inspired by SerenityOS TRY macro
|
||||
#define Try(Value) \
|
||||
({ \
|
||||
auto try_value = (Value); \
|
||||
if (not try_value.has_value()) [[unlikely]] \
|
||||
return tl::unexpected(try_value.error()); \
|
||||
std::move(try_value).value(); \
|
||||
#define Try(Value) \
|
||||
({ \
|
||||
auto try_value = (Value); \
|
||||
using Trait [[maybe_unused]] = Try_Traits<std::decay_t<decltype(try_value)>>; \
|
||||
if (not Trait::is_ok(try_value)) [[unlikely]] \
|
||||
return Trait::yield_error(std::move(try_value)); \
|
||||
Trait::yield_value(std::move(try_value)); \
|
||||
})
|
||||
|
||||
/// Drop in replacement for bool when C++ implcit conversions stand in your way
|
||||
@ -1035,7 +1102,7 @@ struct Interpreter
|
||||
/// There is always at least one context
|
||||
std::vector<Context> context_stack;
|
||||
|
||||
std::function<Result<void>(Interpreter&, Value)> default_action;
|
||||
std::function<std::optional<Error>(Interpreter&, Value)> default_action;
|
||||
|
||||
struct Incoming_Midi_Callbacks;
|
||||
std::unique_ptr<Incoming_Midi_Callbacks> callbacks;
|
||||
@ -1056,7 +1123,7 @@ struct Interpreter
|
||||
void leave_scope();
|
||||
|
||||
/// Play note resolving any missing parameters with context via `midi_connection` member.
|
||||
Result<void> play(Chord);
|
||||
std::optional<Error> play(Chord);
|
||||
|
||||
/// Add to global interpreter scope all builtin function definitions
|
||||
///
|
||||
@ -1150,7 +1217,7 @@ struct Value_Formatter
|
||||
|
||||
Value_Formatter nest(Context nested = Free) const;
|
||||
|
||||
Result<void> format(std::ostream& os, Interpreter &interpreter, Value const& value);
|
||||
std::optional<Error> format(std::ostream& os, Interpreter &interpreter, Value const& value);
|
||||
};
|
||||
|
||||
Result<std::string> format(Interpreter &i, Value const& value);
|
||||
|
@ -27,14 +27,14 @@ struct Guard
|
||||
return Error { std::move(error) };
|
||||
}
|
||||
|
||||
inline Result<void> yield_result() const
|
||||
inline std::optional<Error> yield_result() const
|
||||
{
|
||||
return yield_error();
|
||||
}
|
||||
|
||||
inline Result<void> operator()(bool(*predicate)(Value::Type), Value const& v) const
|
||||
inline std::optional<Error> operator()(bool(*predicate)(Value::Type), Value const& v) const
|
||||
{
|
||||
return predicate(v.type) ? Result<void>{} : yield_result();
|
||||
return predicate(v.type) ? std::optional<Error>{} : yield_result();
|
||||
}
|
||||
};
|
||||
|
||||
@ -98,7 +98,7 @@ struct Interpreter::Incoming_Midi_Callbacks
|
||||
};
|
||||
|
||||
enum class Midi_Connection_Type { Output, Input };
|
||||
Result<void> ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name);
|
||||
std::optional<Error> ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name);
|
||||
|
||||
constexpr std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
|
||||
return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
|
||||
|
@ -35,7 +35,7 @@ concept Iterable = (With_Index_Method<T> || With_Index_Operator<T>) && requires
|
||||
|
||||
/// Create chord out of given notes
|
||||
template<Iterable T>
|
||||
static inline Result<void> create_chord(std::vector<Note> &chord, Interpreter &interpreter, T args)
|
||||
static inline std::optional<Error> create_chord(std::vector<Note> &chord, Interpreter &interpreter, T args)
|
||||
{
|
||||
for (auto i = 0u; i < args.size(); ++i) {
|
||||
Value arg;
|
||||
@ -229,7 +229,7 @@ static auto builtin_program_change(Interpreter &i, std::vector<Value> args) -> R
|
||||
/// Plays sequentialy notes walking into arrays and evaluation blocks
|
||||
///
|
||||
/// @invariant default_action is play one
|
||||
static inline Result<void> sequential_play(Interpreter &i, Value v)
|
||||
static inline std::optional<Error> sequential_play(Interpreter &i, Value v)
|
||||
{
|
||||
switch (v.type) {
|
||||
break; case Value::Type::Array:
|
||||
@ -250,7 +250,7 @@ static inline Result<void> sequential_play(Interpreter &i, Value v)
|
||||
}
|
||||
|
||||
/// Play what's given
|
||||
static Result<void> action_play(Interpreter &i, Value v)
|
||||
static std::optional<Error> action_play(Interpreter &i, Value v)
|
||||
{
|
||||
Try(sequential_play(i, std::move(v)));
|
||||
return {};
|
||||
@ -335,7 +335,7 @@ static Result<Value> builtin_sim(Interpreter &interpreter, std::vector<Value> ar
|
||||
struct {
|
||||
Interpreter &interpreter;
|
||||
|
||||
Result<void> operator()(std::vector<Chord> &track, Value &arg)
|
||||
std::optional<Error> operator()(std::vector<Chord> &track, Value &arg)
|
||||
{
|
||||
if (arg.type == Value::Type::Music) {
|
||||
track.push_back(std::move(arg).chord);
|
||||
@ -351,13 +351,13 @@ static Result<Value> builtin_sim(Interpreter &interpreter, std::vector<Value> ar
|
||||
}
|
||||
|
||||
// Invalid type for sim function
|
||||
return errors::Unsupported_Types_For {
|
||||
return Error{errors::Unsupported_Types_For {
|
||||
.type = errors::Unsupported_Types_For::Function,
|
||||
.name = "sim",
|
||||
.possibilities = {
|
||||
"(music | array of music)+"
|
||||
},
|
||||
};
|
||||
}};
|
||||
}
|
||||
} append { interpreter };
|
||||
|
||||
|
@ -217,7 +217,7 @@ void Interpreter::leave_scope()
|
||||
env = env->leave();
|
||||
}
|
||||
|
||||
Result<void> Interpreter::play(Chord chord)
|
||||
std::optional<Error> Interpreter::play(Chord chord)
|
||||
{
|
||||
Try(ensure_midi_connection_available(*this, Midi_Connection_Type::Output, "play"));
|
||||
auto &ctx = context_stack.back();
|
||||
@ -256,7 +256,7 @@ Result<void> Interpreter::play(Chord chord)
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> ensure_midi_connection_available(Interpreter &i, Midi_Connection_Type m, std::string_view operation_name)
|
||||
std::optional<Error> ensure_midi_connection_available(Interpreter &i, Midi_Connection_Type m, std::string_view operation_name)
|
||||
{
|
||||
switch (m) {
|
||||
break; case Midi_Connection_Type::Output:
|
||||
|
@ -153,7 +153,7 @@ struct Runner
|
||||
}
|
||||
|
||||
/// Run given source
|
||||
Result<void> run(std::string_view source, std::string_view filename, bool output = false)
|
||||
std::optional<Error> run(std::string_view source, std::string_view filename, bool output = false)
|
||||
{
|
||||
auto ast = Try(Parser::parse(source, filename, repl_line_number));
|
||||
|
||||
@ -326,9 +326,10 @@ static Result<void> Main(std::span<char const*> args)
|
||||
|
||||
Lines::the.add_line("<repl>", raw, repl_line_number);
|
||||
auto result = runner.run(raw, "<repl>", true);
|
||||
if (not result.has_value()) {
|
||||
using Traits = Try_Traits<std::decay_t<decltype(result)>>;
|
||||
if (not Traits::is_ok(result)) {
|
||||
std::cout << std::flush;
|
||||
std::cerr << result.error() << std::flush;
|
||||
std::cerr << Traits::yield_error(std::move(result)) << std::flush;
|
||||
}
|
||||
repl_line_number++;
|
||||
// We don't free input line since there could be values that still relay on it
|
||||
|
Loading…
Reference in New Issue
Block a user