new builtins: while, map, duration, set_len

This commit is contained in:
Robert Bendun 2022-10-27 00:27:41 +02:00
parent b612209914
commit 5f0f511066
7 changed files with 179 additions and 23 deletions

View File

@ -7,9 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added
- new builtins: map, while, set_len, duration
### Changed ### Changed
- Block can be called with more parameters then it requires - 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 ## [0.2.1] - 2022-10-21

13
examples/control-flow.mq Normal file
View File

@ -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;
);

View File

@ -19,7 +19,7 @@ factorial_iterative := (n |
); );
-- Calculate factorial using composition of functions -- 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 -- Gather all functions into array, and iterate over it
-- This allows to reduce repeatition of this test case -- This allows to reduce repeatition of this test case

25
examples/noises.mq Normal file
View File

@ -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;
);

View File

@ -1,4 +1,4 @@
factorial := (n | fold (1 + up n) '*); factorial := (n | fold '* (1 + up n));
list_all_permutations := ( array | list_all_permutations := ( array |

View File

@ -525,35 +525,57 @@ static Result<Value> builtin_fold(Interpreter &interpreter, std::vector<Value> a
constexpr auto guard = Guard<2> { constexpr auto guard = Guard<2> {
.name = "fold", .name = "fold",
.possibilities = { .possibilities = {
"(array, callback) -> any", "(callback, ...values) -> any",
"(array, init, callback) -> any"
} }
}; };
Value array, init, callback; if (args.size()) {
switch (args.size()) { if (auto p = get_if<Function>(args.front())) {
break; case 2: auto xs = Try(flatten(interpreter, std::span(args).subspan(1)));
array = std::move(args[0]); if (xs.empty()) {
callback = std::move(args[1]); return Value{};
if (array.size() != 0) { }
init = Try(array.index(interpreter, 0)); 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]); return guard.yield_error();
callback = std::move(args[2]); }
break; default:
static Result<Value> builtin_map(Interpreter &interpreter, std::vector<Value> args)
{
static constexpr auto guard = Guard<2> {
.name = "fold",
.possibilities = {
"(callback, array) -> any",
"(callback, array, init) -> any"
}
};
if (args.empty()) {
return guard.yield_error(); return guard.yield_error();
} }
auto collection = Try(guard.match<Collection>(array)); auto function = Try(guard.match<Function>(args.front()));
auto function = Try(guard.match<Function>(callback));
for (auto i = 0u; i < collection->size(); ++i) { std::vector<Value> result;
auto element = Try(collection->index(interpreter, i));
init = Try((*function)(interpreter, { std::move(init), std::move(element) })); for (auto &arg : std::span(args).subspan(1)) {
if (auto collection = get_if<Collection>(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 /// Scan computes inclusive prefix sum
@ -601,6 +623,29 @@ static Result<Value> builtin_if(Interpreter &i, std::span<Ast> args) {
return Value{}; return Value{};
} }
/// Loop block depending on condition
static Result<Value> builtin_while(Interpreter &i, std::span<Ast> 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 /// Try executing all but last block and if it fails execute last one
static Result<Value> builtin_try(Interpreter &interpreter, std::vector<Value> args) static Result<Value> builtin_try(Interpreter &interpreter, std::vector<Value> args)
{ {
@ -680,6 +725,65 @@ static Result<Value> builtin_len(Interpreter &i, std::vector<Value> args)
return ctx_read_write_property<&Context::length>(i, std::move(args)); return ctx_read_write_property<&Context::length>(i, std::move(args));
} }
Result<Value> traverse(Interpreter &interpreter, Value &&value, auto &&lambda)
{
if (auto collection = get_if<Collection>(value)) {
std::vector<Value> 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]<typename T>(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<Value> builtin_set_len(Interpreter &interpreter, std::vector<Value> args)
{
if (auto len = get_if<Number>(args.front())) {
std::vector<Value> result;
for (auto &arg : std::span(args).subspan(1)) {
auto arg_result = Try(traverse(interpreter, std::move(arg), [&](Chord &c) {
for (Note &note : 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<Value> builtin_duration(Interpreter &interpreter, std::vector<Value> args)
{
auto total = Number{};
for (auto &arg : args) {
Try(traverse(interpreter, std::move(arg), [&](Chord &c) {
for (Note &note : c.notes) {
if (note.length) {
total += *note.length;
}
}
}));
}
return total;
}
/// Join arguments into flat array /// Join arguments into flat array
static Result<Value> builtin_flat(Interpreter &i, std::vector<Value> args) static Result<Value> builtin_flat(Interpreter &i, std::vector<Value> args)
{ {
@ -961,6 +1065,7 @@ void Interpreter::register_builtin_functions()
global.force_define("ceil", apply_numeric_transform<&Number::ceil>); global.force_define("ceil", apply_numeric_transform<&Number::ceil>);
global.force_define("chord", builtin_chord); global.force_define("chord", builtin_chord);
global.force_define("down", builtin_range<Range_Direction::Down>); global.force_define("down", builtin_range<Range_Direction::Down>);
global.force_define("duration", builtin_duration);
global.force_define("flat", builtin_flat); global.force_define("flat", builtin_flat);
global.force_define("floor", apply_numeric_transform<&Number::floor>); global.force_define("floor", apply_numeric_transform<&Number::floor>);
global.force_define("fold", builtin_fold); global.force_define("fold", builtin_fold);
@ -969,6 +1074,7 @@ void Interpreter::register_builtin_functions()
global.force_define("if", builtin_if); global.force_define("if", builtin_if);
global.force_define("instrument", builtin_program_change); global.force_define("instrument", builtin_program_change);
global.force_define("len", builtin_len); global.force_define("len", builtin_len);
global.force_define("map", builtin_map);
global.force_define("max", builtin_max); global.force_define("max", builtin_max);
global.force_define("min", builtin_min); global.force_define("min", builtin_min);
global.force_define("mix", builtin_mix); global.force_define("mix", builtin_mix);
@ -987,6 +1093,7 @@ void Interpreter::register_builtin_functions()
global.force_define("rotate", builtin_rotate); global.force_define("rotate", builtin_rotate);
global.force_define("round", apply_numeric_transform<&Number::round>); global.force_define("round", apply_numeric_transform<&Number::round>);
global.force_define("scan", builtin_scan); global.force_define("scan", builtin_scan);
global.force_define("set_len", builtin_set_len);
global.force_define("shuffle", builtin_shuffle); global.force_define("shuffle", builtin_shuffle);
global.force_define("sim", builtin_sim); global.force_define("sim", builtin_sim);
global.force_define("sort", builtin_sort); global.force_define("sort", builtin_sort);
@ -996,4 +1103,5 @@ void Interpreter::register_builtin_functions()
global.force_define("unique", builtin_unique); global.force_define("unique", builtin_unique);
global.force_define("up", builtin_range<Range_Direction::Up>); global.force_define("up", builtin_range<Range_Direction::Up>);
global.force_define("update", builtin_update); global.force_define("update", builtin_update);
global.force_define("while", builtin_while);
} }

View File

@ -190,7 +190,7 @@ struct Runner
if (std::next(it) != args.end()) if (std::next(it) != args.end())
std::cout << ' '; std::cout << ' ';
} }
std::cout << '\n'; std::cout << std::endl;
return {}; return {};
}); });
} }