diff --git a/src/interpreter.cc b/src/interpreter.cc index 74f6a70..a89d415 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -112,7 +112,7 @@ static Result plus_minus_operator(Interpreter &interpreter, std::vector -static Result binary_operator(Interpreter&, std::vector args) +static Result binary_operator(Interpreter& interpreter, std::vector args) { using NN = Shape; @@ -121,6 +121,10 @@ static Result binary_operator(Interpreter&, std::vector args) return Value::from(Binary_Operation{}(lhs, rhs)); } + if (may_be_vectorized(args)) { + return vectorize(binary_operator, interpreter, args); + } + unreachable(); } diff --git a/src/tests/context.cc b/src/tests/context.cc index 8c3e192..1adb45d 100644 --- a/src/tests/context.cc +++ b/src/tests/context.cc @@ -20,20 +20,33 @@ void test_seconds_compute( } suite context_suite = [] { - "Context duration resolution"_test = [] { - test_seconds_compute(60, Number(2,1), 8); - test_seconds_compute(60, Number(1,1), 4); - test_seconds_compute(60, Number(1,2), 2); - test_seconds_compute(60, Number(1,4), 1); + "Context"_test = [] { + should("resolve note duration length to seconds") = [] { + test_seconds_compute(60, Number(2,1), 8); + test_seconds_compute(60, Number(1,1), 4); + test_seconds_compute(60, Number(1,2), 2); + test_seconds_compute(60, Number(1,4), 1); - test_seconds_compute(96, Number(2,1), 5); - test_seconds_compute(96, Number(1,1), 2.5); - test_seconds_compute(96, Number(1,2), 1.25); - test_seconds_compute(96, Number(1,4), 0.625); + test_seconds_compute(96, Number(2,1), 5); + test_seconds_compute(96, Number(1,1), 2.5); + test_seconds_compute(96, Number(1,2), 1.25); + test_seconds_compute(96, Number(1,4), 0.625); - test_seconds_compute(120, Number(2,1), 4); - test_seconds_compute(120, Number(1,1), 2); - test_seconds_compute(120, Number(1,2), 1); - test_seconds_compute(120, Number(1,4), 0.5); + test_seconds_compute(120, Number(2,1), 4); + test_seconds_compute(120, Number(1,1), 2); + test_seconds_compute(120, Number(1,2), 1); + test_seconds_compute(120, Number(1,4), 0.5); + }; + + should("fill notes with default context values") = [] { + Context ctx; + ctx.length = Number(1, 42); + ctx.octave = 8; + + expect(eq(ctx.fill({ .base = 0 }), Note { .base = 0, .octave = 8, .length = Number(1, 42) })); + expect(eq(ctx.fill({ .base = 0, .length = Number(4) }), Note { .base = 0, .octave = 8, .length = Number(4) })); + expect(eq(ctx.fill({ .base = 0, .octave = 1 }), Note { .base = 0, .octave = 1, .length = Number(1, 42) })); + expect(eq(ctx.fill({ .base = 0, .octave = 1, .length = Number(4) }), Note { .base = 0, .octave = 1, .length = Number(4) })); + }; }; }; diff --git a/src/tests/interpreter.cc b/src/tests/interpreter.cc index cec0119..66feff7 100644 --- a/src/tests/interpreter.cc +++ b/src/tests/interpreter.cc @@ -36,10 +36,10 @@ void expect_alternative( suite intepreter_test = [] { "Interpreter"_test = [] { should("evaluate literals") = [] { - evaluates_to(Value::from(false), "false"); - evaluates_to(Value::from(true), "true"); + evaluates_to(Value::from(false), "false"); + evaluates_to(Value::from(true), "true"); evaluates_to(Value::from(Number(10)), "10"); - evaluates_to(Value{}, "nil"); + evaluates_to(Value{}, "nil"); }; should("evaluate arithmetic") = [] { @@ -104,5 +104,76 @@ suite intepreter_test = [] { should("allow assigning result of function calls to a variable") = [] { evaluates_to(Value::from(Number(42)), "var x = [i|i] 42; x"); }; + + should("support array programming") = [] { + evaluates_to(Value::from(Array { .elements = {{ + Value::from(Number(2)), + Value::from(Number(4)), + Value::from(Number(6)), + Value::from(Number(8)) + }}}), "2 * [1;2;3;4]"); + + evaluates_to(Value::from(Array { .elements = {{ + Value::from(Note { .base = 1 }), + Value::from(Note { .base = 2 }), + Value::from(Note { .base = 3 }), + Value::from(Note { .base = 4 }) + }}}), "c + [1;2;3;4]"); + }; + + should("support number - music operations") = [] { + evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 }, Note { .base = 3 }, Note { .base = 6 } } }), + "c#47 - 1"); + + evaluates_to(Value::from(Chord { .notes = { Note { .base = 2 }, Note { .base = 6 }, Note { .base = 9 } } }), + "c47 + 2"); + + evaluates_to(Value::from(Chord { .notes = { Note { .base = 2 }, Note { .base = 6 }, Note { .base = 9 } } }), + "2 + c47"); + }; + + should("support direct chord creation") = [] { + evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 }, Note { .base = 4 }, Note { .base = 7 } } }), + "chord [c;e;g]"); + + evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 }, Note { .base = 4 }, Note { .base = 7 } } }), + "chord c e g"); + + evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 }, Note { .base = 4 }, Note { .base = 7 } } }), + "chord [c;e] g"); + }; + + should("support if builtin") = [] { + evaluates_to(Value::from(Number(10)), "if true [10] [20]"); + evaluates_to(Value::from(Number(20)), "if false [10] [20]"); + evaluates_to(Value{}, "if false [10]"); + }; + + should("support eager array creation") = [] { + evaluates_to(Value::from(Array { .elements = {{ Value::from(Number(10)), Value::from(Number(20)), Value::from(Number(30)) }}}), + "flat 10 20 30"); + + evaluates_to(Value::from(Array { .elements = {{ Value::from(Number(10)), Value::from(Number(20)), Value::from(Number(30)) }}}), + "flat [10;20] 30"); + + evaluates_to(Value::from(Array { .elements = {{ Value::from(Number(10)), Value::from(Number(20)), Value::from(Number(30)) }}}), + "flat (flat 10 20) 30"); + }; + + should("support indexing") = [] { + evaluates_to(Value::from(Number(10)), "[5;10;15].1"); + evaluates_to(Value::from(Number(10)), "(flat 5 10 15).1"); + }; + + should("support size") = [] { + evaluates_to(Value::from(Number(3)), "len [5;10;15]"); + evaluates_to(Value::from(Number(3)), "len (flat 5 10 15)"); + }; + + should("support call like octave and len setting") = [] { + evaluates_to(Value::from(Chord { .notes = {{ Note { .base = 0, .octave = 5, .length = Number(10) }}}}), "c 5 10"); + evaluates_to(Value::from(Chord { .notes = {{ Note { .base = 0, .octave = 5, .length = Number(10) }}}}), "(c 4 8) 5 10"); + evaluates_to(Value::from(Chord { .notes = {{ Note { .base = 0, .octave = 5 }}}}), "c 5"); + }; }; }; diff --git a/src/tests/value.cc b/src/tests/value.cc index 32a451f..f7d91bc 100644 --- a/src/tests/value.cc +++ b/src/tests/value.cc @@ -67,6 +67,24 @@ static void test_note_resolution( } } +static void test_note_resolution( + std::string_view name, + std::string_view expected = {}, + reflection::source_location sl = reflection::source_location::current()) +{ + if (expected.empty()) + expected = name; + + auto const maybe_note = Note::from(name); + expect(maybe_note.has_value(), sl) << "Note::from didn't recognized " << name << " as a note"; + if (maybe_note) { + auto note = *maybe_note; + std::stringstream ss; + ss << note; + expect(eq(expected, ss.str())) << "Different string representation then expected"; + } +} + suite value_test = [] { "Value"_test = [] { should("be properly created using Value::from") = [] { @@ -151,5 +169,20 @@ suite value_test = [] { test_note_resolution("c##########", 70); test_note_resolution("cbbbbbbbbbb", 50); }; + + should("Preserve note when printing") = [] { + test_note_resolution("c"); + test_note_resolution("c#"); + test_note_resolution("d"); + test_note_resolution("d#"); + test_note_resolution("e"); + test_note_resolution("f"); + test_note_resolution("f#"); + test_note_resolution("g"); + test_note_resolution("g#"); + test_note_resolution("a"); + test_note_resolution("a#"); + test_note_resolution("b"); + }; }; };