diff --git a/include/musique.hh b/include/musique.hh index 3a88d96..41bad3a 100644 --- a/include/musique.hh +++ b/include/musique.hh @@ -1099,4 +1099,9 @@ static constexpr bool is_callable(Value::Type type) Result> flatten(Interpreter &i, std::span); Result> flatten(Interpreter &i, std::vector); +template<> struct std::hash { std::size_t operator()(Token const&) const; }; +template<> struct std::hash { std::size_t operator()(Ast const&) const; }; +template<> struct std::hash { std::size_t operator()(Number const&) const; }; +template<> struct std::hash { std::size_t operator()(Value const&) const; }; + #endif diff --git a/include/musique_internal.hh b/include/musique_internal.hh index 52679c5..f6234cf 100644 --- a/include/musique_internal.hh +++ b/include/musique_internal.hh @@ -97,4 +97,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); +constexpr std::size_t hash_combine(std::size_t lhs, std::size_t rhs) { + return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); +} #endif diff --git a/src/builtin_functions.cc b/src/builtin_functions.cc index 4e62e9d..7c64472 100644 --- a/src/builtin_functions.cc +++ b/src/builtin_functions.cc @@ -4,6 +4,7 @@ #include #include #include +#include void Interpreter::register_callbacks() { @@ -601,6 +602,48 @@ static Result builtin_rotate(Interpreter &i, std::vector args) return Value::from(std::move(array)); } +/// Returns unique collection of arguments +static Result builtin_unique(Interpreter &i, std::vector args) +{ + auto array = Try(flatten(i, args)); + std::unordered_set seen; + + std::vector result; + for (auto &el : array) { + if (!seen.contains(el)) { + seen.insert(el); + result.push_back(std::move(el)); + } + } + return Value::from(std::move(result)); +} + +/// Returns arguments with all successive copies eliminated +static Result builtin_uniq(Interpreter &i, std::vector args) +{ + auto array = Try(flatten(i, args)); + + std::optional previous; + std::vector result; + + for (auto &el : array) { + if (previous && *previous == el) + continue; + result.push_back(el); + previous = std::move(el); + } + return Value::from(std::move(result)); +} + +static Result builtin_hash(Interpreter&, std::vector args) +{ + return Value::from(Number( + std::accumulate(args.cbegin(), args.cend(), size_t(0), [](size_t h, Value const& v) { + return hash_combine(h, std::hash{}(v)); + }) + )); +} + /// Build chord from arguments static Result builtin_chord(Interpreter &i, std::vector args) { @@ -714,6 +757,7 @@ void Interpreter::register_builtin_functions() global.force_define("flat", builtin_flat); global.force_define("floor", apply_numeric_transform<&Number::floor>); global.force_define("for", builtin_for); + global.force_define("hash", builtin_hash); global.force_define("if", builtin_if); global.force_define("incoming", builtin_incoming); global.force_define("instrument", builtin_program_change); @@ -738,6 +782,8 @@ void Interpreter::register_builtin_functions() global.force_define("sort", builtin_sort); global.force_define("try", builtin_try); global.force_define("typeof", builtin_typeof); + global.force_define("uniq", builtin_uniq); + global.force_define("unique", builtin_unique); global.force_define("up", builtin_range); global.force_define("update", builtin_update); } diff --git a/src/lexer.cc b/src/lexer.cc index 6ed9dfa..5bd30c2 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -1,4 +1,5 @@ #include +#include #include @@ -277,3 +278,9 @@ std::string_view type_name(Token::Type type) } unreachable(); } + +std::size_t std::hash::operator()(Token const& token) const +{ + return hash_combine(std::hash{}(token.source), size_t(token.type)); +} + diff --git a/src/number.cc b/src/number.cc index b6291c3..cef8565 100644 --- a/src/number.cc +++ b/src/number.cc @@ -1,4 +1,6 @@ #include +#include + #include #include #include @@ -332,3 +334,9 @@ Result Number::pow(Number n) const // We need to protect ourselfs against even roots of negative numbers. unimplemented(); } + +std::size_t std::hash::operator()(Number const& value) const +{ + std::hash h; + return hash_combine(h(value.num), h(value.den)); +} diff --git a/src/parser.cc b/src/parser.cc index a7fa2af..1cbf874 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1,5 +1,8 @@ #include +#include + #include +#include static Ast wrap_if_several(std::vector &&ast, Ast(*wrapper)(std::vector)); @@ -616,3 +619,12 @@ void dump(Ast const& tree, unsigned indent) std::cout << std::flush; } } + +std::size_t std::hash::operator()(Ast const& value) const +{ + auto h = std::hash{}(value.token); + h = std::accumulate(value.arguments.begin(), value.arguments.end(), h, [this](size_t h, Ast const& node) { + return hash_combine(h, operator()(node)); + }); + return hash_combine(size_t(value.type), h); +} diff --git a/src/value.cc b/src/value.cc index a871acb..b2506e7 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1,4 +1,7 @@ #include +#include + +#include /// Finds numeric value of note. This form is later used as in /// note to midi resolution in formula octave * 12 + note_index @@ -522,3 +525,31 @@ Result> flatten(Interpreter &i, std::vector args) { return flatten(i, std::span(args)); } + +std::size_t std::hash::operator()(Value const& value) const +{ + size_t value_hash = 0; + switch (value.type) { + break; case Value::Type::Nil: value_hash = 0; + break; case Value::Type::Number: value_hash = std::hash{}(value.n); + break; case Value::Type::Symbol: value_hash = std::hash{}(value.s); + break; case Value::Type::Bool: value_hash = std::hash{}(value.b); + break; case Value::Type::Intrinsic: value_hash = ptrdiff_t(value.intr); + break; case Value::Type::Block: value_hash = hash_combine(std::hash{}(value.blk.body), value.blk.parameters.size()); + + break; case Value::Type::Array: + value_hash = std::accumulate(value.array.elements.begin(), value.array.elements.end(), value_hash, [this](size_t h, Value const& v) { + return hash_combine(h, operator()(v)); + }); + + break; case Value::Type::Music: + value_hash = std::accumulate(value.chord.notes.begin(), value.chord.notes.end(), value_hash, [](size_t h, Note const& n) { + h = hash_combine(h, n.base); + h = hash_combine(h, std::hash>{}(n.length)); + h = hash_combine(h, std::hash>{}(n.octave)); + return h; + }); + } + + return hash_combine(value_hash, size_t(value.type)); +}