mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-23 22:46:16 +02:00
Add a flag to start the REPL on evaluation errors
This allows interactively inspecting the state of the evaluator at the point of failure. Example: $ nix eval path:///home/eelco/Dev/nix/flake2#modules.hello-closure._final --start-repl-on-eval-errors error: --- TypeError -------------------------------------------------------------------------------------------------------------------------------------------------------------------- nix at: (20:53) in file: /nix/store/4264z41dxfdiqr95svmpnxxxwhfplhy0-source/flake.nix 19| 20| _final = builtins.foldl' (xs: mod: xs // (mod._module.config { config = _final; })) _defaults _allModules; | ^ 21| }; attempt to call something which is not a function but a set Starting REPL to allow you to inspect the current state of the evaluator. The following extra variables are in scope: arg, fun Welcome to Nix version 2.4. Type :? for help. nix-repl> fun error: --- EvalError -------------------------------------------------------------------------------------------------------------------------------------------------------------------- nix at: (150:28) in file: /nix/store/4264z41dxfdiqr95svmpnxxxwhfplhy0-source/flake.nix 149| 150| tarballClosure = (module { | ^ 151| extends = [ self.modules.derivation ]; attribute 'derivation' missing nix-repl> :t fun a set nix-repl> builtins.attrNames fun [ "tarballClosure" ] nix-repl>
This commit is contained in:
parent
b3e73547a0
commit
e5662ba652
5 changed files with 91 additions and 32 deletions
|
@ -1171,6 +1171,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::function<void(const Error & error, const std::map<std::string, Value *> & env)> debuggerHook;
|
||||||
|
|
||||||
void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & pos)
|
void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
auto trace = evalSettings.traceFunctionCalls ? std::make_unique<FunctionCallTrace>(pos) : nullptr;
|
auto trace = evalSettings.traceFunctionCalls ? std::make_unique<FunctionCallTrace>(pos) : nullptr;
|
||||||
|
@ -1198,8 +1200,15 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fun.type != tLambda)
|
if (fun.type != tLambda) {
|
||||||
throwTypeError(pos, "attempt to call something which is not a function but %1%", fun);
|
auto error = TypeError({
|
||||||
|
.hint = hintfmt("attempt to call something which is not a function but %1%", showType(fun)),
|
||||||
|
.errPos = pos
|
||||||
|
});
|
||||||
|
if (debuggerHook)
|
||||||
|
debuggerHook(error, {{"fun", &fun}, {"arg", &arg}});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
ExprLambda & lambda(*fun.lambda.fun);
|
ExprLambda & lambda(*fun.lambda.fun);
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,30 @@ void StoreCommand::run()
|
||||||
run(getStore());
|
run(getStore());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EvalCommand::EvalCommand()
|
||||||
|
{
|
||||||
|
addFlag({
|
||||||
|
.longName = "start-repl-on-eval-errors",
|
||||||
|
.description = "start an interactive environment if evaluation fails",
|
||||||
|
.handler = {&startReplOnEvalErrors, true},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extern std::function<void(const Error & error, const std::map<std::string, Value *> & env)> debuggerHook;
|
||||||
|
|
||||||
|
ref<EvalState> EvalCommand::getEvalState()
|
||||||
|
{
|
||||||
|
if (!evalState) {
|
||||||
|
evalState = std::make_shared<EvalState>(searchPath, getStore());
|
||||||
|
if (startReplOnEvalErrors)
|
||||||
|
debuggerHook = [evalState{ref<EvalState>(evalState)}](const Error & error, const std::map<std::string, Value *> & env) {
|
||||||
|
printError("%s\n\n" ANSI_BOLD "Starting REPL to allow you to inspect the current state of the evaluator.\n" ANSI_NORMAL, error.what());
|
||||||
|
runRepl(evalState, env);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ref<EvalState>(evalState);
|
||||||
|
}
|
||||||
|
|
||||||
StorePathsCommand::StorePathsCommand(bool recursive)
|
StorePathsCommand::StorePathsCommand(bool recursive)
|
||||||
: recursive(recursive)
|
: recursive(recursive)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,8 +36,12 @@ private:
|
||||||
|
|
||||||
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||||
{
|
{
|
||||||
|
bool startReplOnEvalErrors = false;
|
||||||
|
|
||||||
ref<EvalState> getEvalState();
|
ref<EvalState> getEvalState();
|
||||||
|
|
||||||
|
EvalCommand();
|
||||||
|
|
||||||
std::shared_ptr<EvalState> evalState;
|
std::shared_ptr<EvalState> evalState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -251,4 +255,8 @@ void printClosureDiff(
|
||||||
const StorePath & afterPath,
|
const StorePath & afterPath,
|
||||||
std::string_view indent);
|
std::string_view indent);
|
||||||
|
|
||||||
|
void runRepl(
|
||||||
|
ref<EvalState> evalState,
|
||||||
|
const std::map<std::string, Value *> & extraEnv);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,13 +234,6 @@ void completeFlakeRefWithFragment(
|
||||||
completeFlakeRef(evalState->store, prefix);
|
completeFlakeRef(evalState->store, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref<EvalState> EvalCommand::getEvalState()
|
|
||||||
{
|
|
||||||
if (!evalState)
|
|
||||||
evalState = std::make_shared<EvalState>(searchPath, getStore());
|
|
||||||
return ref<EvalState>(evalState);
|
|
||||||
}
|
|
||||||
|
|
||||||
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
||||||
{
|
{
|
||||||
if (prefix == "")
|
if (prefix == "")
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace nix {
|
||||||
struct NixRepl : gc
|
struct NixRepl : gc
|
||||||
{
|
{
|
||||||
string curDir;
|
string curDir;
|
||||||
std::unique_ptr<EvalState> state;
|
ref<EvalState> state;
|
||||||
Bindings * autoArgs;
|
Bindings * autoArgs;
|
||||||
|
|
||||||
Strings loadedFiles;
|
Strings loadedFiles;
|
||||||
|
@ -54,7 +54,7 @@ struct NixRepl : gc
|
||||||
|
|
||||||
const Path historyFile;
|
const Path historyFile;
|
||||||
|
|
||||||
NixRepl(const Strings & searchPath, nix::ref<Store> store);
|
NixRepl(ref<EvalState> state);
|
||||||
~NixRepl();
|
~NixRepl();
|
||||||
void mainLoop(const std::vector<std::string> & files);
|
void mainLoop(const std::vector<std::string> & files);
|
||||||
StringSet completePrefix(string prefix);
|
StringSet completePrefix(string prefix);
|
||||||
|
@ -65,7 +65,7 @@ struct NixRepl : gc
|
||||||
void initEnv();
|
void initEnv();
|
||||||
void reloadFiles();
|
void reloadFiles();
|
||||||
void addAttrsToScope(Value & attrs);
|
void addAttrsToScope(Value & attrs);
|
||||||
void addVarToScope(const Symbol & name, Value & v);
|
void addVarToScope(const Symbol & name, Value * v);
|
||||||
Expr * parseString(string s);
|
Expr * parseString(string s);
|
||||||
void evalString(string s, Value & v);
|
void evalString(string s, Value & v);
|
||||||
|
|
||||||
|
@ -84,8 +84,8 @@ string removeWhitespace(string s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store)
|
NixRepl::NixRepl(ref<EvalState> state)
|
||||||
: state(std::make_unique<EvalState>(searchPath, store))
|
: state(state)
|
||||||
, staticEnv(false, &state->staticBaseEnv)
|
, staticEnv(false, &state->staticBaseEnv)
|
||||||
, historyFile(getDataDir() + "/nix/repl-history")
|
, historyFile(getDataDir() + "/nix/repl-history")
|
||||||
{
|
{
|
||||||
|
@ -176,11 +176,13 @@ void NixRepl::mainLoop(const std::vector<std::string> & files)
|
||||||
string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
||||||
std::cout << "Welcome to Nix version " << nixVersion << ". Type :? for help." << std::endl << std::endl;
|
std::cout << "Welcome to Nix version " << nixVersion << ". Type :? for help." << std::endl << std::endl;
|
||||||
|
|
||||||
|
if (!files.empty()) {
|
||||||
for (auto & i : files)
|
for (auto & i : files)
|
||||||
loadedFiles.push_back(i);
|
loadedFiles.push_back(i);
|
||||||
|
|
||||||
reloadFiles();
|
reloadFiles();
|
||||||
if (!loadedFiles.empty()) std::cout << std::endl;
|
if (!loadedFiles.empty()) std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
// Allow nix-repl specific settings in .inputrc
|
// Allow nix-repl specific settings in .inputrc
|
||||||
rl_readline_name = "nix-repl";
|
rl_readline_name = "nix-repl";
|
||||||
|
@ -516,10 +518,10 @@ bool NixRepl::processLine(string line)
|
||||||
isVarName(name = removeWhitespace(string(line, 0, p))))
|
isVarName(name = removeWhitespace(string(line, 0, p))))
|
||||||
{
|
{
|
||||||
Expr * e = parseString(string(line, p + 1));
|
Expr * e = parseString(string(line, p + 1));
|
||||||
Value & v(*state->allocValue());
|
auto v = state->allocValue();
|
||||||
v.type = tThunk;
|
v->type = tThunk;
|
||||||
v.thunk.env = env;
|
v->thunk.env = env;
|
||||||
v.thunk.expr = e;
|
v->thunk.expr = e;
|
||||||
addVarToScope(state->symbols.create(name), v);
|
addVarToScope(state->symbols.create(name), v);
|
||||||
} else {
|
} else {
|
||||||
Value v;
|
Value v;
|
||||||
|
@ -577,17 +579,17 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
||||||
{
|
{
|
||||||
state->forceAttrs(attrs);
|
state->forceAttrs(attrs);
|
||||||
for (auto & i : *attrs.attrs)
|
for (auto & i : *attrs.attrs)
|
||||||
addVarToScope(i.name, *i.value);
|
addVarToScope(i.name, i.value);
|
||||||
std::cout << format("Added %1% variables.") % attrs.attrs->size() << std::endl;
|
std::cout << format("Added %1% variables.") % attrs.attrs->size() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void NixRepl::addVarToScope(const Symbol & name, Value & v)
|
void NixRepl::addVarToScope(const Symbol & name, Value * v)
|
||||||
{
|
{
|
||||||
if (displ >= envSize)
|
if (displ >= envSize)
|
||||||
throw Error("environment full; cannot add more variables");
|
throw Error("environment full; cannot add more variables");
|
||||||
staticEnv.vars[name] = displ;
|
staticEnv.vars[name] = displ;
|
||||||
env->values[displ++] = &v;
|
env->values[displ++] = v;
|
||||||
varNames.insert((string) name);
|
varNames.insert((string) name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,6 +756,26 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void runRepl(
|
||||||
|
ref<EvalState> evalState,
|
||||||
|
const std::map<std::string, Value *> & extraEnv)
|
||||||
|
{
|
||||||
|
auto repl = std::make_unique<NixRepl>(evalState);
|
||||||
|
|
||||||
|
repl->initEnv();
|
||||||
|
|
||||||
|
std::set<std::string> names;
|
||||||
|
|
||||||
|
for (auto & [name, value] : extraEnv) {
|
||||||
|
names.insert(ANSI_BOLD + name + ANSI_NORMAL);
|
||||||
|
repl->addVarToScope(repl->state->symbols.create(name), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
printError("The following extra variables are in scope: %s\n", concatStringsSep(", ", names));
|
||||||
|
|
||||||
|
repl->mainLoop({});
|
||||||
|
}
|
||||||
|
|
||||||
struct CmdRepl : StoreCommand, MixEvalArgs
|
struct CmdRepl : StoreCommand, MixEvalArgs
|
||||||
{
|
{
|
||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
|
@ -775,7 +797,7 @@ struct CmdRepl : StoreCommand, MixEvalArgs
|
||||||
Examples examples() override
|
Examples examples() override
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
Example{
|
{
|
||||||
"Display all special commands within the REPL:",
|
"Display all special commands within the REPL:",
|
||||||
"nix repl\n nix-repl> :?"
|
"nix repl\n nix-repl> :?"
|
||||||
}
|
}
|
||||||
|
@ -785,7 +807,10 @@ struct CmdRepl : StoreCommand, MixEvalArgs
|
||||||
void run(ref<Store> store) override
|
void run(ref<Store> store) override
|
||||||
{
|
{
|
||||||
evalSettings.pureEval = false;
|
evalSettings.pureEval = false;
|
||||||
auto repl = std::make_unique<NixRepl>(searchPath, openStore());
|
|
||||||
|
auto evalState = make_ref<EvalState>(searchPath, store);
|
||||||
|
|
||||||
|
auto repl = std::make_unique<NixRepl>(evalState);
|
||||||
repl->autoArgs = getAutoArgs(*repl->state);
|
repl->autoArgs = getAutoArgs(*repl->state);
|
||||||
repl->mainLoop(files);
|
repl->mainLoop(files);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue