Integrated Set type into existing infrastructure

This commit is contained in:
Robert Bendun 2022-11-24 18:35:51 +01:00
parent f19240d931
commit 948243febd
9 changed files with 102 additions and 64 deletions

View File

@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### 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 ### Fixed

View File

@ -5,61 +5,63 @@ WIP implemntation
oct 5, bpm 72, len (1/16), oct 5, bpm 72, len (1/16),
subsection1 := ( subsection1 := (
sim (a4 en) (a2 e3 a3), { a4 en, a2 e3 a3 },
play (oct 4, c e a), oct 4, c e a,
sim (b4 en) (e2 e3 g#3), { b4 en, e2 e3 g#3 },
play (oct 4, e g# b), oct 4, e g# b,
sim (c5 en) (a2 e3 a3), { c5 en, a2 e3 a3 },
play (e4 e5 d#5), 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), { a4 en, a2 e3 a3 },
play c4 e4 a4, c4 e4 a4,
sim (b4 en) (e2 e3 g#3), { b4 en, e2 e3 g#3 },
play d4 c5 b4, d4 c5 b4,
), ),
section1 := ( n | section1 := ( n |
play e d#, e d#,
play e d# e b4 d c, e d# e b4 d c,
call subsection1, call subsection1,
if (n == 1) if (n == 1)
( sim (a4 qn) (a2 e3 a3) ) { a4 qn, a2 e3 a3 }
( sim (a4 en) (a2 e3 a3) ( { a4 en, a2 e3 a3 }
, play b4 c5 d5 , b4 c5 d5
) )
), ),
section2 := ( n | section2 := ( n |
sim (e5 den) (c3 g3 c4), { e5 den, c3 g3 c4 },
play g4 f e, g4 f e,
sim (d 5 den) (g2 g3 b4), { d5 den, g2 g3 b4 },
play f4 e d, f4 e d,
sim (c5 den) (a2 e3 a3), { c5 den, a2 e3 a3 },
play e4 d c, e4 d c,
sim (b4 en) (e2 e3 e4), { b4 en, e2 e3 e4 },
play (e4 e5 e4 e4 e5 e6 d#5 e5 d#5 e5 en), e4 e5 e4 e4 e5 e6 d#5 e5 d#5 e5 en,
play d# e d# e d#, d# e d# e d#,
play e d# e b4 d c, e d# e b4 d c,
call subsection1, call subsection1,
if (n == 1) if (n == 1)
( sim (a4 en) (a2 e3 a3) ( { a4 en, a2 e3 a3 }
, play (b4 c5 d5) , play (b4 c5 d5)
) )
), ),
section1 1, play (
section1 2, section1 1,
section2 1, section1 2,
section2 1,
)

View File

@ -2,6 +2,7 @@
#define MUSIQUE_COMMON_HH #define MUSIQUE_COMMON_HH
#include <cstdint> #include <cstdint>
#include <mutex>
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -62,4 +63,6 @@ concept Three_Way_Comparable = requires (T const& lhs, T const& rhs) {
{ lhs <=> rhs }; { lhs <=> rhs };
}; };
extern std::mutex stdio_mutex;
#endif #endif

View File

@ -49,20 +49,25 @@ std::optional<Error> Value_Formatter::format(std::ostream& os, Interpreter &inte
return {}; return {};
}, },
[&](Block const& block) -> std::optional<Error> { [&](Block const& block) -> std::optional<Error> {
if (block.body.arguments.front().type == Ast::Type::Concurrent)
unimplemented("Nice printing of concurrent blocks is not implemented yet");
if (block.is_collection()) { 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) { for (auto i = 0u; i < block.size(); ++i) {
if (i > 0) { if (i > 0) {
os << ", "; os << ", ";
} }
Try(nest(Inside_Block).format(os, interpreter, Try(block.index(interpreter, i)))); Try(nest(Inside_Block).format(os, interpreter, Try(block.index(interpreter, i))));
} }
os << ')'; os << close;
} else { } else {
os << "<block>"; if (block.body.type == Ast::Type::Concurrent) {
os << "<concurrent block>";
} else {
os << "<sequential block>";
}
} }
return {}; return {};
}, },

View File

@ -1,7 +1,8 @@
#include <musique/algo.hh> #include <musique/algo.hh>
#include <musique/interpreter/env.hh>
#include <musique/guard.hh> #include <musique/guard.hh>
#include <musique/interpreter/env.hh>
#include <musique/interpreter/interpreter.hh> #include <musique/interpreter/interpreter.hh>
#include <musique/scope_exit.hh>
#include <musique/try.hh> #include <musique/try.hh>
#include <chrono> #include <chrono>
@ -12,6 +13,7 @@
#include <random> #include <random>
#include <thread> #include <thread>
#include <unordered_set> #include <unordered_set>
#include <future>
/// Check if type has index method /// Check if type has index method
template<typename T> template<typename T>
@ -254,6 +256,19 @@ static inline std::optional<Error> sequential_play(Interpreter &interpreter, Val
} }
else if (auto chord = get_if<Chord>(v)) { else if (auto chord = get_if<Chord>(v)) {
return interpreter.play(*chord); return interpreter.play(*chord);
} else if (auto set = get_if<Set>(v)) {
std::vector<std::future<std::optional<Error>>> 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 {}; return {};
@ -267,27 +282,25 @@ static std::optional<Error> action_play(Interpreter &interpreter, Value v)
} }
/// Play notes /// Play notes
template<With_Index_Operator Container = std::vector<Value>> static inline Result<Value> builtin_play(Interpreter &interpreter, std::span<Ast> args)
static inline Result<Value> builtin_play(Interpreter &interpreter, Container args)
{ {
Try(ensure_midi_connection_available(interpreter, "play")); Try(ensure_midi_connection_available(interpreter, "play"));
auto const previous_action = std::exchange(interpreter.default_action, action_play); auto const previous_action = std::exchange(interpreter.default_action, action_play);
auto const previous_context = std::exchange(interpreter.current_context, auto const previous_context = std::exchange(interpreter.current_context,
std::make_shared<Context>(*interpreter.current_context)); std::make_shared<Context>(*interpreter.current_context));
auto const finally = [&] { Scope_Exit {
interpreter.default_action = std::move(previous_action); interpreter.default_action = std::move(previous_action);
interpreter.current_context = previous_context; interpreter.current_context = previous_context;
}; };
for (auto &el : args) { for (auto &node : args) {
if (std::optional<Error> error = sequential_play(interpreter, std::move(el))) { auto value = Try(interpreter.eval((Ast)node));
finally(); if (std::optional<Error> error = sequential_play(interpreter, std::move(value))) {
return *std::move(error); return *std::move(error);
} }
} }
finally();
return {}; return {};
} }
@ -316,14 +329,15 @@ static Result<Value> builtin_par(Interpreter &interpreter, std::vector<Value> 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) { // for (auto const& note : chord->notes) {
if (note.base) { // if (note.base) {
interpreter.midi_connection->send_note_off(0, *note.into_midi_note(), 127); // interpreter.midi_connection->send_note_off(0, *note.into_midi_note(), 127);
} // }
} // }
return result; // return result;
} }
/// Plays each argument simultaneously /// Plays each argument simultaneously

View File

@ -1,5 +1,6 @@
#include <musique/interpreter/env.hh> #include <musique/interpreter/env.hh>
#include <musique/interpreter/interpreter.hh> #include <musique/interpreter/interpreter.hh>
#include <musique/options.hh>
#include <musique/try.hh> #include <musique/try.hh>
#include <chrono> #include <chrono>
@ -97,14 +98,14 @@ static Result<Value> eval_concurrent(Interpreter &interpreter, Ast &&ast)
})); }));
} }
std::vector<Value> results; Set set;
for (auto& future : futures) { for (auto& future : futures) {
if (error) { if (error) {
return *error; return *error;
} }
results.push_back(future.get()); set.elements.insert(future.get());
} }
return results; return set;
} }
Result<Value> Interpreter::eval(Ast &&ast) Result<Value> Interpreter::eval(Ast &&ast)
@ -288,6 +289,11 @@ void Interpreter::leave_scope()
std::optional<Error> Interpreter::play(Chord chord) std::optional<Error> 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")); Try(ensure_midi_connection_available(*this, "play"));
auto &ctx = *current_context; auto &ctx = *current_context;

View File

@ -35,15 +35,16 @@ private:
Interpreter(Clone); Interpreter(Clone);
public: public:
/// Explicit clone method
Interpreter clone() const; Interpreter clone() const;
/// Try to evaluate given program tree /// Try to evaluate given program tree
Result<Value> eval(Ast &&ast); Result<Value> eval(Ast &&ast);
// Enter scope by changing current environment /// Enter scope by changing current environment
void enter_scope(); void enter_scope();
// Leave scope by changing current environment /// Leave scope by changing current environment
void leave_scope(); void leave_scope();
/// Play note resolving any missing parameters with context via `midi_connection` member. /// Play note resolving any missing parameters with context via `midi_connection` member.

View File

@ -37,6 +37,8 @@ static bool ast_only_mode = false;
static bool enable_repl = false; static bool enable_repl = false;
static unsigned repl_line_number = 1; static unsigned repl_line_number = 1;
std::mutex stdio_mutex;
#define Ignore(Call) do { auto const ignore_ ## __LINE__ = (Call); (void) ignore_ ## __LINE__; } while(0) #define Ignore(Call) do { auto const ignore_ ## __LINE__ = (Call); (void) ignore_ ## __LINE__; } while(0)
/// Pop string from front of an array /// Pop string from front of an array
@ -190,7 +192,6 @@ struct Runner
} }
Env::global->force_define("print", +[](Interpreter &interpreter, std::vector<Value> args) -> Result<Value> { Env::global->force_define("print", +[](Interpreter &interpreter, std::vector<Value> args) -> Result<Value> {
static std::mutex stdio_mutex;
for (auto it = args.begin(); it != args.end(); ++it) { for (auto it = args.begin(); it != args.end(); ++it) {
{ {
auto result = Try(format(interpreter, *it)); auto result = Try(format(interpreter, *it));

View File

@ -4,6 +4,11 @@
#include <musique/value/block.hh> #include <musique/value/block.hh>
#include <musique/value/value.hh> #include <musique/value/value.hh>
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 /// Helper that produces error when trying to access container with too few elements for given index
static inline std::optional<Error> guard_index(unsigned index, unsigned size) static inline std::optional<Error> guard_index(unsigned index, unsigned size)
{ {
@ -17,18 +22,18 @@ static inline std::optional<Error> guard_index(unsigned index, unsigned size)
Result<Value> Block::index(Interpreter &i, unsigned position) const Result<Value> Block::index(Interpreter &i, unsigned position) const
{ {
ensure(parameters.empty(), "cannot index into block with parameters (for now)"); ensure(parameters.empty(), "cannot index into block with parameters (for now)");
if (body.type != Ast::Type::Sequence) { if (is_ast_collection(body.type)) {
Try(guard_index(position, 1)); Try(guard_index(position, body.arguments.size()));
return i.eval((Ast)body); return i.eval((Ast)body.arguments[position]);
} }
Try(guard_index(position, body.arguments.size())); Try(guard_index(position, 1));
return i.eval((Ast)body.arguments[position]); return i.eval((Ast)body);
} }
usize Block::size() const 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<Value> Block::operator()(Interpreter &i, std::vector<Value> arguments) const Result<Value> Block::operator()(Interpreter &i, std::vector<Value> arguments) const