mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-11 00:36:20 +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)
|
||||
{
|
||||
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)
|
||||
throwTypeError(pos, "attempt to call something which is not a function but %1%", fun);
|
||||
if (fun.type != tLambda) {
|
||||
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);
|
||||
|
||||
|
|
|
@ -31,6 +31,30 @@ void StoreCommand::run()
|
|||
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)
|
||||
: recursive(recursive)
|
||||
{
|
||||
|
|
|
@ -36,8 +36,12 @@ private:
|
|||
|
||||
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||
{
|
||||
bool startReplOnEvalErrors = false;
|
||||
|
||||
ref<EvalState> getEvalState();
|
||||
|
||||
EvalCommand();
|
||||
|
||||
std::shared_ptr<EvalState> evalState;
|
||||
};
|
||||
|
||||
|
@ -251,4 +255,8 @@ void printClosureDiff(
|
|||
const StorePath & afterPath,
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (prefix == "")
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace nix {
|
|||
struct NixRepl : gc
|
||||
{
|
||||
string curDir;
|
||||
std::unique_ptr<EvalState> state;
|
||||
ref<EvalState> state;
|
||||
Bindings * autoArgs;
|
||||
|
||||
Strings loadedFiles;
|
||||
|
@ -54,7 +54,7 @@ struct NixRepl : gc
|
|||
|
||||
const Path historyFile;
|
||||
|
||||
NixRepl(const Strings & searchPath, nix::ref<Store> store);
|
||||
NixRepl(ref<EvalState> state);
|
||||
~NixRepl();
|
||||
void mainLoop(const std::vector<std::string> & files);
|
||||
StringSet completePrefix(string prefix);
|
||||
|
@ -65,13 +65,13 @@ struct NixRepl : gc
|
|||
void initEnv();
|
||||
void reloadFiles();
|
||||
void addAttrsToScope(Value & attrs);
|
||||
void addVarToScope(const Symbol & name, Value & v);
|
||||
void addVarToScope(const Symbol & name, Value * v);
|
||||
Expr * parseString(string s);
|
||||
void evalString(string s, Value & v);
|
||||
|
||||
typedef set<Value *> ValuesSeen;
|
||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth);
|
||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
|
||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth);
|
||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
|
||||
};
|
||||
|
||||
|
||||
|
@ -84,8 +84,8 @@ string removeWhitespace(string s)
|
|||
}
|
||||
|
||||
|
||||
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store)
|
||||
: state(std::make_unique<EvalState>(searchPath, store))
|
||||
NixRepl::NixRepl(ref<EvalState> state)
|
||||
: state(state)
|
||||
, staticEnv(false, &state->staticBaseEnv)
|
||||
, 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 " ";
|
||||
std::cout << "Welcome to Nix version " << nixVersion << ". Type :? for help." << std::endl << std::endl;
|
||||
|
||||
for (auto & i : files)
|
||||
loadedFiles.push_back(i);
|
||||
if (!files.empty()) {
|
||||
for (auto & i : files)
|
||||
loadedFiles.push_back(i);
|
||||
|
||||
reloadFiles();
|
||||
if (!loadedFiles.empty()) std::cout << std::endl;
|
||||
reloadFiles();
|
||||
if (!loadedFiles.empty()) std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Allow nix-repl specific settings in .inputrc
|
||||
rl_readline_name = "nix-repl";
|
||||
|
@ -516,10 +518,10 @@ bool NixRepl::processLine(string line)
|
|||
isVarName(name = removeWhitespace(string(line, 0, p))))
|
||||
{
|
||||
Expr * e = parseString(string(line, p + 1));
|
||||
Value & v(*state->allocValue());
|
||||
v.type = tThunk;
|
||||
v.thunk.env = env;
|
||||
v.thunk.expr = e;
|
||||
auto v = state->allocValue();
|
||||
v->type = tThunk;
|
||||
v->thunk.env = env;
|
||||
v->thunk.expr = e;
|
||||
addVarToScope(state->symbols.create(name), v);
|
||||
} else {
|
||||
Value v;
|
||||
|
@ -577,17 +579,17 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
|||
{
|
||||
state->forceAttrs(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;
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::addVarToScope(const Symbol & name, Value & v)
|
||||
void NixRepl::addVarToScope(const Symbol & name, Value * v)
|
||||
{
|
||||
if (displ >= envSize)
|
||||
throw Error("environment full; cannot add more variables");
|
||||
staticEnv.vars[name] = displ;
|
||||
env->values[displ++] = &v;
|
||||
env->values[displ++] = v;
|
||||
varNames.insert((string) name);
|
||||
}
|
||||
|
||||
|
@ -754,6 +756,26 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
|||
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
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
|
@ -775,17 +797,20 @@ struct CmdRepl : StoreCommand, MixEvalArgs
|
|||
Examples examples() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"Display all special commands within the REPL:",
|
||||
"nix repl\n nix-repl> :?"
|
||||
}
|
||||
{
|
||||
"Display all special commands within the REPL:",
|
||||
"nix repl\n nix-repl> :?"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
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->mainLoop(files);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue