diff --git a/src/errors.cc b/src/errors.cc index 81647be..dafe3b8 100644 --- a/src/errors.cc +++ b/src/errors.cc @@ -71,7 +71,8 @@ static void encourage_contact(std::ostream &os) { os << pretty::begin_comment << "\n" "Interpreter got in state that was not expected by it's developers.\n" - "Contact them providing code that coused it and error message above to resolve this trouble\n" + "Contact them and provide code that coused this error and\n" + "error message above to resolve this trouble\n" << pretty::end << std::flush; } diff --git a/src/interpreter.cc b/src/interpreter.cc index 2511319..6727dd6 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -4,21 +4,124 @@ #include #include +/// Intrinsic implementation primitive providing a short way to check if arguments match required type signature +static inline bool typecheck(std::vector const& args, auto const& ...expected_types) +{ + return (args.size() == sizeof...(expected_types)) && + [&args, expected_types...](std::index_sequence) { + return ((expected_types == args[I].type) && ...); + } (std::make_index_sequence{}); +} + +/// Intrinsic implementation primitive providing a short way to move values based on matched type signature +template +static inline auto move_from(std::vector& args) +{ + return [&args](std::index_sequence) { + return std::tuple { (std::move(args[I]).*(Member_For_Value_Type::value)) ... }; + } (std::make_index_sequence{}); +} + +/// Shape abstraction to define what types are required once +template +struct Shape +{ + static inline auto move_from(std::vector& args) { return ::move_from(args); } + static inline auto typecheck(std::vector& args) { return ::typecheck(args, Types...); } +}; + +/// Returns if type can be indexed +static constexpr bool is_indexable(Value::Type type) +{ + return type == Value::Type::Array || type == Value::Type::Block; +} + +/// Binary operation may be vectorized when there are two argument which one is indexable and other is not +static bool may_be_vectorized(std::vector const& args) +{ + return args.size() == 2 && (is_indexable(args[0].type) != is_indexable(args[1].type)); +} + +/// Intrinsic implementation primitive to ease operation vectorization +/// @invariant args.size() == 2 +Result vectorize(auto &&operation, Interpreter &interpreter, std::vector args) +{ + assert(args.size() == 2, "Vectorization primitive only supports two arguments"); + Array array; + + auto lhs = std::move(args.front()); + auto rhs = std::move(args.back()); + + if (is_indexable(lhs.type) && !is_indexable(rhs.type)) { + Array array; + for (auto i = 0u; i < lhs.size(); ++i) { + array.elements.push_back( + Try(operation(interpreter, { Try(lhs.index(interpreter, i)), rhs }))); + } + return Value::from(std::move(array)); + } + + for (auto i = 0u; i < rhs.size(); ++i) { + array.elements.push_back( + Try(operation(interpreter, { lhs, Try(rhs.index(interpreter, i)) }))); + } + return Value::from(std::move(array)); +} + +/// Creates implementation of plus/minus operator that support following operations: +/// number, number -> number (standard math operations) +/// n: number, m: music -> music +/// m: music, n: number -> music moves m by n semitones (+ goes up, - goes down) +template +static Result plus_minus_operator(Interpreter &interpreter, std::vector args) +{ + using NN = Shape; + using MN = Shape; + using NM = Shape; + + if (NN::typecheck(args)) { + auto [a, b] = NN::move_from(args); + return Value::from(Binary_Operation{}(std::move(a), std::move(b))); + } + + if (MN::typecheck(args)) { + auto [chord, offset] = MN::move_from(args); + for (auto ¬e : chord.notes) { + note.base = Binary_Operation{}(note.base, offset.as_int()); + note.simplify_inplace(); + } + return Value::from(std::move(chord)); + } + + if (NM::typecheck(args)) { + auto [offset, chord] = NM::move_from(args); + for (auto ¬e : chord.notes) { + note.base = Binary_Operation{}(offset.as_int(), note.base); + note.simplify_inplace(); + } + return Value::from(std::move(chord)); + } + + if (may_be_vectorized(args)) { + return vectorize(plus_minus_operator, interpreter, std::move(args)); + } + + assert(false, "Unsupported types for this operation"); // TODO(assert) + unreachable(); +} + + template static Result binary_operator(Interpreter&, std::vector args) { - auto result = std::move(args.front()); - for (auto &v : std::span(args).subspan(1)) { - assert(result.type == Value::Type::Number, "LHS should be a number"); // TODO(assert) - assert(v.type == Value::Type::Number, "RHS should be a number"); // TODO(assert) - if constexpr (std::is_same_v>) { - result.n = Binary_Operation{}(std::move(result.n), std::move(v).n); - } else { - result.type = Value::Type::Bool; - result.b = Binary_Operation{}(std::move(result.n), std::move(v).n); - } + using NN = Shape; + + if (NN::typecheck(args)) { + auto [lhs, rhs] = NN::move_from(args); + return Value::from(Binary_Operation{}(lhs, rhs)); } - return result; + + unreachable(); } template @@ -31,19 +134,19 @@ static Result equality_operator(Interpreter&, std::vector args) template static Result comparison_operator(Interpreter&, std::vector args) { - assert(args.size() == 2, "Ordering only allows for 2 operands"); // TODO(assert) - assert(args.front().type == args.back().type, "Only values of the same type can be ordered"); // TODO(assert) + using NN = Shape; + using BB = Shape; - switch (args.front().type) { - case Value::Type::Number: - return Value::from(Binary_Predicate{}(std::move(args.front()).n, std::move(args.back()).n)); - - case Value::Type::Bool: - return Value::from(Binary_Predicate{}(std::move(args.front()).b, std::move(args.back()).b)); - - default: - assert(false, "Cannot compare value of given types"); // TODO(assert) + if (NN::typecheck(args)) { + auto [a, b] = NN::move_from(args); + return Value::from(Binary_Predicate{}(std::move(a), std::move(b))); } + + if (BB::typecheck(args)) { + auto [a, b] = BB::move_from(args); + return Value::from(Binary_Predicate{}(a, b)); + } + unreachable(); } @@ -141,66 +244,6 @@ static inline Result play_notes(Interpreter &interpreter, T args) return {}; } -constexpr bool is_indexable(Value::Type type) -{ - return type == Value::Type::Array || type == Value::Type::Block; -} - -/// Creates implementation of plus/minus operator that support following operations: -/// number, number -> number (standard math operations) -/// n: number, m: music -> music -/// m: music, n: number -> music moves m by n semitones (+ goes up, - goes down) -template -static Result plus_minus_operator(Interpreter &interpreter, std::vector args) -{ - assert(args.size() == 2, "Binary operator only accepts 2 arguments"); - auto lhs = std::move(args.front()); - auto rhs = std::move(args.back()); - - if (lhs.type == rhs.type && lhs.type == Value::Type::Number) { - return Value::from(Binary_Operation{}(std::move(lhs).n, std::move(rhs).n)); - } - - if (lhs.type == Value::Type::Music && rhs.type == Value::Type::Number) { - for (auto ¬e : lhs.chord.notes) { - note.base = Binary_Operation{}(note.base, rhs.n.as_int()); - note.simplify_inplace(); - } - return lhs; - } - - if (lhs.type == Value::Type::Number && rhs.type == Value::Type::Music) { - for (auto ¬e : rhs.chord.notes) { - note.base = Binary_Operation{}(lhs.n.as_int(), note.base); - note.simplify_inplace(); - } - return rhs; - } - - if (is_indexable(lhs.type) && !is_indexable(rhs.type)) { - Array array; - for (auto i = 0u; i < lhs.size(); ++i) { - array.elements.push_back(Try( - plus_minus_operator( - interpreter, { Try(lhs.index(interpreter, i)), rhs }))); - } - return Value::from(std::move(array)); - } - - if (!is_indexable(lhs.type) && is_indexable(rhs.type)) { - Array array; - for (auto i = 0u; i < rhs.size(); ++i) { - array.elements.push_back(Try( - plus_minus_operator( - interpreter, { lhs, Try(rhs.index(interpreter, i)) }))); - } - return Value::from(std::move(array)); - } - - assert(false, "Unsupported types for this operation"); // TODO(assert) - unreachable(); -} - template Result ctx_read_write_property(Interpreter &interpreter, std::vector args) { diff --git a/src/musique.hh b/src/musique.hh index 24cb4d6..29be61f 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -770,6 +770,30 @@ struct Value bool operator==(Value const& other) const; }; +template +struct Member_For_Value_Type {}; + +template<> struct Member_For_Value_Type +{ static constexpr auto value = &Value::b; }; + +template<> struct Member_For_Value_Type +{ static constexpr auto value = &Value::n; }; + +template<> struct Member_For_Value_Type +{ static constexpr auto value = &Value::s; }; + +template<> struct Member_For_Value_Type +{ static constexpr auto value = &Value::intr; }; + +template<> struct Member_For_Value_Type +{ static constexpr auto value = &Value::blk; }; + +template<> struct Member_For_Value_Type +{ static constexpr auto value = &Value::array; }; + +template<> struct Member_For_Value_Type +{ static constexpr auto value = &Value::chord; }; + /// Returns type name of Value type std::string_view type_name(Value::Type t);