diff --git a/CHANGELOG.md b/CHANGELOG.md index 236ae31..4d9240c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- `{}` notation for concurrently executing blocks +- `{}` notation for concurrently executing blocks and describing set of values +- `set` data type which contains an unordered collection of unique elements ### Fixed diff --git a/examples/for-elise.mq b/examples/for-elise.mq index 765c76c..1076479 100644 --- a/examples/for-elise.mq +++ b/examples/for-elise.mq @@ -5,61 +5,63 @@ WIP implemntation oct 5, bpm 72, len (1/16), subsection1 := ( - sim (a4 en) (a2 e3 a3), - play (oct 4, c e a), + { a4 en, a2 e3 a3 }, + oct 4, c e a, - sim (b4 en) (e2 e3 g#3), - play (oct 4, e g# b), + { b4 en, e2 e3 g#3 }, + oct 4, e g# b, - sim (c5 en) (a2 e3 a3), - play (e4 e5 d#5), + { c5 en, a2 e3 a3 }, + e4 e5 d#5, - play e d# e b4 d c, + oct 5, e d# e b4 d c, - sim (a4 en) (a2 e3 a3), - play c4 e4 a4, + { a4 en, a2 e3 a3 }, + c4 e4 a4, - sim (b4 en) (e2 e3 g#3), - play d4 c5 b4, + { b4 en, e2 e3 g#3 }, + d4 c5 b4, ), section1 := ( n | - play e d#, - play e d# e b4 d c, + e d#, + e d# e b4 d c, call subsection1, if (n == 1) - ( sim (a4 qn) (a2 e3 a3) ) - ( sim (a4 en) (a2 e3 a3) - , play b4 c5 d5 + { a4 qn, a2 e3 a3 } + ( { a4 en, a2 e3 a3 } + , b4 c5 d5 ) ), section2 := ( n | - sim (e5 den) (c3 g3 c4), - play g4 f e, + { e5 den, c3 g3 c4 }, + g4 f e, - sim (d 5 den) (g2 g3 b4), - play f4 e d, + { d5 den, g2 g3 b4 }, + f4 e d, - sim (c5 den) (a2 e3 a3), - play e4 d c, + { c5 den, a2 e3 a3 }, + e4 d c, - sim (b4 en) (e2 e3 e4), - play (e4 e5 e4 e4 e5 e6 d#5 e5 d#5 e5 en), + { b4 en, e2 e3 e4 }, + e4 e5 e4 e4 e5 e6 d#5 e5 d#5 e5 en, - play d# e d# e d#, - play e d# e b4 d c, + d# e d# e d#, + e d# e b4 d c, call subsection1, if (n == 1) - ( sim (a4 en) (a2 e3 a3) + ( { a4 en, a2 e3 a3 } , play (b4 c5 d5) ) ), -section1 1, -section1 2, -section2 1, +play ( + section1 1, + section1 2, + section2 1, +) diff --git a/musique/common.hh b/musique/common.hh index 0182bf3..59bcb5f 100644 --- a/musique/common.hh +++ b/musique/common.hh @@ -2,6 +2,7 @@ #define MUSIQUE_COMMON_HH #include +#include #include #include @@ -62,4 +63,6 @@ concept Three_Way_Comparable = requires (T const& lhs, T const& rhs) { { lhs <=> rhs }; }; +extern std::mutex stdio_mutex; + #endif diff --git a/musique/format.cc b/musique/format.cc index 9ba9b07..9844d8e 100644 --- a/musique/format.cc +++ b/musique/format.cc @@ -49,20 +49,25 @@ 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 << '('; + auto const [open, close] = block.body.type == Ast::Type::Concurrent + ? std::pair { '{', '}' } + : std::pair { '(', ')' }; + + os << open; for (auto i = 0u; i < block.size(); ++i) { if (i > 0) { os << ", "; } Try(nest(Inside_Block).format(os, interpreter, Try(block.index(interpreter, i)))); } - os << ')'; + os << close; } else { - os << ""; + if (block.body.type == Ast::Type::Concurrent) { + os << ""; + } else { + os << ""; + } } return {}; }, diff --git a/musique/interpreter/builtin_functions.cc b/musique/interpreter/builtin_functions.cc index 38d236d..bf6e515 100644 --- a/musique/interpreter/builtin_functions.cc +++ b/musique/interpreter/builtin_functions.cc @@ -1,7 +1,8 @@ #include -#include #include +#include #include +#include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include /// Check if type has index method template @@ -254,6 +256,19 @@ static inline std::optional sequential_play(Interpreter &interpreter, Val } else if (auto chord = get_if(v)) { return interpreter.play(*chord); + } else if (auto set = get_if(v)) { + std::vector>> futures; + for (auto&& value : set->elements) { + futures.push_back(std::async(std::launch::async, + [interpreter = interpreter.clone(), value = std::move(value)]() mutable { + return sequential_play(interpreter, std::move(value)); + } + )); + } + + for (auto &fut : futures) { + Try(fut.get()); + } } return {}; @@ -267,27 +282,25 @@ static std::optional action_play(Interpreter &interpreter, Value v) } /// Play notes -template> -static inline Result builtin_play(Interpreter &interpreter, Container args) +static inline Result builtin_play(Interpreter &interpreter, std::span args) { Try(ensure_midi_connection_available(interpreter, "play")); auto const previous_action = std::exchange(interpreter.default_action, action_play); auto const previous_context = std::exchange(interpreter.current_context, std::make_shared(*interpreter.current_context)); - auto const finally = [&] { + Scope_Exit { interpreter.default_action = std::move(previous_action); interpreter.current_context = previous_context; }; - for (auto &el : args) { - if (std::optional error = sequential_play(interpreter, std::move(el))) { - finally(); + for (auto &node : args) { + auto value = Try(interpreter.eval((Ast)node)); + if (std::optional error = sequential_play(interpreter, std::move(value))) { return *std::move(error); } } - finally(); return {}; } @@ -316,14 +329,15 @@ static Result builtin_par(Interpreter &interpreter, std::vector ar } } - auto result = builtin_play(interpreter, std::span(args).subspan(1)); + unimplemented(); + // auto result = builtin_play(interpreter, std::span(args).subspan(1)); - for (auto const& note : chord->notes) { - if (note.base) { - interpreter.midi_connection->send_note_off(0, *note.into_midi_note(), 127); - } - } - return result; + // for (auto const& note : chord->notes) { + // if (note.base) { + // interpreter.midi_connection->send_note_off(0, *note.into_midi_note(), 127); + // } + // } + // return result; } /// Plays each argument simultaneously diff --git a/musique/interpreter/interpreter.cc b/musique/interpreter/interpreter.cc index 36271fb..a1976f6 100644 --- a/musique/interpreter/interpreter.cc +++ b/musique/interpreter/interpreter.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -97,14 +98,14 @@ static Result eval_concurrent(Interpreter &interpreter, Ast &&ast) })); } - std::vector results; + Set set; for (auto& future : futures) { if (error) { return *error; } - results.push_back(future.get()); + set.elements.insert(future.get()); } - return results; + return set; } Result Interpreter::eval(Ast &&ast) @@ -288,6 +289,11 @@ void Interpreter::leave_scope() std::optional Interpreter::play(Chord chord) { + if (global_options::dump_play_actions) { + std::lock_guard guard{stdio_mutex}; + std::cerr << "[DEBUG] Interpreter::play " << chord << std::endl; + } + Try(ensure_midi_connection_available(*this, "play")); auto &ctx = *current_context; diff --git a/musique/interpreter/interpreter.hh b/musique/interpreter/interpreter.hh index a94b706..975d29e 100644 --- a/musique/interpreter/interpreter.hh +++ b/musique/interpreter/interpreter.hh @@ -35,15 +35,16 @@ private: Interpreter(Clone); public: + /// Explicit clone method Interpreter clone() const; /// Try to evaluate given program tree Result eval(Ast &&ast); - // Enter scope by changing current environment + /// Enter scope by changing current environment void enter_scope(); - // Leave scope by changing current environment + /// Leave scope by changing current environment void leave_scope(); /// Play note resolving any missing parameters with context via `midi_connection` member. diff --git a/musique/main.cc b/musique/main.cc index 6fe59dc..ab5a45a 100644 --- a/musique/main.cc +++ b/musique/main.cc @@ -37,6 +37,8 @@ static bool ast_only_mode = false; static bool enable_repl = false; static unsigned repl_line_number = 1; +std::mutex stdio_mutex; + #define Ignore(Call) do { auto const ignore_ ## __LINE__ = (Call); (void) ignore_ ## __LINE__; } while(0) /// Pop string from front of an array @@ -190,7 +192,6 @@ struct Runner } Env::global->force_define("print", +[](Interpreter &interpreter, std::vector args) -> Result { - static std::mutex stdio_mutex; for (auto it = args.begin(); it != args.end(); ++it) { { auto result = Try(format(interpreter, *it)); diff --git a/musique/value/block.cc b/musique/value/block.cc index 09413fa..e5c2cc7 100644 --- a/musique/value/block.cc +++ b/musique/value/block.cc @@ -4,6 +4,11 @@ #include #include +inline bool is_ast_collection(Ast::Type type) +{ + return type == Ast::Type::Sequence || type == Ast::Type::Concurrent; +} + /// Helper that produces error when trying to access container with too few elements for given index static inline std::optional guard_index(unsigned index, unsigned size) { @@ -17,18 +22,18 @@ static inline std::optional guard_index(unsigned index, unsigned size) Result Block::index(Interpreter &i, unsigned position) const { ensure(parameters.empty(), "cannot index into block with parameters (for now)"); - if (body.type != Ast::Type::Sequence) { - Try(guard_index(position, 1)); - return i.eval((Ast)body); + if (is_ast_collection(body.type)) { + Try(guard_index(position, body.arguments.size())); + return i.eval((Ast)body.arguments[position]); } - Try(guard_index(position, body.arguments.size())); - return i.eval((Ast)body.arguments[position]); + Try(guard_index(position, 1)); + return i.eval((Ast)body); } usize Block::size() const { - return body.type == Ast::Type::Sequence ? body.arguments.size() : 1; + return is_ast_collection(body.type) ? body.arguments.size() : 1; } Result Block::operator()(Interpreter &i, std::vector arguments) const