Early text-based snapshot system
This commit is contained in:
parent
89b3519c24
commit
268455532b
@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- `<note><octave>` notation like `c4` that mimics [scientific notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation)
|
- `<note><octave>` notation like `c4` that mimics [scientific notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation)
|
||||||
- operator pretty printing while printing values
|
- operator pretty printing while printing values
|
||||||
- macros: builtin functions that takes AST and produces value
|
- macros: builtin functions that takes AST and produces value
|
||||||
|
- early text-based snapshot system available via `:snap` command inside interactive session
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -277,3 +277,132 @@ std::optional<Error> ensure_midi_connection_available(Interpreter &i, std::strin
|
|||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void snapshot(std::ostream& out, Note const& note) {
|
||||||
|
if (note.length) {
|
||||||
|
out << "(" << note << ")";
|
||||||
|
} else {
|
||||||
|
out << note;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snapshot(std::ostream &out, Ast const& ast) {
|
||||||
|
switch (ast.type) {
|
||||||
|
break; case Ast::Type::Sequence:
|
||||||
|
{
|
||||||
|
for (auto const& a : ast.arguments) {
|
||||||
|
snapshot(out, a);
|
||||||
|
out << ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break; case Ast::Type::Block:
|
||||||
|
ensure(ast.arguments.size() == 1, "Block can contain only one node which contains its body");
|
||||||
|
out << "(";
|
||||||
|
snapshot(out, ast.arguments.front());
|
||||||
|
out << ")";
|
||||||
|
|
||||||
|
break; case Ast::Type::Lambda:
|
||||||
|
out << "(";
|
||||||
|
for (auto i = 0u; i+1 < ast.arguments.size(); ++i) {
|
||||||
|
ensure(ast.arguments[i].type == Ast::Type::Literal, "Lambda arguments should be an identifiers");
|
||||||
|
out << ast.arguments[i].token.source << " ";
|
||||||
|
}
|
||||||
|
out << "|";
|
||||||
|
snapshot(out, ast.arguments.back());
|
||||||
|
out << ")";
|
||||||
|
|
||||||
|
break; case Ast::Type::Variable_Declaration:
|
||||||
|
ensure(ast.arguments.size() == 2, "Variable declaration snapshots only support single lhs variables");
|
||||||
|
ensure(ast.arguments.front().type == Ast::Type::Literal, "Expected first value to be an identifier");
|
||||||
|
out << ast.arguments[0].token.source << " := ";
|
||||||
|
return snapshot(out, ast.arguments.back());
|
||||||
|
|
||||||
|
break; case Ast::Type::Literal:
|
||||||
|
out << ast.token.source;
|
||||||
|
|
||||||
|
break; case Ast::Type::Binary:
|
||||||
|
out << "(";
|
||||||
|
snapshot(out, ast.arguments[0]);
|
||||||
|
out << ") ";
|
||||||
|
out << ast.token.source;
|
||||||
|
out << " (";
|
||||||
|
snapshot(out, ast.arguments[1]);
|
||||||
|
out << ")";
|
||||||
|
|
||||||
|
break; case Ast::Type::Call:
|
||||||
|
out << "(";
|
||||||
|
for (auto const& a : ast.arguments) {
|
||||||
|
out << "(";
|
||||||
|
snapshot(out, a);
|
||||||
|
out << ") ";
|
||||||
|
}
|
||||||
|
out << ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snapshot(std::ostream& out, Value const& value) {
|
||||||
|
std::visit(Overloaded{
|
||||||
|
[&](Nil) { out << "nil"; },
|
||||||
|
[&](Bool const& b) {
|
||||||
|
out << (b ? "true" : "false");
|
||||||
|
},
|
||||||
|
[&](Number const& n) {
|
||||||
|
out << "(" << n.num << "/" << n.den << ")";
|
||||||
|
},
|
||||||
|
[&](Array const& array) {
|
||||||
|
out << "(flat (";
|
||||||
|
for (auto const& nested : array.elements) {
|
||||||
|
snapshot(out, nested);
|
||||||
|
out << ", ";
|
||||||
|
}
|
||||||
|
out << "))";
|
||||||
|
},
|
||||||
|
[&](Chord const& chord) {
|
||||||
|
if (chord.notes.size() == 1) {
|
||||||
|
auto note = chord.notes.front();
|
||||||
|
snapshot(out, note);
|
||||||
|
} else {
|
||||||
|
out << "(chord ";
|
||||||
|
for (auto const ¬e : chord.notes) {
|
||||||
|
snapshot(out, note);
|
||||||
|
out << " ";
|
||||||
|
}
|
||||||
|
out << ")";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&](Symbol const& symbol) {
|
||||||
|
out << "'" << symbol;
|
||||||
|
},
|
||||||
|
[&](Block const& block) {
|
||||||
|
out << "(";
|
||||||
|
for (auto const& param : block.parameters) {
|
||||||
|
out << param << ' ';
|
||||||
|
}
|
||||||
|
out << "| ";
|
||||||
|
snapshot(out, block.body);
|
||||||
|
out << ")";
|
||||||
|
},
|
||||||
|
[](Intrinsic const&) { unreachable(); },
|
||||||
|
[](Macro const&) { unreachable(); }
|
||||||
|
}, value.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interpreter::snapshot(std::ostream& out)
|
||||||
|
{
|
||||||
|
auto const& ctx = context_stack.back();
|
||||||
|
out << ", oct " << int(ctx.octave) << '\n';
|
||||||
|
out << ", len (" << ctx.length.num << "/" << ctx.length.den << ")\n";
|
||||||
|
out << ", bpm " << ctx.bpm << '\n';
|
||||||
|
|
||||||
|
for (auto current = env.get(); current; current = current->parent.get()) {
|
||||||
|
for (auto const& [name, value] : current->variables) {
|
||||||
|
if (std::holds_alternative<Intrinsic>(value.data) || std::holds_alternative<Macro>(value.data)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out << ", " << name << " := ";
|
||||||
|
::snapshot(out, value);
|
||||||
|
out << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << std::flush;
|
||||||
|
}
|
||||||
|
@ -51,6 +51,9 @@ struct Interpreter
|
|||||||
///
|
///
|
||||||
/// Invoked during construction
|
/// Invoked during construction
|
||||||
void register_builtin_operators();
|
void register_builtin_operators();
|
||||||
|
|
||||||
|
/// Dumps snapshot of interpreter into stream
|
||||||
|
void snapshot(std::ostream& out);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<Error> ensure_midi_connection_available(Interpreter&, std::string_view operation_name);
|
std::optional<Error> ensure_midi_connection_available(Interpreter&, std::string_view operation_name);
|
||||||
|
@ -314,6 +314,18 @@ static Result<bool> handle_repl_session_commands(std::string_view input, Runner
|
|||||||
return runner.run(eternal_sources.back(), path);
|
return runner.run(eternal_sources.back(), path);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Command {
|
||||||
|
"snap",
|
||||||
|
+[](Runner &runner, std::optional<std::string_view> arg) -> std::optional<Error> {
|
||||||
|
std::ostream *out = &std::cout;
|
||||||
|
std::fstream file;
|
||||||
|
if (arg.has_value() && arg->size()) {
|
||||||
|
file.open(std::string(*arg));
|
||||||
|
}
|
||||||
|
runner.interpreter.snapshot(*out);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (input.starts_with('!')) {
|
if (input.starts_with('!')) {
|
||||||
|
Loading…
Reference in New Issue
Block a user