diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ffb620..50d57a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- new builtins: map, while, set_len, duration + ### Changed - Block can be called with more parameters then it requires +- Removed `[]` sequence notation in favour of `()` +- reorganized fold argument order + +### Fixed + +- Index operation using booleans behaves like a mask and not fancy way of spelling 0 and 1 ## [0.2.1] - 2022-10-21 diff --git a/examples/control-flow.mq b/examples/control-flow.mq new file mode 100644 index 0000000..019a633 --- /dev/null +++ b/examples/control-flow.mq @@ -0,0 +1,13 @@ + +say (if true 4 2); +say (if false 4 2); + +if true (say 4) (say 2); +if false (say 4) (say 2); + +n := 0; +while (n < 5) ( + say 'while n; + n = n + 1; +); + diff --git a/examples/factorial.mq b/examples/factorial.mq index a3af654..a684b0a 100644 --- a/examples/factorial.mq +++ b/examples/factorial.mq @@ -19,7 +19,7 @@ factorial_iterative := (n | ); -- Calculate factorial using composition of functions -factorial := (n | fold (1 + up n) 1 '*); +factorial := (n | fold '* 1 (1 + up n)); -- Gather all functions into array, and iterate over it -- This allows to reduce repeatition of this test case diff --git a/examples/noises.mq b/examples/noises.mq new file mode 100644 index 0000000..d664152 --- /dev/null +++ b/examples/noises.mq @@ -0,0 +1,25 @@ +-- todo: cicho, ostatni parametr midi +-- todo: for doesn't forwards implicit play context + +pick := (array | (shuffle array).0); + +hand1_pool := ( + chord (g# 3) (g 4); + chord (d# 3) (d 4); + chord (g 3) (g# 4); + chord (d 3) (d# 4) +); + +hand2_pool := (d 8; d# 8; g 8; g# 8; d 9; d# 9); + +for (up 10) ( + hand1_length := pick (hn; dhn); + hand1 := (set_len hand1_length (pick hand1_pool)); + + hand2 := (); + while (fold '+ (map duration hand2) != hand1_length) ( + hand2 = flat hand2 (set_len (1/64) (pick hand2_pool)); + ); + + sim hand1 hand2; +); diff --git a/examples/permutations.mq b/examples/permutations.mq index 8873cd1..8a6c2c4 100644 --- a/examples/permutations.mq +++ b/examples/permutations.mq @@ -1,4 +1,4 @@ -factorial := (n | fold (1 + up n) '*); +factorial := (n | fold '* (1 + up n)); list_all_permutations := ( array | diff --git a/musique/interpreter/builtin_functions.cc b/musique/interpreter/builtin_functions.cc index 9329b8f..ab21e28 100644 --- a/musique/interpreter/builtin_functions.cc +++ b/musique/interpreter/builtin_functions.cc @@ -525,35 +525,57 @@ static Result builtin_fold(Interpreter &interpreter, std::vector a constexpr auto guard = Guard<2> { .name = "fold", .possibilities = { - "(array, callback) -> any", - "(array, init, callback) -> any" + "(callback, ...values) -> any", } }; - Value array, init, callback; - switch (args.size()) { - break; case 2: - array = std::move(args[0]); - callback = std::move(args[1]); - if (array.size() != 0) { - init = Try(array.index(interpreter, 0)); + if (args.size()) { + if (auto p = get_if(args.front())) { + auto xs = Try(flatten(interpreter, std::span(args).subspan(1))); + if (xs.empty()) { + return Value{}; + } + auto init = xs[0]; + for (auto i = 1u; i < xs.size(); ++i) { + init = Try((*p)(interpreter, { std::move(init), std::move(xs[i]) })); + } + return init; } - break; case 3: - array = std::move(args[0]); - init = std::move(args[1]); - callback = std::move(args[2]); - break; default: + } + + return guard.yield_error(); +} + +static Result builtin_map(Interpreter &interpreter, std::vector args) +{ + static constexpr auto guard = Guard<2> { + .name = "fold", + .possibilities = { + "(callback, array) -> any", + "(callback, array, init) -> any" + } + }; + + if (args.empty()) { return guard.yield_error(); } - auto collection = Try(guard.match(array)); - auto function = Try(guard.match(callback)); + auto function = Try(guard.match(args.front())); - for (auto i = 0u; i < collection->size(); ++i) { - auto element = Try(collection->index(interpreter, i)); - init = Try((*function)(interpreter, { std::move(init), std::move(element) })); + std::vector result; + + for (auto &arg : std::span(args).subspan(1)) { + if (auto collection = get_if(arg)) { + for (auto i = 0u; i < collection->size(); ++i) { + auto element = Try(collection->index(interpreter, i)); + result.push_back(Try((*function)(interpreter, { std::move(element) }))); + } + } else { + result.push_back(Try((*function)(interpreter, { std::move(arg) }))); + } } - return init; + + return result; } /// Scan computes inclusive prefix sum @@ -601,6 +623,29 @@ static Result builtin_if(Interpreter &i, std::span args) { return Value{}; } +/// Loop block depending on condition +static Result builtin_while(Interpreter &i, std::span args) { + static constexpr auto guard = Guard<2> { + .name = "while", + .possibilities = { + "(any, function) -> any" + } + }; + + if (args.size() != 2) { + return guard.yield_error(); + } + + while (Try(i.eval((Ast)args.front())).truthy()) { + if (args[1].type == Ast::Type::Block) { + Try(i.eval((Ast)args[1].arguments.front())); + } else { + Try(i.eval((Ast)args[1])); + } + } + return Value{}; +} + /// Try executing all but last block and if it fails execute last one static Result builtin_try(Interpreter &interpreter, std::vector args) { @@ -680,6 +725,65 @@ static Result builtin_len(Interpreter &i, std::vector args) return ctx_read_write_property<&Context::length>(i, std::move(args)); } +Result traverse(Interpreter &interpreter, Value &&value, auto &&lambda) +{ + if (auto collection = get_if(value)) { + std::vector flat; + for (auto i = 0u; i < collection->size(); ++i) { + Value v = Try(collection->index(interpreter, i)); + flat.push_back(Try(traverse(interpreter, std::move(v), lambda))); + } + return flat; + } + + std::visit([&lambda](T &value) { + if constexpr (requires { {lambda(value)}; }) { + lambda(value); + } + }, value.data); + return value; +} + +/// Set length (first argument) to all other arguments preserving their shape +static Result builtin_set_len(Interpreter &interpreter, std::vector args) +{ + if (auto len = get_if(args.front())) { + std::vector result; + for (auto &arg : std::span(args).subspan(1)) { + auto arg_result = Try(traverse(interpreter, std::move(arg), [&](Chord &c) { + for (Note ¬e : c.notes) { + note.length = *len; + } + })); + result.push_back(std::move(arg_result)); + } + return result; + } + + return errors::Unsupported_Types_For { + .type = errors::Unsupported_Types_For::Function, + .name = "set_len", + .possibilities = { + "TODO" + } + }; +} + +static Result builtin_duration(Interpreter &interpreter, std::vector args) +{ + auto total = Number{}; + for (auto &arg : args) { + Try(traverse(interpreter, std::move(arg), [&](Chord &c) { + for (Note ¬e : c.notes) { + if (note.length) { + total += *note.length; + } + } + })); + } + return total; +} + /// Join arguments into flat array static Result builtin_flat(Interpreter &i, std::vector args) { @@ -961,6 +1065,7 @@ void Interpreter::register_builtin_functions() global.force_define("ceil", apply_numeric_transform<&Number::ceil>); global.force_define("chord", builtin_chord); global.force_define("down", builtin_range); + global.force_define("duration", builtin_duration); global.force_define("flat", builtin_flat); global.force_define("floor", apply_numeric_transform<&Number::floor>); global.force_define("fold", builtin_fold); @@ -969,6 +1074,7 @@ void Interpreter::register_builtin_functions() global.force_define("if", builtin_if); global.force_define("instrument", builtin_program_change); global.force_define("len", builtin_len); + global.force_define("map", builtin_map); global.force_define("max", builtin_max); global.force_define("min", builtin_min); global.force_define("mix", builtin_mix); @@ -987,6 +1093,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_len", builtin_set_len); global.force_define("shuffle", builtin_shuffle); global.force_define("sim", builtin_sim); global.force_define("sort", builtin_sort); @@ -996,4 +1103,5 @@ void Interpreter::register_builtin_functions() global.force_define("unique", builtin_unique); global.force_define("up", builtin_range); global.force_define("update", builtin_update); + global.force_define("while", builtin_while); } diff --git a/musique/main.cc b/musique/main.cc index 234b88b..813464e 100644 --- a/musique/main.cc +++ b/musique/main.cc @@ -190,7 +190,7 @@ struct Runner if (std::next(it) != args.end()) std::cout << ' '; } - std::cout << '\n'; + std::cout << std::endl; return {}; }); }