forked from lix-project/lix
Allow primops to have Markdown documentation
This commit is contained in:
parent
88d5c9ec58
commit
33b1679d75
|
@ -9,9 +9,6 @@ scope. Instead, you can access them through the `builtins` built-in
|
||||||
value, which is a set that contains all built-in functions and values.
|
value, which is a set that contains all built-in functions and values.
|
||||||
For instance, `derivation` is also available as `builtins.derivation`.
|
For instance, `derivation` is also available as `builtins.derivation`.
|
||||||
|
|
||||||
- `abort` *s*; `builtins.abort` *s*
|
|
||||||
Abort Nix expression evaluation, print error message *s*.
|
|
||||||
|
|
||||||
- `builtins.add` *e1* *e2*
|
- `builtins.add` *e1* *e2*
|
||||||
Return the sum of the numbers *e1* and *e2*.
|
Return the sum of the numbers *e1* and *e2*.
|
||||||
|
|
||||||
|
|
|
@ -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)
|
Value & EvalState::getBuiltin(const string & name)
|
||||||
{
|
{
|
||||||
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
|
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
|
||||||
|
|
|
@ -30,6 +30,8 @@ struct PrimOp
|
||||||
PrimOpFun fun;
|
PrimOpFun fun;
|
||||||
size_t arity;
|
size_t arity;
|
||||||
Symbol name;
|
Symbol name;
|
||||||
|
std::vector<std::string> args;
|
||||||
|
const char * doc = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -240,6 +242,8 @@ private:
|
||||||
Value * addPrimOp(const string & name,
|
Value * addPrimOp(const string & name,
|
||||||
size_t arity, PrimOpFun primOp);
|
size_t arity, PrimOpFun primOp);
|
||||||
|
|
||||||
|
Value * addPrimOp(PrimOp && primOp);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Value & getBuiltin(const string & name);
|
Value & getBuiltin(const string & name);
|
||||||
|
|
|
@ -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)
|
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;
|
PathSet context;
|
||||||
string s = state.coerceToString(pos, *args[0], context);
|
string s = state.coerceToString(pos, *args[0], context);
|
||||||
throw Abort("evaluation aborted with the following error message: '%1%'", s);
|
throw Abort("evaluation aborted with the following error message: '%1%'", s);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
static void prim_throw(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
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)
|
std::optional<std::string> requiredFeature)
|
||||||
{
|
{
|
||||||
if (!primOps) primOps = new PrimOps;
|
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("__isBool", 1, prim_isBool);
|
||||||
addPrimOp("__isPath", 1, prim_isPath);
|
addPrimOp("__isPath", 1, prim_isPath);
|
||||||
addPrimOp("__genericClosure", 1, prim_genericClosure);
|
addPrimOp("__genericClosure", 1, prim_genericClosure);
|
||||||
addPrimOp("abort", 1, prim_abort);
|
|
||||||
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
|
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
|
||||||
addPrimOp("__tryEval", 1, prim_tryEval);
|
addPrimOp("__tryEval", 1, prim_tryEval);
|
||||||
addPrimOp("__getEnv", 1, prim_getEnv);
|
addPrimOp("__getEnv", 1, prim_getEnv);
|
||||||
|
@ -2431,7 +2450,13 @@ void EvalState::createBaseEnv()
|
||||||
if (RegisterPrimOp::primOps)
|
if (RegisterPrimOp::primOps)
|
||||||
for (auto & primOp : *RegisterPrimOp::primOps)
|
for (auto & primOp : *RegisterPrimOp::primOps)
|
||||||
if (!primOp.requiredFeature || settings.isExperimentalFeatureEnabled(*primOp.requiredFeature))
|
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,
|
/* Now that we've added all primops, sort the `builtins' set,
|
||||||
because attribute lookups expect it to be sorted. */
|
because attribute lookups expect it to be sorted. */
|
||||||
|
|
|
@ -10,9 +10,11 @@ struct RegisterPrimOp
|
||||||
struct Info
|
struct Info
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
size_t arity;
|
std::vector<std::string> args;
|
||||||
PrimOpFun primOp;
|
size_t arity = 0;
|
||||||
|
const char * doc;
|
||||||
std::optional<std::string> requiredFeature;
|
std::optional<std::string> requiredFeature;
|
||||||
|
PrimOpFun fun;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<Info> PrimOps;
|
typedef std::vector<Info> PrimOps;
|
||||||
|
@ -26,6 +28,8 @@ struct RegisterPrimOp
|
||||||
size_t arity,
|
size_t arity,
|
||||||
PrimOpFun fun,
|
PrimOpFun fun,
|
||||||
std::optional<std::string> requiredFeature = {});
|
std::optional<std::string> requiredFeature = {});
|
||||||
|
|
||||||
|
RegisterPrimOp(Info && info);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* These primops are disabled without enableNativeCode, but plugins
|
/* These primops are disabled without enableNativeCode, but plugins
|
||||||
|
|
|
@ -416,7 +416,8 @@ bool NixRepl::processLine(string line)
|
||||||
<< " :r Reload all files\n"
|
<< " :r Reload all files\n"
|
||||||
<< " :s <expr> Build dependencies of derivation, then start nix-shell\n"
|
<< " :s <expr> Build dependencies of derivation, then start nix-shell\n"
|
||||||
<< " :t <expr> Describe result of evaluation\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") {
|
else if (command == ":a" || command == ":add") {
|
||||||
|
@ -509,6 +510,24 @@ bool NixRepl::processLine(string line)
|
||||||
else if (command == ":q" || command == ":quit")
|
else if (command == ":q" || command == ":quit")
|
||||||
return false;
|
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 != "")
|
else if (command != "")
|
||||||
throw Error("unknown command '%1%'", command);
|
throw Error("unknown command '%1%'", command);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue