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)
|
||||
- operator pretty printing while printing values
|
||||
- macros: builtin functions that takes AST and produces value
|
||||
- early text-based snapshot system available via `:snap` command inside interactive session
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -277,3 +277,132 @@ std::optional<Error> ensure_midi_connection_available(Interpreter &i, std::strin
|
||||
}
|
||||
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
|
||||
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);
|
||||
|
@ -314,6 +314,18 @@ static Result<bool> handle_repl_session_commands(std::string_view input, Runner
|
||||
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('!')) {
|
||||
|
Loading…
Reference in New Issue
Block a user