new builtins: while, map, duration, set_len
This commit is contained in:
parent
b612209914
commit
5f0f511066
10
CHANGELOG.md
10
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
|
||||
|
||||
|
13
examples/control-flow.mq
Normal file
13
examples/control-flow.mq
Normal 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;
|
||||
);
|
||||
|
@ -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
|
||||
|
25
examples/noises.mq
Normal file
25
examples/noises.mq
Normal 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;
|
||||
);
|
@ -1,4 +1,4 @@
|
||||
factorial := (n | fold (1 + up n) '*);
|
||||
factorial := (n | fold '* (1 + up n));
|
||||
|
||||
|
||||
list_all_permutations := ( array |
|
||||
|
@ -525,35 +525,57 @@ static Result<Value> builtin_fold(Interpreter &interpreter, std::vector<Value> 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<Function>(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<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();
|
||||
}
|
||||
|
||||
auto collection = Try(guard.match<Collection>(array));
|
||||
auto function = Try(guard.match<Function>(callback));
|
||||
auto function = Try(guard.match<Function>(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<Value> result;
|
||||
|
||||
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
|
||||
@ -601,6 +623,29 @@ static Result<Value> builtin_if(Interpreter &i, std::span<Ast> args) {
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
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 ¬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<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 ¬e : c.notes) {
|
||||
if (note.length) {
|
||||
total += *note.length;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/// Join arguments into flat array
|
||||
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("chord", builtin_chord);
|
||||
global.force_define("down", builtin_range<Range_Direction::Down>);
|
||||
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<Range_Direction::Up>);
|
||||
global.force_define("update", builtin_update);
|
||||
global.force_define("while", builtin_while);
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ struct Runner
|
||||
if (std::next(it) != args.end())
|
||||
std::cout << ' ';
|
||||
}
|
||||
std::cout << '\n';
|
||||
std::cout << std::endl;
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user