aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/eval.cc28
-rw-r--r--src/libexpr/eval.hh4
-rw-r--r--src/libexpr/primops.cc43
-rw-r--r--src/libexpr/primops.hh8
-rw-r--r--src/nix/repl.cc21
5 files changed, 92 insertions, 12 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 9a155c055..6ac8bbc9f 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -525,6 +525,34 @@ Value * EvalState::addPrimOp(const string & name,
}
+Value * EvalState::addPrimOp(PrimOp && primOp)
+{
+ /* Hack to make constants lazy: turn them into a application of
+ the primop to a dummy value. */
+ if (primOp.arity == 0) {
+ primOp.arity = 1;
+ auto vPrimOp = allocValue();
+ vPrimOp->type = tPrimOp;
+ vPrimOp->primOp = new PrimOp(std::move(primOp));
+ Value v;
+ mkApp(v, *vPrimOp, *vPrimOp);
+ return addConstant(primOp.name, v);
+ }
+
+ Symbol envName = primOp.name;
+ if (hasPrefix(primOp.name, "__"))
+ primOp.name = symbols.create(std::string(primOp.name, 2));
+
+ Value * v = allocValue();
+ v->type = tPrimOp;
+ v->primOp = new PrimOp(std::move(primOp));
+ staticBaseEnv.vars[envName] = baseEnvDispl;
+ baseEnv.values[baseEnvDispl++] = v;
+ baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v));
+ return v;
+}
+
+
Value & EvalState::getBuiltin(const string & name)
{
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 38e025a3d..db8eb3e16 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -30,6 +30,8 @@ struct PrimOp
PrimOpFun fun;
size_t arity;
Symbol name;
+ std::vector<std::string> args;
+ const char * doc = nullptr;
};
@@ -240,6 +242,8 @@ private:
Value * addPrimOp(const string & name,
size_t arity, PrimOpFun primOp);
+ Value * addPrimOp(PrimOp && primOp);
+
public:
Value & getBuiltin(const string & name);
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 4e35dedb0..902a37e6b 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -445,12 +445,19 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
}
-static void prim_abort(EvalState & state, const Pos & pos, Value * * args, Value & v)
-{
- PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
- throw Abort("evaluation aborted with the following error message: '%1%'", s);
-}
+static RegisterPrimOp primop_abort({
+ .name = "abort",
+ .args = {"s"},
+ .doc = R"(
+ Abort Nix expression evaluation and print the error message *s*.
+ )",
+ .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
+ {
+ PathSet context;
+ string s = state.coerceToString(pos, *args[0], context);
+ throw Abort("evaluation aborted with the following error message: '%1%'", s);
+ }
+});
static void prim_throw(EvalState & state, const Pos & pos, Value * * args, Value & v)
@@ -2238,7 +2245,20 @@ RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun,
std::optional<std::string> requiredFeature)
{
if (!primOps) primOps = new PrimOps;
- primOps->push_back({name, arity, fun, requiredFeature});
+ primOps->push_back({
+ .name = name,
+ .args = {},
+ .arity = arity,
+ .requiredFeature = std::move(requiredFeature),
+ .fun = fun
+ });
+}
+
+
+RegisterPrimOp::RegisterPrimOp(Info && info)
+{
+ if (!primOps) primOps = new PrimOps;
+ primOps->push_back(std::move(info));
}
@@ -2314,7 +2334,6 @@ void EvalState::createBaseEnv()
addPrimOp("__isBool", 1, prim_isBool);
addPrimOp("__isPath", 1, prim_isPath);
addPrimOp("__genericClosure", 1, prim_genericClosure);
- addPrimOp("abort", 1, prim_abort);
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
addPrimOp("__tryEval", 1, prim_tryEval);
addPrimOp("__getEnv", 1, prim_getEnv);
@@ -2431,7 +2450,13 @@ void EvalState::createBaseEnv()
if (RegisterPrimOp::primOps)
for (auto & primOp : *RegisterPrimOp::primOps)
if (!primOp.requiredFeature || settings.isExperimentalFeatureEnabled(*primOp.requiredFeature))
- addPrimOp(primOp.name, primOp.arity, primOp.primOp);
+ addPrimOp({
+ .fun = primOp.fun,
+ .arity = std::max(primOp.args.size(), primOp.arity),
+ .name = symbols.create(primOp.name),
+ .args = std::move(primOp.args),
+ .doc = primOp.doc,
+ });
/* Now that we've added all primops, sort the `builtins' set,
because attribute lookups expect it to be sorted. */
diff --git a/src/libexpr/primops.hh b/src/libexpr/primops.hh
index 75c460ecf..ed5e2ea58 100644
--- a/src/libexpr/primops.hh
+++ b/src/libexpr/primops.hh
@@ -10,9 +10,11 @@ struct RegisterPrimOp
struct Info
{
std::string name;
- size_t arity;
- PrimOpFun primOp;
+ std::vector<std::string> args;
+ size_t arity = 0;
+ const char * doc;
std::optional<std::string> requiredFeature;
+ PrimOpFun fun;
};
typedef std::vector<Info> PrimOps;
@@ -26,6 +28,8 @@ struct RegisterPrimOp
size_t arity,
PrimOpFun fun,
std::optional<std::string> requiredFeature = {});
+
+ RegisterPrimOp(Info && info);
};
/* These primops are disabled without enableNativeCode, but plugins
diff --git a/src/nix/repl.cc b/src/nix/repl.cc
index 0cbe9643c..d370ca767 100644
--- a/src/nix/repl.cc
+++ b/src/nix/repl.cc
@@ -416,7 +416,8 @@ bool NixRepl::processLine(string line)
<< " :r Reload all files\n"
<< " :s <expr> Build dependencies of derivation, then start nix-shell\n"
<< " :t <expr> Describe result of evaluation\n"
- << " :u <expr> Build derivation, then start nix-shell\n";
+ << " :u <expr> Build derivation, then start nix-shell\n"
+ << " :doc <expr> Show documentation of a builtin function\n";
}
else if (command == ":a" || command == ":add") {
@@ -509,6 +510,24 @@ bool NixRepl::processLine(string line)
else if (command == ":q" || command == ":quit")
return false;
+ else if (command == ":doc") {
+ Value v;
+ evalString(arg, v);
+ if (v.type == tPrimOp || v.type == tPrimOpApp) {
+ auto v2 = &v;
+ while (v2->type == tPrimOpApp)
+ v2 = v2->primOpApp.left;
+ if (v2->primOp->doc) {
+ // FIXME: format markdown.
+ if (!v2->primOp->args.empty())
+ std::cout << fmt("Arguments: %s\n\n", concatStringsSep(" ", v2->primOp->args));
+ std::cout << trim(stripIndentation(v2->primOp->doc)) << "\n";
+ } else
+ throw Error("builtin function '%s' does not have documentation", v2->primOp->name);
+ } else
+ throw Error("value does not have documentation");
+ }
+
else if (command != "")
throw Error("unknown command '%1%'", command);