diff --git a/include/musique.hh b/include/musique.hh index 97499f8..032caa7 100644 --- a/include/musique.hh +++ b/include/musique.hh @@ -2,7 +2,6 @@ #define Musique_Header_HH #include -#include #include #include #include @@ -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 +struct Try_Traits +{ + template + static constexpr bool is_ok(T const& v) { return Try_Traits::is_ok(v); } + + template + static constexpr auto yield_value(T&& v) { return Try_Traits::yield_value(std::forward(v)); } + + template + static constexpr auto yield_error(T&& v) { return Try_Traits::yield_error(std::forward(v)); } +}; + +template<> +struct Try_Traits> +{ + using Value_Type = std::nullopt_t; + using Error_Type = Error; + + static constexpr bool is_ok(std::optional const& o) + { + return not o.has_value(); + } + + static std::nullopt_t yield_value(std::optional&& err) + { + assert(not err.has_value(), "Trying to yield value from optional that contains error"); + return std::nullopt; + } + + static Error yield_error(std::optional&& err) + { + assert(err.has_value(), "Trying to yield value from optional that NOT constains error"); + return std::move(*err); + } +}; + +template +struct Try_Traits> +{ + using Value_Type = T; + using Error_Type = Error; + + static constexpr bool is_ok(Result const& o) + { + return o.has_value(); + } + + static auto yield_value(Result val) + { + assert(val.has_value(), "Trying to yield value from expected that contains error"); + if constexpr (std::is_void_v) { + } else { + return std::move(*val); + } + } + + static Error yield_error(Result&& 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>; \ + 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_stack; - std::function(Interpreter&, Value)> default_action; + std::function(Interpreter&, Value)> default_action; struct Incoming_Midi_Callbacks; std::unique_ptr callbacks; @@ -1056,7 +1123,7 @@ struct Interpreter void leave_scope(); /// Play note resolving any missing parameters with context via `midi_connection` member. - Result play(Chord); + std::optional 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 format(std::ostream& os, Interpreter &interpreter, Value const& value); + std::optional format(std::ostream& os, Interpreter &interpreter, Value const& value); }; Result format(Interpreter &i, Value const& value); diff --git a/include/musique_internal.hh b/include/musique_internal.hh index b806408..da64188 100644 --- a/include/musique_internal.hh +++ b/include/musique_internal.hh @@ -27,14 +27,14 @@ struct Guard return Error { std::move(error) }; } - inline Result yield_result() const + inline std::optional yield_result() const { return yield_error(); } - inline Result operator()(bool(*predicate)(Value::Type), Value const& v) const + inline std::optional operator()(bool(*predicate)(Value::Type), Value const& v) const { - return predicate(v.type) ? Result{} : yield_result(); + return predicate(v.type) ? std::optional{} : yield_result(); } }; @@ -98,7 +98,7 @@ struct Interpreter::Incoming_Midi_Callbacks }; enum class Midi_Connection_Type { Output, Input }; -Result ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name); +std::optional 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); diff --git a/src/builtin_functions.cc b/src/builtin_functions.cc index 13f9a2f..c575f40 100644 --- a/src/builtin_functions.cc +++ b/src/builtin_functions.cc @@ -35,7 +35,7 @@ concept Iterable = (With_Index_Method || With_Index_Operator) && requires /// Create chord out of given notes template -static inline Result create_chord(std::vector &chord, Interpreter &interpreter, T args) +static inline std::optional create_chord(std::vector &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 args) -> R /// Plays sequentialy notes walking into arrays and evaluation blocks /// /// @invariant default_action is play one -static inline Result sequential_play(Interpreter &i, Value v) +static inline std::optional sequential_play(Interpreter &i, Value v) { switch (v.type) { break; case Value::Type::Array: @@ -250,7 +250,7 @@ static inline Result sequential_play(Interpreter &i, Value v) } /// Play what's given -static Result action_play(Interpreter &i, Value v) +static std::optional action_play(Interpreter &i, Value v) { Try(sequential_play(i, std::move(v))); return {}; @@ -335,7 +335,7 @@ static Result builtin_sim(Interpreter &interpreter, std::vector ar struct { Interpreter &interpreter; - Result operator()(std::vector &track, Value &arg) + std::optional operator()(std::vector &track, Value &arg) { if (arg.type == Value::Type::Music) { track.push_back(std::move(arg).chord); @@ -351,13 +351,13 @@ static Result builtin_sim(Interpreter &interpreter, std::vector 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 }; diff --git a/src/interpreter.cc b/src/interpreter.cc index 10b49b4..85278d9 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -217,7 +217,7 @@ void Interpreter::leave_scope() env = env->leave(); } -Result Interpreter::play(Chord chord) +std::optional 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 Interpreter::play(Chord chord) return {}; } -Result ensure_midi_connection_available(Interpreter &i, Midi_Connection_Type m, std::string_view operation_name) +std::optional ensure_midi_connection_available(Interpreter &i, Midi_Connection_Type m, std::string_view operation_name) { switch (m) { break; case Midi_Connection_Type::Output: diff --git a/src/main.cc b/src/main.cc index 0fdb772..6798bb3 100644 --- a/src/main.cc +++ b/src/main.cc @@ -153,7 +153,7 @@ struct Runner } /// Run given source - Result run(std::string_view source, std::string_view filename, bool output = false) + std::optional 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 Main(std::span args) Lines::the.add_line("", raw, repl_line_number); auto result = runner.run(raw, "", true); - if (not result.has_value()) { + using Traits = Try_Traits>; + 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