Made context stack a tree; fixed function name in error printing
This commit is contained in:
parent
a32bfa24d3
commit
7f73f3dcad
@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- release build script was producing executable with wrong path
|
||||||
|
- `examples/for-elise.mq` had bug in it
|
||||||
|
- in error reporting printing garbage instead of function name was fixed
|
||||||
|
|
||||||
|
## [0.3.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- new builtins: map, while, set_len, set_oct, duration, pick
|
- new builtins: map, while, set_len, set_oct, duration, pick
|
||||||
|
@ -97,7 +97,7 @@ namespace errors
|
|||||||
enum Type { Operator, Function } type;
|
enum Type { Operator, Function } type;
|
||||||
|
|
||||||
/// Name of operation
|
/// Name of operation
|
||||||
std::string_view name;
|
std::string name;
|
||||||
|
|
||||||
/// Possible ways to use it correctly
|
/// Possible ways to use it correctly
|
||||||
std::vector<std::string> possibilities;
|
std::vector<std::string> possibilities;
|
||||||
|
@ -87,15 +87,15 @@ static Result<Value> ctx_read_write_property(Interpreter &interpreter, std::vect
|
|||||||
using Member_Type = std::remove_cvref_t<decltype(std::declval<Context>().*(Mem_Ptr))>;
|
using Member_Type = std::remove_cvref_t<decltype(std::declval<Context>().*(Mem_Ptr))>;
|
||||||
|
|
||||||
if (args.size() == 0) {
|
if (args.size() == 0) {
|
||||||
return Number(interpreter.context_stack.back().*(Mem_Ptr));
|
return Number((*interpreter.current_context).*(Mem_Ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure(std::holds_alternative<Number>(args.front().data), "Ctx only holds numeric values");
|
ensure(std::holds_alternative<Number>(args.front().data), "Ctx only holds numeric values");
|
||||||
|
|
||||||
if constexpr (std::is_same_v<Member_Type, Number>) {
|
if constexpr (std::is_same_v<Member_Type, Number>) {
|
||||||
interpreter.context_stack.back().*(Mem_Ptr) = std::get<Number>(args.front().data);
|
(*interpreter.current_context).*(Mem_Ptr) = std::get<Number>(args.front().data);
|
||||||
} else {
|
} else {
|
||||||
interpreter.context_stack.back().*(Mem_Ptr) = static_cast<Member_Type>(
|
(*interpreter.current_context).*(Mem_Ptr) = static_cast<Member_Type>(
|
||||||
std::get<Number>(args.front().data).as_int()
|
std::get<Number>(args.front().data).as_int()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -266,19 +266,20 @@ static std::optional<Error> action_play(Interpreter &i, Value v)
|
|||||||
|
|
||||||
/// Play notes
|
/// Play notes
|
||||||
template<With_Index_Operator Container = std::vector<Value>>
|
template<With_Index_Operator Container = std::vector<Value>>
|
||||||
static inline Result<Value> builtin_play(Interpreter &i, Container args)
|
static inline Result<Value> builtin_play(Interpreter &interpreter, Container args)
|
||||||
{
|
{
|
||||||
Try(ensure_midi_connection_available(i, "play"));
|
Try(ensure_midi_connection_available(interpreter, "play"));
|
||||||
auto previous_action = std::exchange(i.default_action, action_play);
|
auto const previous_action = std::exchange(interpreter.default_action, action_play);
|
||||||
i.context_stack.push_back(i.context_stack.back());
|
auto const previous_context = std::exchange(interpreter.current_context,
|
||||||
|
std::make_shared<Context>(*interpreter.current_context));
|
||||||
|
|
||||||
auto const finally = [&] {
|
auto const finally = [&] {
|
||||||
i.default_action = std::move(previous_action);
|
interpreter.default_action = std::move(previous_action);
|
||||||
i.context_stack.pop_back();
|
interpreter.current_context = previous_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto &el : args) {
|
for (auto &el : args) {
|
||||||
if (std::optional<Error> error = sequential_play(i, std::move(el))) {
|
if (std::optional<Error> error = sequential_play(interpreter, std::move(el))) {
|
||||||
finally();
|
finally();
|
||||||
return *std::move(error);
|
return *std::move(error);
|
||||||
}
|
}
|
||||||
@ -289,19 +290,19 @@ static inline Result<Value> builtin_play(Interpreter &i, Container args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Play first argument while playing all others
|
/// Play first argument while playing all others
|
||||||
static Result<Value> builtin_par(Interpreter &i, std::vector<Value> args) {
|
static Result<Value> builtin_par(Interpreter &interpreter, std::vector<Value> args) {
|
||||||
Try(ensure_midi_connection_available(i, "par"));
|
Try(ensure_midi_connection_available(interpreter, "par"));
|
||||||
|
|
||||||
ensure(args.size() >= 1, "par only makes sense for at least one argument"); // TODO(assert)
|
ensure(args.size() >= 1, "par only makes sense for at least one argument"); // TODO(assert)
|
||||||
if (args.size() == 1) {
|
if (args.size() == 1) {
|
||||||
auto chord = get_if<Chord>(args.front());
|
auto chord = get_if<Chord>(args.front());
|
||||||
ensure(chord, "Par expects music value as first argument"); // TODO(assert)
|
ensure(chord, "Par expects music value as first argument"); // TODO(assert)
|
||||||
Try(i.play(std::move(*chord)));
|
Try(interpreter.play(std::move(*chord)));
|
||||||
return Value{};
|
return Value{};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create chord that should sustain during playing of all other notes
|
// Create chord that should sustain during playing of all other notes
|
||||||
auto &ctx = i.context_stack.back();
|
auto &ctx = *interpreter.current_context;
|
||||||
auto chord = get_if<Chord>(args.front());
|
auto chord = get_if<Chord>(args.front());
|
||||||
ensure(chord, "par expects music value as first argument"); // TODO(assert)
|
ensure(chord, "par expects music value as first argument"); // TODO(assert)
|
||||||
|
|
||||||
@ -309,15 +310,15 @@ static Result<Value> builtin_par(Interpreter &i, std::vector<Value> args) {
|
|||||||
|
|
||||||
for (auto const& note : chord->notes) {
|
for (auto const& note : chord->notes) {
|
||||||
if (note.base) {
|
if (note.base) {
|
||||||
i.midi_connection->send_note_on(0, *note.into_midi_note(), 127);
|
interpreter.midi_connection->send_note_on(0, *note.into_midi_note(), 127);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = builtin_play(i, std::span(args).subspan(1));
|
auto result = builtin_play(interpreter, std::span(args).subspan(1));
|
||||||
|
|
||||||
for (auto const& note : chord->notes) {
|
for (auto const& note : chord->notes) {
|
||||||
if (note.base) {
|
if (note.base) {
|
||||||
i.midi_connection->send_note_off(0, *note.into_midi_note(), 127);
|
interpreter.midi_connection->send_note_off(0, *note.into_midi_note(), 127);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -386,7 +387,7 @@ static Result<Value> builtin_sim(Interpreter &interpreter, std::vector<Value> ar
|
|||||||
};
|
};
|
||||||
std::vector<Instruction> schedule;
|
std::vector<Instruction> schedule;
|
||||||
|
|
||||||
auto const& ctx = interpreter.context_stack.back();
|
auto const& ctx = *interpreter.current_context;
|
||||||
|
|
||||||
for (auto const& track : tracks) {
|
for (auto const& track : tracks) {
|
||||||
auto passed_time = Number(0);
|
auto passed_time = Number(0);
|
||||||
@ -997,19 +998,19 @@ static Result<Value> builtin_chord(Interpreter &i, std::vector<Value> args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Send MIDI message Note On
|
/// Send MIDI message Note On
|
||||||
static Result<Value> builtin_note_on(Interpreter &i, std::vector<Value> args)
|
static Result<Value> builtin_note_on(Interpreter &interpreter, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
if (auto a = match<Number, Number, Number>(args)) {
|
if (auto a = match<Number, Number, Number>(args)) {
|
||||||
auto [chan, note, vel] = *a;
|
auto [chan, note, vel] = *a;
|
||||||
i.midi_connection->send_note_on(chan.as_int(), note.as_int(), vel.as_int());
|
interpreter.midi_connection->send_note_on(chan.as_int(), note.as_int(), vel.as_int());
|
||||||
return Value {};
|
return Value {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto a = match<Number, Chord, Number>(args)) {
|
if (auto a = match<Number, Chord, Number>(args)) {
|
||||||
auto [chan, chord, vel] = *a;
|
auto [chan, chord, vel] = *a;
|
||||||
for (auto note : chord.notes) {
|
for (auto note : chord.notes) {
|
||||||
note = i.context_stack.back().fill(note);
|
note = interpreter.current_context->fill(note);
|
||||||
i.midi_connection->send_note_on(chan.as_int(), *note.into_midi_note(), vel.as_int());
|
interpreter.midi_connection->send_note_on(chan.as_int(), *note.into_midi_note(), vel.as_int());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1027,11 +1028,11 @@ static Result<Value> builtin_note_on(Interpreter &i, std::vector<Value> args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Send MIDI message Note Off
|
/// Send MIDI message Note Off
|
||||||
static Result<Value> builtin_note_off(Interpreter &i, std::vector<Value> args)
|
static Result<Value> builtin_note_off(Interpreter &interpreter, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
if (auto a = match<Number, Number>(args)) {
|
if (auto a = match<Number, Number>(args)) {
|
||||||
auto [chan, note] = *a;
|
auto [chan, note] = *a;
|
||||||
i.midi_connection->send_note_off(chan.as_int(), note.as_int(), 127);
|
interpreter.midi_connection->send_note_off(chan.as_int(), note.as_int(), 127);
|
||||||
return Value {};
|
return Value {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1039,8 +1040,8 @@ static Result<Value> builtin_note_off(Interpreter &i, std::vector<Value> args)
|
|||||||
auto& [chan, chord] = *a;
|
auto& [chan, chord] = *a;
|
||||||
|
|
||||||
for (auto note : chord.notes) {
|
for (auto note : chord.notes) {
|
||||||
note = i.context_stack.back().fill(note);
|
note = interpreter.current_context->fill(note);
|
||||||
i.midi_connection->send_note_off(chan.as_int(), *note.into_midi_note(), 127);
|
interpreter.midi_connection->send_note_off(chan.as_int(), *note.into_midi_note(), 127);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <musique/value/note.hh>
|
#include <musique/value/note.hh>
|
||||||
#include <musique/value/number.hh>
|
#include <musique/value/number.hh>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
/// Context holds default values for music related actions
|
/// Context holds default values for music related actions
|
||||||
struct Context
|
struct Context
|
||||||
@ -23,6 +24,8 @@ struct Context
|
|||||||
|
|
||||||
/// Converts length to seconds with current bpm
|
/// Converts length to seconds with current bpm
|
||||||
std::chrono::duration<float> length_to_duration(std::optional<Number> length) const;
|
std::chrono::duration<float> length_to_duration(std::optional<Number> length) const;
|
||||||
|
|
||||||
|
std::shared_ptr<Context> parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
midi::Connection *Interpreter::midi_connection = nullptr;
|
||||||
|
std::unordered_map<std::string, Intrinsic> Interpreter::operators {};
|
||||||
|
|
||||||
/// Registers constants like `fn = full note = 1/1`
|
/// Registers constants like `fn = full note = 1/1`
|
||||||
static inline void register_note_length_constants()
|
static inline void register_note_length_constants()
|
||||||
{
|
{
|
||||||
@ -32,7 +35,7 @@ static inline void register_note_length_constants()
|
|||||||
Interpreter::Interpreter()
|
Interpreter::Interpreter()
|
||||||
{
|
{
|
||||||
// Context initialization
|
// Context initialization
|
||||||
context_stack.emplace_back();
|
current_context = std::make_shared<Context>();
|
||||||
|
|
||||||
// Environment initlialization
|
// Environment initlialization
|
||||||
ensure(!bool(Env::global), "Only one instance of interpreter can be at one time");
|
ensure(!bool(Env::global), "Only one instance of interpreter can be at one time");
|
||||||
@ -228,7 +231,7 @@ void Interpreter::leave_scope()
|
|||||||
std::optional<Error> Interpreter::play(Chord chord)
|
std::optional<Error> Interpreter::play(Chord chord)
|
||||||
{
|
{
|
||||||
Try(ensure_midi_connection_available(*this, "play"));
|
Try(ensure_midi_connection_available(*this, "play"));
|
||||||
auto &ctx = context_stack.back();
|
auto &ctx = *current_context;
|
||||||
|
|
||||||
if (chord.notes.size() == 0) {
|
if (chord.notes.size() == 0) {
|
||||||
std::this_thread::sleep_for(ctx.length_to_duration(ctx.length));
|
std::this_thread::sleep_for(ctx.length_to_duration(ctx.length));
|
||||||
@ -389,7 +392,7 @@ static void snapshot(std::ostream& out, Value const& value) {
|
|||||||
|
|
||||||
void Interpreter::snapshot(std::ostream& out)
|
void Interpreter::snapshot(std::ostream& out)
|
||||||
{
|
{
|
||||||
auto const& ctx = context_stack.back();
|
auto const& ctx = *current_context;
|
||||||
out << ", oct " << int(ctx.octave) << '\n';
|
out << ", oct " << int(ctx.octave) << '\n';
|
||||||
out << ", len (" << ctx.length.num << "/" << ctx.length.den << ")\n";
|
out << ", len (" << ctx.length.num << "/" << ctx.length.den << ")\n";
|
||||||
out << ", bpm " << ctx.bpm << '\n';
|
out << ", bpm " << ctx.bpm << '\n';
|
||||||
|
@ -11,24 +11,24 @@ struct Interpreter
|
|||||||
{
|
{
|
||||||
/// MIDI connection that is used to play music.
|
/// MIDI connection that is used to play music.
|
||||||
/// It's optional for simple interpreter testing.
|
/// It's optional for simple interpreter testing.
|
||||||
midi::Connection *midi_connection = nullptr;
|
static midi::Connection *midi_connection;
|
||||||
|
|
||||||
/// Operators defined for language
|
/// Operators defined for language
|
||||||
std::unordered_map<std::string, Intrinsic> operators;
|
static std::unordered_map<std::string, Intrinsic> operators;
|
||||||
|
|
||||||
/// Current environment (current scope)
|
/// Current environment (current scope)
|
||||||
std::shared_ptr<Env> env;
|
std::shared_ptr<Env> env;
|
||||||
|
|
||||||
/// Context stack. `constext_stack.back()` is a current context.
|
/// Context stack. `constext_stack.back()` is a current context.
|
||||||
/// There is always at least one context
|
/// There is always at least one context
|
||||||
std::vector<Context> context_stack;
|
std::shared_ptr<Context> current_context;
|
||||||
|
|
||||||
std::function<std::optional<Error>(Interpreter&, Value)> default_action;
|
std::function<std::optional<Error>(Interpreter&, Value)> default_action;
|
||||||
|
|
||||||
Interpreter();
|
Interpreter();
|
||||||
~Interpreter();
|
~Interpreter();
|
||||||
|
Interpreter(Interpreter &&) = delete;
|
||||||
Interpreter(Interpreter const&) = delete;
|
Interpreter(Interpreter const&) = delete;
|
||||||
Interpreter(Interpreter &&) = default;
|
|
||||||
|
|
||||||
/// Try to evaluate given program tree
|
/// Try to evaluate given program tree
|
||||||
Result<Value> eval(Ast &&ast);
|
Result<Value> eval(Ast &&ast);
|
||||||
|
@ -26,7 +26,7 @@ cp bin/musique "$Target"/musique-x86_64-linux
|
|||||||
|
|
||||||
sudo rm -rf bin/
|
sudo rm -rf bin/
|
||||||
make os=windows >/dev/null
|
make os=windows >/dev/null
|
||||||
cp bin/musique.exe "$Target"/musique-x86_64-windows
|
cp bin/musique.exe "$Target"/musique-x86_64-windows.exe
|
||||||
|
|
||||||
cp LICENSE "$Target"/LICENSE
|
cp LICENSE "$Target"/LICENSE
|
||||||
cp CHANGELOG.md "$Target/CHANGELOG.md"
|
cp CHANGELOG.md "$Target/CHANGELOG.md"
|
||||||
@ -37,6 +37,7 @@ chmod 0755 "$Target"/install.sh
|
|||||||
lowdown -s doc/functions.md -m "title:Lista funkcji języka Musique" -o "$Target"/functions.html
|
lowdown -s doc/functions.md -m "title:Lista funkcji języka Musique" -o "$Target"/functions.html
|
||||||
python scripts/language-cmp-cheatsheet.py doc/musique-vs-languages-cheatsheet.template
|
python scripts/language-cmp-cheatsheet.py doc/musique-vs-languages-cheatsheet.template
|
||||||
mv doc/musique-vs-languages-cheatsheet.html "${Target}/musique-vs-others-cheatsheet.html"
|
mv doc/musique-vs-languages-cheatsheet.html "${Target}/musique-vs-others-cheatsheet.html"
|
||||||
|
make doc/wprowadzenie.html && mv doc/wprowadzenie.html "${Target}/wprowadzenie.html"
|
||||||
|
|
||||||
git clone --recursive --quiet --depth=1 "$(git remote -v | awk '{ print $2 }' | head -n1)" "$Target"/source_code
|
git clone --recursive --quiet --depth=1 "$(git remote -v | awk '{ print $2 }' | head -n1)" "$Target"/source_code
|
||||||
rm -rf "$Target"/source_code/.git
|
rm -rf "$Target"/source_code/.git
|
||||||
|
Loading…
Reference in New Issue
Block a user