Overhaul of chord call semantics to support easy sequencing

This commit is contained in:
Robert Bendun 2022-09-17 13:10:24 +02:00
parent 98a88c3acc
commit 676f41909f
3 changed files with 58 additions and 25 deletions

View File

@ -2,12 +2,12 @@ var C = chord (c 3 1) (g 3 1) (c 4 1);
var G = chord (g 3 1) (d 4 1) (g 4 1); var G = chord (g 3 1) (d 4 1) (g 4 1);
oct 5; oct 5;
par (C) e e f g; par C e e f g;
par (G) g f e d; par G g f e d;
par (C) c c d e; par C c c d e;
par (G) (e 5 (3/8)) (d 5 (1/8)) (d 5 (1/2)); par G (e 5 (3/8) d 5 (1/8) d 5 (1/2));
par (C) e e f g; par C e e f g;
par (G) g f e d; par G g f e d;
par (C) c c d e; par C c c d e;
par (G) (d 5 (1/2)) (c 5 (1/8)); par G (d 5 (1/2) c 5 (1/8));
par (C) (c 5 1); par C (c 5 1);

View File

@ -811,6 +811,9 @@ struct Chord
static Chord from(std::string_view source); static Chord from(std::string_view source);
bool operator==(Chord const&) const = default; bool operator==(Chord const&) const = default;
/// Fill length and octave or sequence multiple chords
Result<Value> operator()(Interpreter &i, std::vector<Value> args);
}; };
std::ostream& operator<<(std::ostream& os, Chord const& chord); std::ostream& operator<<(std::ostream& os, Chord const& chord);

View File

@ -161,22 +161,7 @@ Result<Value> Value::operator()(Interpreter &i, std::vector<Value> args)
switch (type) { switch (type) {
case Type::Intrinsic: return intr(i, std::move(args)); case Type::Intrinsic: return intr(i, std::move(args));
case Type::Block: return blk(i, std::move(args)); case Type::Block: return blk(i, std::move(args));
case Type::Music: case Type::Music: return chord(i, std::move(args));
{
assert(args.size() == 1 || args.size() == 2, "music value can be called only in form note <octave> [<length>]"); // TODO(assert)
assert(args[0].type == Type::Number, "expected octave to be a number"); // TODO(assert)
assert(args.size() == 2 ? args[1].type == Type::Number : true, "expected length to be a number"); // TODO(assert)
for (auto &note : chord.notes) {
note.octave = args[0].n.as_int();
if (args.size() == 2) {
note.length = args[1].n;
}
note.simplify_inplace();
}
return *this;
}
default: default:
return Error { return Error {
.details = errors::Not_Callable { .type = type_name(type) }, .details = errors::Not_Callable { .type = type_name(type) },
@ -509,6 +494,51 @@ Chord Chord::from(std::string_view source)
return chord; return chord;
} }
Result<Value> Chord::operator()(Interpreter&, std::vector<Value> args)
{
std::vector<Value> array;
Chord *current = this;
enum State {
Waiting_For_Octave,
Waiting_For_Length,
Waiting_For_Note
} state = Waiting_For_Octave;
for (auto &arg : args) {
if (arg.type == Value::Type::Number) {
switch (state) {
break; case Waiting_For_Octave:
std::for_each(current->notes.begin(), current->notes.end(),
[&arg](Note &n) { n.octave = arg.n.floor().as_int(); });
state = Waiting_For_Length;
continue;
break; case Waiting_For_Length:
std::for_each(current->notes.begin(), current->notes.end(),
[&arg](Note &n) { n.length = arg.n; });
state = Waiting_For_Note;
continue;
default:
unimplemented();
}
}
if (arg.type == Value::Type::Music) {
if (array.empty()) {
array.push_back(Value::from(std::move(*current)));
}
array.push_back(std::move(arg));
current = &array.back().chord;
state = Waiting_For_Octave;
}
}
return array.empty()
? Value::from(*current)
: Value::from(Array{array});
}
std::ostream& operator<<(std::ostream& os, Chord const& chord) std::ostream& operator<<(std::ostream& os, Chord const& chord)
{ {
if (chord.notes.size() == 1) { if (chord.notes.size() == 1) {