Refactored operators, loosing binary requirements due to them
Due to addition of turning binary operators into functions, they can now be called with more or less then 2 arguments, which would trigger assertions. Not all were converted to support new call syntax though
This commit is contained in:
parent
fefde2f12e
commit
cb13dc9591
@ -318,6 +318,15 @@ struct [[nodiscard("This value may contain critical error, so it should NOT be i
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Arg>
|
||||||
|
requires requires (Arg a) {
|
||||||
|
{ Error { .details = std::move(a) } };
|
||||||
|
}
|
||||||
|
inline Result(Arg a)
|
||||||
|
: Storage(tl::unexpected(Error { .details = std::move(a) }))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
// Internal function used for definition of Try macro
|
// Internal function used for definition of Try macro
|
||||||
inline auto value() &&
|
inline auto value() &&
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
#define Musique_Internal_HH
|
#define Musique_Internal_HH
|
||||||
|
|
||||||
#include <musique.hh>
|
#include <musique.hh>
|
||||||
|
#include <optional>
|
||||||
|
#include <numeric>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
/// Allows creation of guards that ensure proper type
|
/// Allows creation of guards that ensure proper type
|
||||||
template<usize N>
|
template<usize N>
|
||||||
@ -100,4 +103,45 @@ Result<void> ensure_midi_connection_available(Interpreter&, Midi_Connection_Type
|
|||||||
constexpr std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
|
constexpr std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
|
||||||
return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
|
return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Result<Value> wrap_value(Result<T> &&value)
|
||||||
|
{
|
||||||
|
return std::move(value).map([](auto &&value) { return Value::from(std::move(value)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
Value wrap_value(auto &&value)
|
||||||
|
{
|
||||||
|
return Value::from(std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic algorithms support
|
||||||
|
namespace algo
|
||||||
|
{
|
||||||
|
/// Check if predicate is true for all successive pairs of elements
|
||||||
|
constexpr bool pairwise_all(
|
||||||
|
std::ranges::forward_range auto &&range,
|
||||||
|
auto &&binary_predicate)
|
||||||
|
{
|
||||||
|
auto it = std::begin(range);
|
||||||
|
auto const end_it = std::end(range);
|
||||||
|
for (auto next_it = std::next(it); it != end_it && next_it != end_it; ++it, ++next_it) {
|
||||||
|
if (not binary_predicate(*it, *next_it)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fold that stops iteration on error value via Result type
|
||||||
|
template<typename T>
|
||||||
|
constexpr Result<T> fold(auto&& range, T init, auto &&reducer)
|
||||||
|
{
|
||||||
|
for (auto &&value : range) {
|
||||||
|
init = Try(reducer(std::move(init), value));
|
||||||
|
}
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,15 +2,10 @@
|
|||||||
#include <musique_internal.hh>
|
#include <musique_internal.hh>
|
||||||
|
|
||||||
/// Intrinsic implementation primitive to ease operation vectorization
|
/// Intrinsic implementation primitive to ease operation vectorization
|
||||||
/// @invariant args.size() == 2
|
Result<Value> vectorize(auto &&operation, Interpreter &interpreter, Value lhs, Value rhs)
|
||||||
Result<Value> vectorize(auto &&operation, Interpreter &interpreter, std::vector<Value> args)
|
|
||||||
{
|
{
|
||||||
assert(args.size() == 2, "Vectorization primitive only supports two arguments");
|
|
||||||
Array array;
|
Array array;
|
||||||
|
|
||||||
auto lhs = std::move(args.front());
|
|
||||||
auto rhs = std::move(args.back());
|
|
||||||
|
|
||||||
if (is_indexable(lhs.type) && !is_indexable(rhs.type)) {
|
if (is_indexable(lhs.type) && !is_indexable(rhs.type)) {
|
||||||
Array array;
|
Array array;
|
||||||
for (auto i = 0u; i < lhs.size(); ++i) {
|
for (auto i = 0u; i < lhs.size(); ++i) {
|
||||||
@ -27,6 +22,26 @@ Result<Value> vectorize(auto &&operation, Interpreter &interpreter, std::vector<
|
|||||||
return Value::from(std::move(array));
|
return Value::from(std::move(array));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Intrinsic implementation primitive to ease operation vectorization
|
||||||
|
/// @invariant args.size() == 2
|
||||||
|
Result<Value> vectorize(auto &&operation, Interpreter &interpreter, std::vector<Value> args)
|
||||||
|
{
|
||||||
|
assert(args.size() == 2, "Vectorization primitive only supports two arguments");
|
||||||
|
return vectorize(std::move(operation), interpreter, std::move(args.front()), std::move(args.back()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::optional<Value> symetric(Value::Type t1, Value::Type t2, Value &lhs, Value &rhs, auto binary_predicate)
|
||||||
|
{
|
||||||
|
if (lhs.type == t1 && rhs.type == t2) {
|
||||||
|
return binary_predicate(std::move(lhs), std::move(rhs));
|
||||||
|
} else if (lhs.type == t2 && rhs.type == t1) {
|
||||||
|
return binary_predicate(std::move(rhs), std::move(lhs));
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates implementation of plus/minus operator that support following operations:
|
/// Creates implementation of plus/minus operator that support following operations:
|
||||||
/// number, number -> number (standard math operations)
|
/// number, number -> number (standard math operations)
|
||||||
/// n: number, m: music -> music
|
/// n: number, m: music -> music
|
||||||
@ -95,40 +110,39 @@ template<typename Binary_Operation, char ...Chars>
|
|||||||
static Result<Value> binary_operator(Interpreter& interpreter, std::vector<Value> args)
|
static Result<Value> binary_operator(Interpreter& interpreter, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
static constexpr char Name[] = { Chars..., '\0' };
|
static constexpr char Name[] = { Chars..., '\0' };
|
||||||
|
if (args.empty()) {
|
||||||
using NN = Shape<Value::Type::Number, Value::Type::Number>;
|
return Value::from(Number(1));
|
||||||
|
|
||||||
if (NN::typecheck(args)) {
|
|
||||||
auto [lhs, rhs] = NN::move_from(args);
|
|
||||||
if constexpr (is_template_v<Result, decltype(Binary_Operation{}(lhs, rhs))>) {
|
|
||||||
return Value::from(Try(Binary_Operation{}(lhs, rhs)));
|
|
||||||
} else {
|
|
||||||
return Value::from(Binary_Operation{}(lhs, rhs));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
auto init = std::move(args.front());
|
||||||
if (may_be_vectorized(args)) {
|
return algo::fold(std::span(args).subspan(1), std::move(init),
|
||||||
return vectorize(binary_operator<Binary_Operation, Chars...>, interpreter, args);
|
[&interpreter](Value lhs, Value &rhs) -> Result<Value> {
|
||||||
}
|
if (lhs.type == Value::Type::Number && rhs.type == Value::Type::Number) {
|
||||||
|
return wrap_value(Binary_Operation{}(std::move(lhs).n, std::move(rhs).n));
|
||||||
return Error {
|
|
||||||
.details = errors::Unsupported_Types_For {
|
|
||||||
.type = errors::Unsupported_Types_For::Operator,
|
|
||||||
.name = Name,
|
|
||||||
.possibilities = {
|
|
||||||
"(number, number) -> number",
|
|
||||||
"(array, number) -> array",
|
|
||||||
"(number, array) -> array"
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
.location = {}, // TODO fill location
|
if (is_indexable(lhs.type) != is_indexable(rhs.type)) {
|
||||||
};
|
return vectorize(binary_operator<Binary_Operation, Chars...>, interpreter, std::move(lhs), std::move(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors::Unsupported_Types_For {
|
||||||
|
.type = errors::Unsupported_Types_For::Operator,
|
||||||
|
.name = Name,
|
||||||
|
.possibilities = {
|
||||||
|
"(number, number) -> number",
|
||||||
|
"(array, number) -> array",
|
||||||
|
"(number, array) -> array"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Binary_Predicate>
|
template<typename Binary_Predicate>
|
||||||
static Result<Value> equality_operator(Interpreter &interpreter, std::vector<Value> args)
|
static Result<Value> comparison_operator(Interpreter &interpreter, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
assert(args.size() == 2, "(in)Equality only allows for 2 operands"); // TODO(assert)
|
if (args.size() != 2) {
|
||||||
|
return Value::from(algo::pairwise_all(std::move(args), Binary_Predicate{}));
|
||||||
|
}
|
||||||
|
|
||||||
if (is_indexable(args[1].type) && !is_indexable(args[0].type)) {
|
if (is_indexable(args[1].type) && !is_indexable(args[0].type)) {
|
||||||
std::swap(args[1], args[0]);
|
std::swap(args[1], args[0]);
|
||||||
@ -155,43 +169,39 @@ static Result<Value> equality_operator(Interpreter &interpreter, std::vector<Val
|
|||||||
return Value::from(Binary_Predicate{}(std::move(args.front()), std::move(args.back())));
|
return Value::from(Binary_Predicate{}(std::move(args.front()), std::move(args.back())));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Binary_Predicate>
|
|
||||||
static Result<Value> comparison_operator(Interpreter&, std::vector<Value> args)
|
|
||||||
{
|
|
||||||
assert(args.size() == 2, "Operator handler cannot accept any shape different then 2 arguments");
|
|
||||||
return Value::from(Binary_Predicate{}(args.front(), args.back()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result<Value> multiplication_operator(Interpreter &i, std::vector<Value> args)
|
static Result<Value> multiplication_operator(Interpreter &i, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
using MN = Shape<Value::Type::Music, Value::Type::Number>;
|
if (args.empty()) {
|
||||||
using NM = Shape<Value::Type::Number, Value::Type::Music>;
|
return Value::from(Number(1));
|
||||||
|
|
||||||
Number repeat; Chord what; bool typechecked = false;
|
|
||||||
|
|
||||||
// TODO add automatic symetry resolution for cases like this
|
|
||||||
if (NM::typecheck(args)) { typechecked = true; std::tie(repeat, what) = NM::move_from(args); }
|
|
||||||
else if (MN::typecheck(args)) { typechecked = true; std::tie(what, repeat) = MN::move_from(args); }
|
|
||||||
|
|
||||||
if (typechecked) {
|
|
||||||
return Value::from(Array {
|
|
||||||
.elements = std::vector<Value>(
|
|
||||||
repeat.floor().as_int(), Value::from(std::move(what))
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If binary_operator returns an error that lists all possible overloads
|
auto init = std::move(args.front());
|
||||||
// of this operator we must inject overloads that we provided above
|
return algo::fold(std::span(args).subspan(1), std::move(init), [&i](Value lhs, Value &rhs) -> Result<Value> {
|
||||||
auto result = binary_operator<std::multiplies<>, '*'>(i, std::move(args));
|
{
|
||||||
if (!result.has_value()) {
|
auto result = symetric(Value::Type::Number, Value::Type::Music, lhs, rhs, [](Value lhs, Value rhs) {
|
||||||
auto &details = result.error().details;
|
return Value::from(Array {
|
||||||
if (auto p = std::get_if<errors::Unsupported_Types_For>(&details)) {
|
.elements = std::vector<Value>(lhs.n.floor().as_int(), std::move(rhs))
|
||||||
p->possibilities.push_back("(repeat: number, what: music) -> array of music");
|
});
|
||||||
p->possibilities.push_back("(what: music, repeat: number) -> array of music");
|
});
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
return *std::move(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result;
|
// If binary_operator returns an error that lists all possible overloads
|
||||||
|
// of this operator we must inject overloads that we provided above
|
||||||
|
auto result = binary_operator<std::multiplies<>, '*'>(i, { std::move(lhs), std::move(rhs) });
|
||||||
|
if (!result.has_value()) {
|
||||||
|
auto &details = result.error().details;
|
||||||
|
if (auto p = std::get_if<errors::Unsupported_Types_For>(&details)) {
|
||||||
|
p->possibilities.push_back("(repeat: number, what: music) -> array of music");
|
||||||
|
p->possibilities.push_back("(what: music, repeat: number) -> array of music");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
using Operator_Entry = std::tuple<char const*, Intrinsic>;
|
using Operator_Entry = std::tuple<char const*, Intrinsic>;
|
||||||
@ -213,14 +223,13 @@ static constexpr auto Operators = std::array {
|
|||||||
Operator_Entry { "%", binary_operator<std::modulus<>, '%'> },
|
Operator_Entry { "%", binary_operator<std::modulus<>, '%'> },
|
||||||
Operator_Entry { "**", binary_operator<pow_operator, '*', '*'> },
|
Operator_Entry { "**", binary_operator<pow_operator, '*', '*'> },
|
||||||
|
|
||||||
|
Operator_Entry { "!=", comparison_operator<std::not_equal_to<>> },
|
||||||
Operator_Entry { "<", comparison_operator<std::less<>> },
|
Operator_Entry { "<", comparison_operator<std::less<>> },
|
||||||
Operator_Entry { ">", comparison_operator<std::greater<>> },
|
|
||||||
Operator_Entry { "<=", comparison_operator<std::less_equal<>> },
|
Operator_Entry { "<=", comparison_operator<std::less_equal<>> },
|
||||||
|
Operator_Entry { "==", comparison_operator<std::equal_to<>> },
|
||||||
|
Operator_Entry { ">", comparison_operator<std::greater<>> },
|
||||||
Operator_Entry { ">=", comparison_operator<std::greater_equal<>> },
|
Operator_Entry { ">=", comparison_operator<std::greater_equal<>> },
|
||||||
|
|
||||||
Operator_Entry { "==", equality_operator<std::equal_to<>> },
|
|
||||||
Operator_Entry { "!=", equality_operator<std::not_equal_to<>> },
|
|
||||||
|
|
||||||
Operator_Entry { ".",
|
Operator_Entry { ".",
|
||||||
+[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
|
+[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
|
||||||
if (args.size() == 2 && is_indexable(args[0].type)) {
|
if (args.size() == 2 && is_indexable(args[0].type)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user