diff --git a/musique/format.cc b/musique/format.cc index f35d1de..9ba9b07 100644 --- a/musique/format.cc +++ b/musique/format.cc @@ -19,6 +19,7 @@ Value_Formatter Value_Formatter::nest(Context nested) const std::optional Value_Formatter::format(std::ostream& os, Interpreter &interpreter, Value const& value) { + static_assert(requires { os << value; }); return std::visit(Overloaded { [&](Intrinsic const& intrinsic) -> std::optional { for (auto const& [key, val] : Env::global->variables) { @@ -48,6 +49,9 @@ std::optional Value_Formatter::format(std::ostream& os, Interpreter &inte return {}; }, [&](Block const& block) -> std::optional { + if (block.body.arguments.front().type == Ast::Type::Concurrent) + unimplemented("Nice printing of concurrent blocks is not implemented yet"); + if (block.is_collection()) { os << '('; for (auto i = 0u; i < block.size(); ++i) { @@ -62,6 +66,17 @@ std::optional Value_Formatter::format(std::ostream& os, Interpreter &inte } return {}; }, + [&](Set const& set) -> std::optional { + os << '{'; + for (auto it = set.elements.begin(); it != set.elements.end(); ++it) { + if (it != set.elements.begin()) { + os << ", "; + } + Try(nest(Inside_Block).format(os, interpreter, *it)); + } + os << '}'; + return {}; + }, [&](auto&&) -> std::optional { os << value; return {}; diff --git a/musique/interpreter/builtin_functions.cc b/musique/interpreter/builtin_functions.cc index 04a5fea..38d236d 100644 --- a/musique/interpreter/builtin_functions.cc +++ b/musique/interpreter/builtin_functions.cc @@ -838,6 +838,14 @@ static Result builtin_flat(Interpreter &i, std::vector args) return Try(into_flat_array(i, std::move(args))); } +/// Constructs set from given arguments +static Result builtin_set(Interpreter&, std::vector args) +{ + Set set; + std::move(args.begin(), args.end(), std::inserter(set.elements, set.elements.end())); + return set; +} + /// Pick random value from arugments static Result builtin_pick(Interpreter &i, std::vector args) { @@ -1154,6 +1162,7 @@ void Interpreter::register_builtin_functions() global.force_define("rotate", builtin_rotate); global.force_define("round", apply_numeric_transform<&Number::round>); global.force_define("scan", builtin_scan); + global.force_define("set", builtin_set); global.force_define("set_len", builtin_set_len); global.force_define("set_oct", builtin_set_oct); global.force_define("shuffle", builtin_shuffle); diff --git a/musique/interpreter/interpreter.cc b/musique/interpreter/interpreter.cc index ff59be8..36271fb 100644 --- a/musique/interpreter/interpreter.cc +++ b/musique/interpreter/interpreter.cc @@ -448,7 +448,8 @@ static void snapshot(std::ostream& out, Value const& value) { out << ")"; }, [](Intrinsic const&) { unreachable(); }, - [](Macro const&) { unreachable(); } + [](Macro const&) { unreachable(); }, + [](Set const&) { unimplemented("Snapshoting is not supported yet"); } }, value.data); } diff --git a/musique/value/array.hh b/musique/value/array.hh index 8869224..8c0a167 100644 --- a/musique/value/array.hh +++ b/musique/value/array.hh @@ -8,6 +8,8 @@ struct Interpreter; struct Value; +// TODO Array should have an ability to be invoked with duration parameter like singular note + /// Eager Array struct Array : Collection { diff --git a/musique/value/block.hh b/musique/value/block.hh index ae5aad7..a545d35 100644 --- a/musique/value/block.hh +++ b/musique/value/block.hh @@ -11,6 +11,8 @@ struct Env; struct Interpreter; struct Value; +// TODO Block should have an ability to be invoked with duration parameter like singular note + /// Lazy Array / Continuation / Closure type thingy struct Block : Collection, Function { diff --git a/musique/value/hash.cc b/musique/value/hash.cc new file mode 100644 index 0000000..5722e89 --- /dev/null +++ b/musique/value/hash.cc @@ -0,0 +1,50 @@ +#include +#include + +size_t std::hash::operator()(Set const& set) const noexcept +{ + return std::accumulate( + set.elements.cbegin(), set.elements.cend(), + size_t(0), + [](size_t hash, Value const& element) { + return hash_combine(hash, std::hash{}(element)); + } + ); +} + +std::size_t std::hash::operator()(Value const& value) const noexcept +{ + auto const value_hash = std::visit(Overloaded { + [](Nil) { + return std::size_t(0); + }, + + [](Intrinsic i) { + return size_t(i.function_pointer); + }, + + [](Block const& b) { + return hash_combine(std::hash{}(b.body), b.parameters.size()); + }, + + [this](Array const& array) { + return std::accumulate( + array.elements.begin(), array.elements.end(), size_t(0), + [this](size_t h, Value const& v) { return hash_combine(h, operator()(v)); } + ); + }, + + [](Chord const& chord) { + return std::accumulate(chord.notes.begin(), chord.notes.end(), size_t(0), [](size_t h, Note const& n) { + h = hash_combine(h, std::hash>{}(n.base)); + h = hash_combine(h, std::hash>{}(n.length)); + h = hash_combine(h, std::hash>{}(n.octave)); + return h; + }); + }, + + [](T const& t) { return std::hash{}(t); }, + }, value.data); + + return hash_combine(value_hash, size_t(value.data.index())); +} diff --git a/musique/value/hash.hh b/musique/value/hash.hh new file mode 100644 index 0000000..60f47f2 --- /dev/null +++ b/musique/value/hash.hh @@ -0,0 +1,17 @@ +#ifndef MUSIQUE_VALUE_HASH_HH +#define MUSIQUE_VALUE_HASH_HH + +#include + +#define Hash_For(Name) \ + struct Name; \ + template<> \ + struct std::hash { \ + size_t operator()(Name const&) const noexcept; \ + }; \ + +Hash_For(Block) +Hash_For(Set) +Hash_For(Value) + +#endif // MUSIQUE_VALUE_HASH_HH diff --git a/musique/value/set.cc b/musique/value/set.cc new file mode 100644 index 0000000..d00a78e --- /dev/null +++ b/musique/value/set.cc @@ -0,0 +1,60 @@ +#include +#include + + +Result Set::operator()(Interpreter&, std::vector params) const +{ + auto copy = *this; + + if (auto a = match(params)) { + auto [length] = *a; + for (auto& value : copy.elements) { + if (auto chord = get_if(value)) { + // FIXME This const_cast should be unnesesary + for (auto ¬e : const_cast(chord)->notes) { + note.length = length; + } + continue; + } + } + } + + // TODO Error reporting when parameters doesnt match + // TODO reconsider different options for this data structure invocation + return copy; +} + +Result Set::index(Interpreter&, unsigned position) const +{ + if (elements.size() < position) { + return errors::Out_Of_Range { + .required_index = position, + .size = elements.size() + }; + } + // FIXME std::unordered_set::iterator has forward iterator, which means + // that any element lookup has complecity O(n). This may have serious + // performance implications and further investigation is needed. + return *std::next(elements.begin(), position); +} + +usize Set::size() const +{ + return elements.size(); +} + +bool Set::is_collection() const +{ + return true; +} + +std::strong_ordering Set::operator<=>(Set const&) const +{ + unimplemented(); +} + +std::ostream& operator<<(std::ostream& out, Set const& set) +{ + unimplemented(); + return out; +} diff --git a/musique/value/set.hh b/musique/value/set.hh new file mode 100644 index 0000000..610db76 --- /dev/null +++ b/musique/value/set.hh @@ -0,0 +1,36 @@ +#ifndef MUSIQUE_VALUE_SET_HH +#define MUSIQUE_VALUE_SET_HH + +#include +#include + +// Needs to be always first, before all the implicit template instantiations +#include + +#include +#include +#include + +struct Value; + +struct Set : Collection, Function +{ + std::unordered_set elements; + + ~Set() = default; + + Result operator()(Interpreter &i, std::vector params) const override; + + Result index(Interpreter &i, unsigned position) const override; + + usize size() const override; + + bool is_collection() const override; + + bool operator==(Set const&) const = default; + std::strong_ordering operator<=>(Set const&) const; +}; + +std::ostream& operator<<(std::ostream& out, Set const& set); + +#endif diff --git a/musique/value/value.cc b/musique/value/value.cc index 8f12de2..a26971e 100644 --- a/musique/value/value.cc +++ b/musique/value/value.cc @@ -107,6 +107,11 @@ Value::Value(Chord chord) { } +Value::Value(Set &&set) + : data(std::move(set)) +{ +} + Result Value::operator()(Interpreter &i, std::vector args) const { if (auto func = get_if(data)) { @@ -188,7 +193,10 @@ std::ostream& operator<<(std::ostream& os, Value const& v) [&](Nil) { os << "nil"; }, [&](Intrinsic) { os << ""; }, [&](Block const&) { os << ""; }, - [&](auto const& s) { os << s; } + [&](auto const& s) { + static_assert(requires { os << s; }); + os << s; + } }, v.data); return os; } @@ -205,6 +213,7 @@ std::string_view type_name(Value const& v) [&](Nil const&) { return "nil"; }, [&](Number const&) { return "number"; }, [&](Symbol const&) { return "symbol"; }, + [&](Set const&) { return "set"; }, }, v.data); } @@ -228,28 +237,3 @@ Result> flatten(Interpreter &i, std::vector args) return flatten(i, std::span(args)); } -std::size_t std::hash::operator()(Value const& value) const -{ - auto const value_hash = std::visit(Overloaded { - [](Nil) { return std::size_t(0); }, - [](Intrinsic i) { return size_t(i.function_pointer); }, - [](Block const& b) { return hash_combine(std::hash{}(b.body), b.parameters.size()); }, - [this](Array const& array) { - return std::accumulate( - array.elements.begin(), array.elements.end(), size_t(0), - [this](size_t h, Value const& v) { return hash_combine(h, operator()(v)); } - ); - }, - [](Chord const& chord) { - return std::accumulate(chord.notes.begin(), chord.notes.end(), size_t(0), [](size_t h, Note const& n) { - h = hash_combine(h, std::hash>{}(n.base)); - h = hash_combine(h, std::hash>{}(n.length)); - h = hash_combine(h, std::hash>{}(n.octave)); - return h; - }); - }, - [](T const& t) { return std::hash{}(t); }, - }, value.data); - - return hash_combine(value_hash, size_t(value.data.index())); -} diff --git a/musique/value/value.hh b/musique/value/value.hh index 21d3fc3..8255b87 100644 --- a/musique/value/value.hh +++ b/musique/value/value.hh @@ -1,6 +1,9 @@ #ifndef MUSIQUE_VALUE_HH #define MUSIQUE_VALUE_HH +// Needs to be always first, before all the implicit template instantiations +#include + #include #include #include @@ -10,6 +13,7 @@ #include #include #include +#include struct Nil { @@ -40,6 +44,7 @@ struct Value Value(char const* s); ///< Create value of type symbol holding provided symbol Value(std::string s); ///< Create value of type symbol holding provided symbol Value(std::string_view s); ///< Create value of type symbol holding provided symbol + Value(Set &&set); ///< Create value of type set holding provided set explicit Value(std::vector &&array); ///< Create value of type array holding provided array // TODO Most strings should not be allocated by Value, but reference to string allocated previously @@ -54,6 +59,7 @@ struct Value Intrinsic, Block, Array, + Set, Chord, Macro > data = Nil{}; @@ -108,9 +114,6 @@ inline T* get_if(Value& v) { return get_if(v.data); } /// Returns type name of Value type std::string_view type_name(Value const& v); -std::ostream& operator<<(std::ostream& os, Value const& v); -template<> struct std::hash { std::size_t operator()(Value const&) const; }; - template Result wrap_value(Result &&value) { @@ -170,4 +173,6 @@ constexpr auto match(Values& ...values) -> std::optional> } (std::make_index_sequence{}); } +std::ostream& operator<<(std::ostream& os, Value const& v); + #endif