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]
|
## [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
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
|
-- 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
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 |
|
list_all_permutations := ( array |
|
||||||
|
@ -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 ¬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
|
/// 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);
|
||||||
}
|
}
|
||||||
|
@ -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 {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user