From 70e1431f05ad2b2f3a192c3cf089a528ed8ba2e8 Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Wed, 25 May 2022 04:30:24 +0200 Subject: [PATCH] Begining of support of array programming --- Makefile | 2 +- examples/fib.mq | 1 + prelude.mq | 15 ++++ src/interpreter.cc | 180 ++++++++++++++++++++++++++++----------------- src/value.cc | 3 + 5 files changed, 132 insertions(+), 69 deletions(-) create mode 100644 examples/fib.mq create mode 100644 prelude.mq diff --git a/Makefile b/Makefile index c53ba30..efc6f1e 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ bin/musique: $(Release_Obj) bin/main.o src/*.hh lib/midi/libmidi-alsa.a bin/debug/musique: $(Debug_Obj) bin/debug/main.o src/*.hh @echo "CXX $@" - @$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/debug/main.o + @$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/debug/main.o $(LDFLAGS) $(LDLIBS) bin/debug/%.o: src/%.cc src/*.hh @echo "CXX $@" diff --git a/examples/fib.mq b/examples/fib.mq new file mode 100644 index 0000000..2910a13 --- /dev/null +++ b/examples/fib.mq @@ -0,0 +1 @@ +var fib = [n | if (n <= 1) [ n ] [ fib (n-1) + fib (n-2) ] ]; diff --git a/prelude.mq b/prelude.mq new file mode 100644 index 0000000..daf6190 --- /dev/null +++ b/prelude.mq @@ -0,0 +1,15 @@ +var iota = [n | + var iter = [start stop | + if (start >= stop) [[]] [flat start (iter (start+1) stop)] + ]; + iter 0 n +]; + +var map = [array fun| + var iter = [start stop | + if (start >= stop) [[]] [flat (fun (array.start)) (iter (start+1) stop)] + ]; + iter 0 (len array) +]; + +var cdur = c47; diff --git a/src/interpreter.cc b/src/interpreter.cc index 8fd9b43..c36b61f 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -5,52 +5,46 @@ #include template -constexpr auto binary_operator() +static Result binary_operator(Interpreter&, std::vector args) { - return [](Interpreter&, std::vector args) -> Result { - auto result = std::move(args.front()); - for (auto &v : std::span(args).subspan(1)) { - assert(result.type == Value::Type::Number, "LHS should be a number"); // TODO(assert) - assert(v.type == Value::Type::Number, "RHS should be a number"); // TODO(assert) - if constexpr (std::is_same_v>) { - result.n = Binary_Operation{}(std::move(result.n), std::move(v).n); - } else { - result.type = Value::Type::Bool; - result.b = Binary_Operation{}(std::move(result.n), std::move(v).n); - } + auto result = std::move(args.front()); + for (auto &v : std::span(args).subspan(1)) { + assert(result.type == Value::Type::Number, "LHS should be a number"); // TODO(assert) + assert(v.type == Value::Type::Number, "RHS should be a number"); // TODO(assert) + if constexpr (std::is_same_v>) { + result.n = Binary_Operation{}(std::move(result.n), std::move(v).n); + } else { + result.type = Value::Type::Bool; + result.b = Binary_Operation{}(std::move(result.n), std::move(v).n); } - return result; - }; + } + return result; } template -constexpr auto equality_operator() +static Result equality_operator(Interpreter&, std::vector args) { - return [](Interpreter&, std::vector args) -> Result { - assert(args.size() == 2, "(in)Equality only allows for 2 operands"); // TODO(assert) - return Value::from(Binary_Predicate{}(std::move(args.front()), std::move(args.back()))); - }; + assert(args.size() == 2, "(in)Equality only allows for 2 operands"); // TODO(assert) + return Value::from(Binary_Predicate{}(std::move(args.front()), std::move(args.back()))); } template -constexpr auto comparison_operator() +static Result comparison_operator(Interpreter&, std::vector args) { - return [](Interpreter&, std::vector args) -> Result { - assert(args.size() == 2, "Ordering only allows for 2 operands"); // TODO(assert) - assert(args.front().type == args.back().type, "Only values of the same type can be ordered"); // TODO(assert) + assert(args.size() == 2, "Ordering only allows for 2 operands"); // TODO(assert) + assert(args.front().type == args.back().type, "Only values of the same type can be ordered"); // TODO(assert) - switch (args.front().type) { - case Value::Type::Number: - return Value::from(Binary_Predicate{}(std::move(args.front()).n, std::move(args.back()).n)); + switch (args.front().type) { + case Value::Type::Number: + return Value::from(Binary_Predicate{}(std::move(args.front()).n, std::move(args.back()).n)); - case Value::Type::Bool: - return Value::from(Binary_Predicate{}(std::move(args.front()).b, std::move(args.back()).b)); + case Value::Type::Bool: + return Value::from(Binary_Predicate{}(std::move(args.front()).b, std::move(args.back()).b)); - default: - assert(false, "Cannot compare value of given types"); // TODO(assert) - } - unreachable(); - }; + default: + assert(false, "Cannot compare value of given types"); // TODO(assert) + } + unreachable(); } /// Registers constants like `fn = full note = 1/1` @@ -118,40 +112,93 @@ static inline Result create_chord(std::vector &chord, Interpreter &i return {}; } +template +static inline Result play_notes(Interpreter &interpreter, T args) +{ + for (auto i = 0u; i < args.size(); ++i) { + Value arg; + if constexpr (With_Index_Method) { + arg = Try(args.index(interpreter, i)); + } else { + arg = std::move(args[i]); + } + + switch (arg.type) { + case Value::Type::Array: + case Value::Type::Block: + Try(play_notes(interpreter, std::move(arg))); + break; + + case Value::Type::Music: + interpreter.play(arg.chord); + break; + + default: + assert(false, "this type does not support playing"); + } + } + + return {}; +} + +constexpr bool is_indexable(Value::Type type) +{ + return type == Value::Type::Array || type == Value::Type::Block; +} + /// Creates implementation of plus/minus operator that support following operations: /// number, number -> number (standard math operations) /// n: number, m: music -> music /// m: music, n: number -> music moves m by n semitones (+ goes up, - goes down) template -[[gnu::always_inline]] -static inline auto plus_minus_operator() +static Result plus_minus_operator(Interpreter &interpreter, std::vector args) { - return [](Interpreter&, std::vector args) -> Result { - assert(args.size() == 2, "Binary operator only accepts 2 arguments"); - auto lhs = std::move(args.front()); - auto rhs = std::move(args.back()); + assert(args.size() == 2, "Binary operator only accepts 2 arguments"); + auto lhs = std::move(args.front()); + auto rhs = std::move(args.back()); - if (lhs.type == rhs.type && lhs.type == Value::Type::Number) { - return Value::from(Binary_Operation{}(std::move(lhs).n, std::move(rhs).n)); + if (lhs.type == rhs.type && lhs.type == Value::Type::Number) { + return Value::from(Binary_Operation{}(std::move(lhs).n, std::move(rhs).n)); + } + + if (lhs.type == Value::Type::Music && rhs.type == Value::Type::Number) { + for (auto ¬e : lhs.chord.notes) { + note.base = Binary_Operation{}(note.base, rhs.n.as_int()); + note.simplify_inplace(); } + return lhs; + } - if (lhs.type == Value::Type::Music && rhs.type == Value::Type::Number) { -music_number_operation: - for (auto ¬e : lhs.chord.notes) { - note.base = Binary_Operation{}(note.base, rhs.n.as_int()); - note.simplify_inplace(); - } - return lhs; + if (lhs.type == Value::Type::Number && rhs.type == Value::Type::Music) { + for (auto ¬e : rhs.chord.notes) { + note.base = Binary_Operation{}(lhs.n.as_int(), note.base); + note.simplify_inplace(); } + return rhs; + } - if (lhs.type == Value::Type::Number && rhs.type == Value::Type::Music) { - std::swap(lhs, rhs); - goto music_number_operation; + if (is_indexable(lhs.type) && !is_indexable(rhs.type)) { + Array array; + for (auto i = 0u; i < lhs.size(); ++i) { + array.elements.push_back(Try( + plus_minus_operator( + interpreter, { Try(lhs.index(interpreter, i)), rhs }))); } + return Value::from(std::move(array)); + } - assert(false, "Unsupported types for this operation"); // TODO(assert) - unreachable(); - }; + if (!is_indexable(lhs.type) && is_indexable(rhs.type)) { + Array array; + for (auto i = 0u; i < rhs.size(); ++i) { + array.elements.push_back(Try( + plus_minus_operator( + interpreter, { lhs, Try(rhs.index(interpreter, i)) }))); + } + return Value::from(std::move(array)); + } + + assert(false, "Unsupported types for this operation"); // TODO(assert) + unreachable(); } Interpreter::Interpreter() @@ -191,10 +238,7 @@ Interpreter::Interpreter() }); global.force_define("play", +[](Interpreter &i, std::vector args) -> Result { - for (auto &arg : args) { - assert(arg.type == Value::Type::Music, "Only music values can be played"); // TODO(assert) - i.play(arg.chord); - } + Try(play_notes(i, std::move(args))); return Value{}; }); @@ -225,18 +269,18 @@ Interpreter::Interpreter() return Value::from(std::move(chord)); }); - operators["+"] = plus_minus_operator>(); - operators["-"] = plus_minus_operator>(); - operators["*"] = binary_operator>(); - operators["/"] = binary_operator>(); + operators["+"] = plus_minus_operator>; + operators["-"] = plus_minus_operator>; + operators["*"] = binary_operator>; + operators["/"] = binary_operator>; - operators["<"] = comparison_operator>(); - operators[">"] = comparison_operator>(); - operators["<="] = comparison_operator>(); - operators[">="] = comparison_operator>(); + operators["<"] = comparison_operator>; + operators[">"] = comparison_operator>; + operators["<="] = comparison_operator>; + operators[">="] = comparison_operator>; - operators["=="] = equality_operator>(); - operators["!="] = equality_operator>(); + operators["=="] = equality_operator>; + operators["!="] = equality_operator>; operators["."] = +[](Interpreter &i, std::vector args) -> Result { assert(args.size() == 2, "Operator . requires two arguments"); // TODO(assert) diff --git a/src/value.cc b/src/value.cc index e563afe..3010d3d 100644 --- a/src/value.cc +++ b/src/value.cc @@ -202,6 +202,9 @@ Result Value::index(Interpreter &i, unsigned position) case Type::Block: return blk.index(i, position); + case Type::Array: + return array.index(i, position); + default: assert(false, "Block indexing is not supported for this type"); // TODO(assert) }