forked from lix-project/lix
Automatically document builtin constants
This is done in roughly the same way builtin functions are documented. Also auto-link experimental features for primops, subsuming PR #8371. Co-authored-by: Eelco Dolstra <edolstra@gmail.com> Co-authored-by: Robert Hensing <roberth@users.noreply.github.com> Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
This commit is contained in:
parent
d40f0e534d
commit
22b278e011
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -18,7 +18,7 @@ perl/Makefile.config
|
||||||
/doc/manual/generated/*
|
/doc/manual/generated/*
|
||||||
/doc/manual/nix.json
|
/doc/manual/nix.json
|
||||||
/doc/manual/conf-file.json
|
/doc/manual/conf-file.json
|
||||||
/doc/manual/builtins.json
|
/doc/manual/language.json
|
||||||
/doc/manual/xp-features.json
|
/doc/manual/xp-features.json
|
||||||
/doc/manual/src/SUMMARY.md
|
/doc/manual/src/SUMMARY.md
|
||||||
/doc/manual/src/command-ref/new-cli
|
/doc/manual/src/command-ref/new-cli
|
||||||
|
@ -26,6 +26,7 @@ perl/Makefile.config
|
||||||
/doc/manual/src/command-ref/experimental-features-shortlist.md
|
/doc/manual/src/command-ref/experimental-features-shortlist.md
|
||||||
/doc/manual/src/contributing/experimental-feature-descriptions.md
|
/doc/manual/src/contributing/experimental-feature-descriptions.md
|
||||||
/doc/manual/src/language/builtins.md
|
/doc/manual/src/language/builtins.md
|
||||||
|
/doc/manual/src/language/builtin-constants.md
|
||||||
|
|
||||||
# /scripts/
|
# /scripts/
|
||||||
/scripts/nix-profile.sh
|
/scripts/nix-profile.sh
|
||||||
|
|
29
doc/manual/generate-builtin-constants.nix
Normal file
29
doc/manual/generate-builtin-constants.nix
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
let
|
||||||
|
inherit (builtins) concatStringsSep attrValues mapAttrs;
|
||||||
|
inherit (import ./utils.nix) optionalString squash;
|
||||||
|
in
|
||||||
|
|
||||||
|
builtinsInfo:
|
||||||
|
let
|
||||||
|
showBuiltin = name: { doc, type, impure-only }:
|
||||||
|
let
|
||||||
|
type' = optionalString (type != null) " (${type})";
|
||||||
|
|
||||||
|
impureNotice = optionalString impure-only ''
|
||||||
|
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
squash ''
|
||||||
|
<dt id="builtin-constants-${name}">
|
||||||
|
<a href="#builtin-constants-${name}"><code>${name}</code>${type'}</a>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
|
||||||
|
${doc}
|
||||||
|
|
||||||
|
${impureNotice}
|
||||||
|
|
||||||
|
</dd>
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo))
|
|
@ -1,14 +1,17 @@
|
||||||
let
|
let
|
||||||
inherit (builtins) concatStringsSep attrNames;
|
inherit (builtins) concatStringsSep attrValues mapAttrs;
|
||||||
|
inherit (import ./utils.nix) optionalString squash;
|
||||||
in
|
in
|
||||||
|
|
||||||
builtinsInfo:
|
builtinsInfo:
|
||||||
let
|
let
|
||||||
showBuiltin = name:
|
showBuiltin = name: { doc, args, arity, experimental-feature }:
|
||||||
let
|
let
|
||||||
inherit (builtinsInfo.${name}) doc args;
|
experimentalNotice = optionalString (experimental-feature != null) ''
|
||||||
|
This function is only available if the [${experimental-feature}](@docroot@/contributing/experimental-features.md#xp-feature-${experimental-feature}) experimental feature is enabled.
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
''
|
squash ''
|
||||||
<dt id="builtins-${name}">
|
<dt id="builtins-${name}">
|
||||||
<a href="#builtins-${name}"><code>${name} ${listArgs args}</code></a>
|
<a href="#builtins-${name}"><code>${name} ${listArgs args}</code></a>
|
||||||
</dt>
|
</dt>
|
||||||
|
@ -16,9 +19,10 @@ let
|
||||||
|
|
||||||
${doc}
|
${doc}
|
||||||
|
|
||||||
|
${experimentalNotice}
|
||||||
|
|
||||||
</dd>
|
</dd>
|
||||||
'';
|
'';
|
||||||
listArgs = args: concatStringsSep " " (map (s: "<var>${s}</var>") args);
|
listArgs = args: concatStringsSep " " (map (s: "<var>${s}</var>") args);
|
||||||
in
|
in
|
||||||
concatStringsSep "\n" (map showBuiltin (attrNames builtinsInfo))
|
concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo))
|
||||||
|
|
||||||
|
|
|
@ -128,14 +128,20 @@ $(d)/xp-features.json: $(bindir)/nix
|
||||||
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp
|
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp
|
||||||
@mv $@.tmp $@
|
@mv $@.tmp $@
|
||||||
|
|
||||||
$(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
|
$(d)/src/language/builtins.md: $(d)/language.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
|
||||||
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
|
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
|
||||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
|
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<)).builtins' >> $@.tmp;
|
||||||
@cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
|
@cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
|
||||||
@mv $@.tmp $@
|
@mv $@.tmp $@
|
||||||
|
|
||||||
$(d)/builtins.json: $(bindir)/nix
|
$(d)/src/language/builtin-constants.md: $(d)/language.json $(d)/generate-builtin-constants.nix $(d)/src/language/builtin-constants-prefix.md $(bindir)/nix
|
||||||
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-builtins > $@.tmp
|
@cat doc/manual/src/language/builtin-constants-prefix.md > $@.tmp
|
||||||
|
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtin-constants.nix (builtins.fromJSON (builtins.readFile $<)).constants' >> $@.tmp;
|
||||||
|
@cat doc/manual/src/language/builtin-constants-suffix.md >> $@.tmp
|
||||||
|
@mv $@.tmp $@
|
||||||
|
|
||||||
|
$(d)/language.json: $(bindir)/nix
|
||||||
|
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-language > $@.tmp
|
||||||
@mv $@.tmp $@
|
@mv $@.tmp $@
|
||||||
|
|
||||||
# Generate the HTML manual.
|
# Generate the HTML manual.
|
||||||
|
@ -167,7 +173,7 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli
|
||||||
done
|
done
|
||||||
@touch $@
|
@touch $@
|
||||||
|
|
||||||
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md
|
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md
|
||||||
$(trace-gen) \
|
$(trace-gen) \
|
||||||
tmp="$$(mktemp -d)"; \
|
tmp="$$(mktemp -d)"; \
|
||||||
cp -r doc/manual "$$tmp"; \
|
cp -r doc/manual "$$tmp"; \
|
||||||
|
|
5
doc/manual/src/language/builtin-constants-prefix.md
Normal file
5
doc/manual/src/language/builtin-constants-prefix.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Built-in Constants
|
||||||
|
|
||||||
|
These constants are built into the Nix language evaluator:
|
||||||
|
|
||||||
|
<dl>
|
1
doc/manual/src/language/builtin-constants-suffix.md
Normal file
1
doc/manual/src/language/builtin-constants-suffix.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
</dl>
|
|
@ -1,44 +0,0 @@
|
||||||
# Built-in Constants
|
|
||||||
|
|
||||||
These constants are built into the Nix language evaluator:
|
|
||||||
|
|
||||||
- [`builtins`]{#builtins-builtins} (attribute set)
|
|
||||||
|
|
||||||
Contains all the [built-in functions](./builtins.md) and values.
|
|
||||||
|
|
||||||
Since built-in functions were added over time, [testing for attributes](./operators.md#has-attribute) in `builtins` can be used for graceful fallback on older Nix installations:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
# if hasContext is not available, we assume `s` has a context
|
|
||||||
if builtins ? hasContext then builtins.hasContext s else true
|
|
||||||
```
|
|
||||||
|
|
||||||
- [`builtins.currentSystem`]{#builtins-currentSystem} (string)
|
|
||||||
|
|
||||||
The built-in value `currentSystem` evaluates to the Nix platform
|
|
||||||
identifier for the Nix installation on which the expression is being
|
|
||||||
evaluated, such as `"i686-linux"` or `"x86_64-darwin"`.
|
|
||||||
|
|
||||||
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
|
||||||
|
|
||||||
- [`builtins.currentTime`]{#builtins-currentTime} (integer)
|
|
||||||
|
|
||||||
Return the [Unix time](https://en.wikipedia.org/wiki/Unix_time) at first evaluation.
|
|
||||||
Repeated references to that name will re-use the initially obtained value.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ nix repl
|
|
||||||
Welcome to Nix 2.15.1 Type :? for help.
|
|
||||||
|
|
||||||
nix-repl> builtins.currentTime
|
|
||||||
1683705525
|
|
||||||
|
|
||||||
nix-repl> builtins.currentTime
|
|
||||||
1683705525
|
|
||||||
```
|
|
||||||
|
|
||||||
The [store path](@docroot@/glossary.md#gloss-store-path) of a derivation depending on `currentTime` will differ for each evaluation.
|
|
||||||
|
|
||||||
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
|
|
@ -703,28 +703,34 @@ Path EvalState::toRealPath(const Path & path, const NixStringContext & context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value * EvalState::addConstant(const std::string & name, Value & v)
|
Value * EvalState::addConstant(const std::string & name, Value & v, Constant info)
|
||||||
{
|
{
|
||||||
Value * v2 = allocValue();
|
Value * v2 = allocValue();
|
||||||
*v2 = v;
|
*v2 = v;
|
||||||
addConstant(name, v2);
|
addConstant(name, v2, info);
|
||||||
return v2;
|
return v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::addConstant(const std::string & name, Value * v)
|
void EvalState::addConstant(const std::string & name, Value * v, Constant info)
|
||||||
{
|
{
|
||||||
|
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
|
||||||
|
|
||||||
|
constantInfos.push_back({name2, info});
|
||||||
|
|
||||||
|
if (!(evalSettings.pureEval && info.impureOnly)) {
|
||||||
|
/* Check the type, if possible.
|
||||||
|
|
||||||
|
We might know the type of a thunk in advance, so be allowed
|
||||||
|
to just write it down in that case. */
|
||||||
|
if (auto gotType = v->type(true); gotType != nThunk)
|
||||||
|
assert(info.type == gotType);
|
||||||
|
|
||||||
|
/* Install value the base environment. */
|
||||||
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
||||||
baseEnv.values[baseEnvDispl++] = v;
|
baseEnv.values[baseEnvDispl++] = v;
|
||||||
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
|
|
||||||
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
|
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value * EvalState::addPrimOp(const std::string & name,
|
|
||||||
size_t arity, PrimOpFun primOp)
|
|
||||||
{
|
|
||||||
return addPrimOp(PrimOp { .fun = primOp, .arity = arity, .name = name });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -738,7 +744,10 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
||||||
vPrimOp->mkPrimOp(new PrimOp(primOp));
|
vPrimOp->mkPrimOp(new PrimOp(primOp));
|
||||||
Value v;
|
Value v;
|
||||||
v.mkApp(vPrimOp, vPrimOp);
|
v.mkApp(vPrimOp, vPrimOp);
|
||||||
return addConstant(primOp.name, v);
|
return addConstant(primOp.name, v, {
|
||||||
|
.type = nThunk, // FIXME
|
||||||
|
.doc = primOp.doc,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto envName = symbols.create(primOp.name);
|
auto envName = symbols.create(primOp.name);
|
||||||
|
@ -764,13 +773,13 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||||
{
|
{
|
||||||
if (v.isPrimOp()) {
|
if (v.isPrimOp()) {
|
||||||
auto v2 = &v;
|
auto v2 = &v;
|
||||||
if (v2->primOp->doc)
|
if (auto * doc = v2->primOp->doc)
|
||||||
return Doc {
|
return Doc {
|
||||||
.pos = {},
|
.pos = {},
|
||||||
.name = v2->primOp->name,
|
.name = v2->primOp->name,
|
||||||
.arity = v2->primOp->arity,
|
.arity = v2->primOp->arity,
|
||||||
.args = v2->primOp->args,
|
.args = v2->primOp->args,
|
||||||
.doc = v2->primOp->doc,
|
.doc = doc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -25,15 +25,72 @@ struct DerivedPath;
|
||||||
enum RepairFlag : bool;
|
enum RepairFlag : bool;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that implements a primop.
|
||||||
|
*/
|
||||||
typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Info about a primitive operation, and its implementation
|
||||||
|
*/
|
||||||
struct PrimOp
|
struct PrimOp
|
||||||
{
|
{
|
||||||
PrimOpFun fun;
|
/**
|
||||||
size_t arity;
|
* Name of the primop. `__` prefix is treated specially.
|
||||||
|
*/
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Names of the parameters of a primop, for primops that take a
|
||||||
|
* fixed number of arguments to be substituted for these parameters.
|
||||||
|
*/
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aritiy of the primop.
|
||||||
|
*
|
||||||
|
* If `args` is not empty, this field will be computed from that
|
||||||
|
* field instead, so it doesn't need to be manually set.
|
||||||
|
*/
|
||||||
|
size_t arity = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional free-form documentation about the primop.
|
||||||
|
*/
|
||||||
const char * doc = nullptr;
|
const char * doc = nullptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the primop.
|
||||||
|
*/
|
||||||
|
PrimOpFun fun;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional experimental for this to be gated on.
|
||||||
|
*/
|
||||||
|
std::optional<ExperimentalFeature> experimentalFeature;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Info about a constant
|
||||||
|
*/
|
||||||
|
struct Constant
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Optional type of the constant (known since it is a fixed value).
|
||||||
|
*
|
||||||
|
* @todo we should use an enum for this.
|
||||||
|
*/
|
||||||
|
ValueType type = nThunk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional free-form documentation about the constant.
|
||||||
|
*/
|
||||||
|
const char * doc = nullptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the constant is impure, and not available in pure mode.
|
||||||
|
*/
|
||||||
|
bool impureOnly = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
|
@ -513,18 +570,23 @@ public:
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<StaticEnv> staticBaseEnv; // !!! should be private
|
std::shared_ptr<StaticEnv> staticBaseEnv; // !!! should be private
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name and documentation about every constant.
|
||||||
|
*
|
||||||
|
* Constants from primops are hard to crawl, and their docs will go
|
||||||
|
* here too.
|
||||||
|
*/
|
||||||
|
std::vector<std::pair<std::string, Constant>> constantInfos;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
unsigned int baseEnvDispl = 0;
|
unsigned int baseEnvDispl = 0;
|
||||||
|
|
||||||
void createBaseEnv();
|
void createBaseEnv();
|
||||||
|
|
||||||
Value * addConstant(const std::string & name, Value & v);
|
Value * addConstant(const std::string & name, Value & v, Constant info);
|
||||||
|
|
||||||
void addConstant(const std::string & name, Value * v);
|
void addConstant(const std::string & name, Value * v, Constant info);
|
||||||
|
|
||||||
Value * addPrimOp(const std::string & name,
|
|
||||||
size_t arity, PrimOpFun primOp);
|
|
||||||
|
|
||||||
Value * addPrimOp(PrimOp && primOp);
|
Value * addPrimOp(PrimOp && primOp);
|
||||||
|
|
||||||
|
@ -538,6 +600,10 @@ public:
|
||||||
std::optional<std::string> name;
|
std::optional<std::string> name;
|
||||||
size_t arity;
|
size_t arity;
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
|
/**
|
||||||
|
* Unlike the other `doc` fields in this file, this one should never be
|
||||||
|
* `null`.
|
||||||
|
*/
|
||||||
const char * doc;
|
const char * doc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -740,7 +806,12 @@ struct EvalSettings : Config
|
||||||
|
|
||||||
Setting<Strings> nixPath{
|
Setting<Strings> nixPath{
|
||||||
this, getDefaultNixPath(), "nix-path",
|
this, getDefaultNixPath(), "nix-path",
|
||||||
"List of directories to be searched for `<...>` file references."};
|
R"(
|
||||||
|
List of directories to be searched for `<...>` file references
|
||||||
|
|
||||||
|
In particular, outside of [pure evaluation mode](#conf-pure-evaluation), this determines the value of
|
||||||
|
[`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtin-constants-nixPath).
|
||||||
|
)"};
|
||||||
|
|
||||||
Setting<bool> restrictEval{
|
Setting<bool> restrictEval{
|
||||||
this, false, "restrict-eval",
|
this, false, "restrict-eval",
|
||||||
|
|
|
@ -788,9 +788,6 @@ static RegisterPrimOp r2({
|
||||||
```nix
|
```nix
|
||||||
(builtins.getFlake "github:edolstra/dwarffs").rev
|
(builtins.getFlake "github:edolstra/dwarffs").rev
|
||||||
```
|
```
|
||||||
|
|
||||||
This function is only available if you enable the experimental feature
|
|
||||||
`flakes`.
|
|
||||||
)",
|
)",
|
||||||
.fun = prim_getFlake,
|
.fun = prim_getFlake,
|
||||||
.experimentalFeature = Xp::Flakes,
|
.experimentalFeature = Xp::Flakes,
|
||||||
|
|
|
@ -238,7 +238,7 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_scopedImport(RegisterPrimOp::Info {
|
static RegisterPrimOp primop_scopedImport(PrimOp {
|
||||||
.name = "scopedImport",
|
.name = "scopedImport",
|
||||||
.arity = 2,
|
.arity = 2,
|
||||||
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
|
@ -692,7 +692,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
|
||||||
v.listElems()[n++] = i;
|
v.listElems()[n++] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info {
|
static RegisterPrimOp primop_genericClosure(PrimOp {
|
||||||
.name = "__genericClosure",
|
.name = "__genericClosure",
|
||||||
.args = {"attrset"},
|
.args = {"attrset"},
|
||||||
.arity = 1,
|
.arity = 1,
|
||||||
|
@ -809,7 +809,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info {
|
static RegisterPrimOp primop_addErrorContext(PrimOp {
|
||||||
.name = "__addErrorContext",
|
.name = "__addErrorContext",
|
||||||
.arity = 2,
|
.arity = 2,
|
||||||
.fun = prim_addErrorContext,
|
.fun = prim_addErrorContext,
|
||||||
|
@ -1400,7 +1400,7 @@ drvName, Bindings * attrs, Value & v)
|
||||||
v.mkAttrs(result);
|
v.mkAttrs(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info {
|
static RegisterPrimOp primop_derivationStrict(PrimOp {
|
||||||
.name = "derivationStrict",
|
.name = "derivationStrict",
|
||||||
.arity = 1,
|
.arity = 1,
|
||||||
.fun = prim_derivationStrict,
|
.fun = prim_derivationStrict,
|
||||||
|
@ -1667,9 +1667,52 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V
|
||||||
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
|
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_findFile(RegisterPrimOp::Info {
|
static RegisterPrimOp primop_findFile(PrimOp {
|
||||||
.name = "__findFile",
|
.name = "__findFile",
|
||||||
.arity = 2,
|
.args = {"search path", "lookup path"},
|
||||||
|
.doc = R"(
|
||||||
|
Look up the given path with the given search path.
|
||||||
|
|
||||||
|
A search path is represented list of [attribute sets](./values.md#attribute-set) with two attributes, `prefix`, and `path`.
|
||||||
|
`prefix` is a relative path.
|
||||||
|
`path` denotes a file system location; the exact syntax depends on the command line interface.
|
||||||
|
|
||||||
|
Examples of search path attribute sets:
|
||||||
|
|
||||||
|
- ```
|
||||||
|
{
|
||||||
|
prefix = "nixos-config";
|
||||||
|
path = "/etc/nixos/configuration.nix";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- ```
|
||||||
|
{
|
||||||
|
prefix = "";
|
||||||
|
path = "/nix/var/nix/profiles/per-user/root/channels";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/values.html#type-path) of the match.
|
||||||
|
|
||||||
|
This is the process for each entry:
|
||||||
|
If the lookup path matches `prefix`, then the remainder of the lookup path (the "suffix") is searched for within the directory denoted by `patch`.
|
||||||
|
Note that the `path` may need to be downloaded at this point to look inside.
|
||||||
|
If the suffix is found inside that directory, then the entry is a match;
|
||||||
|
the combined absolute path of the directory (now downloaded if need be) and the suffix is returned.
|
||||||
|
|
||||||
|
The syntax
|
||||||
|
|
||||||
|
```nix
|
||||||
|
<nixpkgs>
|
||||||
|
```
|
||||||
|
|
||||||
|
is equivalent to:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
builtins.findFile builtins.nixPath "nixpkgs"
|
||||||
|
```
|
||||||
|
)",
|
||||||
.fun = prim_findFile,
|
.fun = prim_findFile,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2388,7 +2431,7 @@ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * *
|
||||||
state.mkPos(v, i->pos);
|
state.mkPos(v, i->pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
|
static RegisterPrimOp primop_unsafeGetAttrPos(PrimOp {
|
||||||
.name = "__unsafeGetAttrPos",
|
.name = "__unsafeGetAttrPos",
|
||||||
.arity = 2,
|
.arity = 2,
|
||||||
.fun = prim_unsafeGetAttrPos,
|
.fun = prim_unsafeGetAttrPos,
|
||||||
|
@ -4061,10 +4104,10 @@ static RegisterPrimOp primop_splitVersion({
|
||||||
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
|
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
|
||||||
|
|
||||||
|
|
||||||
RegisterPrimOp::RegisterPrimOp(Info && info)
|
RegisterPrimOp::RegisterPrimOp(PrimOp && primOp)
|
||||||
{
|
{
|
||||||
if (!primOps) primOps = new PrimOps;
|
if (!primOps) primOps = new PrimOps;
|
||||||
primOps->push_back(std::move(info));
|
primOps->push_back(std::move(primOp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4077,54 +4120,202 @@ void EvalState::createBaseEnv()
|
||||||
|
|
||||||
/* `builtins' must be first! */
|
/* `builtins' must be first! */
|
||||||
v.mkAttrs(buildBindings(128).finish());
|
v.mkAttrs(buildBindings(128).finish());
|
||||||
addConstant("builtins", v);
|
addConstant("builtins", v, {
|
||||||
|
.type = nAttrs,
|
||||||
|
.doc = R"(
|
||||||
|
Contains all the [built-in functions](@docroot@/language/builtins.md) and values.
|
||||||
|
|
||||||
|
Since built-in functions were added over time, [testing for attributes](./operators.md#has-attribute) in `builtins` can be used for graceful fallback on older Nix installations:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# if hasContext is not available, we assume `s` has a context
|
||||||
|
if builtins ? hasContext then builtins.hasContext s else true
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
});
|
||||||
|
|
||||||
v.mkBool(true);
|
v.mkBool(true);
|
||||||
addConstant("true", v);
|
addConstant("true", v, {
|
||||||
|
.type = nBool,
|
||||||
|
.doc = R"(
|
||||||
|
Primitive value.
|
||||||
|
|
||||||
|
It can be returned by
|
||||||
|
[comparison operators](@docroot@/language/operators.md#Comparison)
|
||||||
|
and used in
|
||||||
|
[conditional expressions](@docroot@/language/constructs.md#Conditionals).
|
||||||
|
|
||||||
|
The name `true` is not special, and can be shadowed:
|
||||||
|
|
||||||
|
```nix-repl
|
||||||
|
nix-repl> let true = 1; in true
|
||||||
|
1
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
});
|
||||||
|
|
||||||
v.mkBool(false);
|
v.mkBool(false);
|
||||||
addConstant("false", v);
|
addConstant("false", v, {
|
||||||
|
.type = nBool,
|
||||||
|
.doc = R"(
|
||||||
|
Primitive value.
|
||||||
|
|
||||||
|
It can be returned by
|
||||||
|
[comparison operators](@docroot@/language/operators.md#Comparison)
|
||||||
|
and used in
|
||||||
|
[conditional expressions](@docroot@/language/constructs.md#Conditionals).
|
||||||
|
|
||||||
|
The name `false` is not special, and can be shadowed:
|
||||||
|
|
||||||
|
```nix-repl
|
||||||
|
nix-repl> let false = 1; in false
|
||||||
|
1
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
});
|
||||||
|
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
addConstant("null", v);
|
addConstant("null", v, {
|
||||||
|
.type = nNull,
|
||||||
|
.doc = R"(
|
||||||
|
Primitive value.
|
||||||
|
|
||||||
|
The name `null` is not special, and can be shadowed:
|
||||||
|
|
||||||
|
```nix-repl
|
||||||
|
nix-repl> let null = 1; in null
|
||||||
|
1
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
});
|
||||||
|
|
||||||
if (!evalSettings.pureEval) {
|
if (!evalSettings.pureEval) {
|
||||||
v.mkInt(time(0));
|
v.mkInt(time(0));
|
||||||
addConstant("__currentTime", v);
|
|
||||||
|
|
||||||
v.mkString(settings.thisSystem.get());
|
|
||||||
addConstant("__currentSystem", v);
|
|
||||||
}
|
}
|
||||||
|
addConstant("__currentTime", v, {
|
||||||
|
.type = nInt,
|
||||||
|
.doc = R"(
|
||||||
|
Return the [Unix time](https://en.wikipedia.org/wiki/Unix_time) at first evaluation.
|
||||||
|
Repeated references to that name will re-use the initially obtained value.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix repl
|
||||||
|
Welcome to Nix 2.15.1 Type :? for help.
|
||||||
|
|
||||||
|
nix-repl> builtins.currentTime
|
||||||
|
1683705525
|
||||||
|
|
||||||
|
nix-repl> builtins.currentTime
|
||||||
|
1683705525
|
||||||
|
```
|
||||||
|
|
||||||
|
The [store path](@docroot@/glossary.md#gloss-store-path) of a derivation depending on `currentTime` will differ for each evaluation, unless both evaluate `builtins.currentTime` in the same second.
|
||||||
|
)",
|
||||||
|
.impureOnly = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!evalSettings.pureEval) {
|
||||||
|
v.mkString(settings.thisSystem.get());
|
||||||
|
}
|
||||||
|
addConstant("__currentSystem", v, {
|
||||||
|
.type = nString,
|
||||||
|
.doc = R"(
|
||||||
|
The value of the [`system` configuration option](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
||||||
|
|
||||||
|
It can be used to set the `system` attribute for [`builtins.derivation`](@docroot@/language/derivations.md) such that the resulting derivation can be built on the same system that evaluates the Nix expression:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
builtins.derivation {
|
||||||
|
# ...
|
||||||
|
system = builtins.currentSystem;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It can be overridden in order to create derivations for different system than the current one:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix-instantiate --system "mips64-linux" --eval --expr 'builtins.currentSystem'
|
||||||
|
"mips64-linux"
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
.impureOnly = true,
|
||||||
|
});
|
||||||
|
|
||||||
v.mkString(nixVersion);
|
v.mkString(nixVersion);
|
||||||
addConstant("__nixVersion", v);
|
addConstant("__nixVersion", v, {
|
||||||
|
.type = nString,
|
||||||
|
.doc = R"(
|
||||||
|
The version of Nix.
|
||||||
|
|
||||||
|
For example, where the command line returns the current Nix version,
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ nix --version
|
||||||
|
nix (Nix) 2.16.0
|
||||||
|
```
|
||||||
|
|
||||||
|
the Nix language evaluator returns the same value:
|
||||||
|
|
||||||
|
```nix-repl
|
||||||
|
nix-repl> builtins.nixVersion
|
||||||
|
"2.16.0"
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
});
|
||||||
|
|
||||||
v.mkString(store->storeDir);
|
v.mkString(store->storeDir);
|
||||||
addConstant("__storeDir", v);
|
addConstant("__storeDir", v, {
|
||||||
|
.type = nString,
|
||||||
|
.doc = R"(
|
||||||
|
Logical file system location of the [Nix store](@docroot@/glossary.md#gloss-store) currently in use.
|
||||||
|
|
||||||
|
This value is determined by the `store` parameter in [Store URLs](@docroot@/command-ref/new-cli/nix3-help-stores.md):
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ nix-instantiate --store 'dummy://?store=/blah' --eval --expr builtins.storeDir
|
||||||
|
"/blah"
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
});
|
||||||
|
|
||||||
/* Language version. This should be increased every time a new
|
/* Language version. This should be increased every time a new
|
||||||
language feature gets added. It's not necessary to increase it
|
language feature gets added. It's not necessary to increase it
|
||||||
when primops get added, because you can just use `builtins ?
|
when primops get added, because you can just use `builtins ?
|
||||||
primOp' to check. */
|
primOp' to check. */
|
||||||
v.mkInt(6);
|
v.mkInt(6);
|
||||||
addConstant("__langVersion", v);
|
addConstant("__langVersion", v, {
|
||||||
|
.type = nInt,
|
||||||
|
.doc = R"(
|
||||||
|
The current version of the Nix language.
|
||||||
|
)",
|
||||||
|
});
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
if (evalSettings.enableNativeCode) {
|
if (evalSettings.enableNativeCode) {
|
||||||
addPrimOp("__importNative", 2, prim_importNative);
|
addPrimOp({
|
||||||
addPrimOp("__exec", 1, prim_exec);
|
.name = "__importNative",
|
||||||
|
.arity = 2,
|
||||||
|
.fun = prim_importNative,
|
||||||
|
});
|
||||||
|
addPrimOp({
|
||||||
|
.name = "__exec",
|
||||||
|
.arity = 1,
|
||||||
|
.fun = prim_exec,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addPrimOp({
|
addPrimOp({
|
||||||
.fun = evalSettings.traceVerbose ? prim_trace : prim_second,
|
|
||||||
.arity = 2,
|
|
||||||
.name = "__traceVerbose",
|
.name = "__traceVerbose",
|
||||||
.args = { "e1", "e2" },
|
.args = { "e1", "e2" },
|
||||||
|
.arity = 2,
|
||||||
.doc = R"(
|
.doc = R"(
|
||||||
Evaluate *e1* and print its abstract syntax representation on standard
|
Evaluate *e1* and print its abstract syntax representation on standard
|
||||||
error if `--trace-verbose` is enabled. Then return *e2*. This function
|
error if `--trace-verbose` is enabled. Then return *e2*. This function
|
||||||
is useful for debugging.
|
is useful for debugging.
|
||||||
)",
|
)",
|
||||||
|
.fun = evalSettings.traceVerbose ? prim_trace : prim_second,
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Add a value containing the current Nix expression search path. */
|
/* Add a value containing the current Nix expression search path. */
|
||||||
|
@ -4136,26 +4327,46 @@ void EvalState::createBaseEnv()
|
||||||
attrs.alloc("prefix").mkString(i.prefix);
|
attrs.alloc("prefix").mkString(i.prefix);
|
||||||
(v.listElems()[n++] = allocValue())->mkAttrs(attrs);
|
(v.listElems()[n++] = allocValue())->mkAttrs(attrs);
|
||||||
}
|
}
|
||||||
addConstant("__nixPath", v);
|
addConstant("__nixPath", v, {
|
||||||
|
.type = nList,
|
||||||
|
.doc = R"(
|
||||||
|
The search path used to resolve angle bracket path lookups.
|
||||||
|
|
||||||
|
Angle bracket expressions can be
|
||||||
|
[desugared](https://en.wikipedia.org/wiki/Syntactic_sugar)
|
||||||
|
using this and
|
||||||
|
[`builtins.findFile`](./builtins.html#builtins-findFile):
|
||||||
|
|
||||||
|
```nix
|
||||||
|
<nixpkgs>
|
||||||
|
```
|
||||||
|
|
||||||
|
is equivalent to:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
builtins.findFile builtins.nixPath "nixpkgs"
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
});
|
||||||
|
|
||||||
if (RegisterPrimOp::primOps)
|
if (RegisterPrimOp::primOps)
|
||||||
for (auto & primOp : *RegisterPrimOp::primOps)
|
for (auto & primOp : *RegisterPrimOp::primOps)
|
||||||
if (!primOp.experimentalFeature
|
if (experimentalFeatureSettings.isEnabled(primOp.experimentalFeature))
|
||||||
|| experimentalFeatureSettings.isEnabled(*primOp.experimentalFeature))
|
|
||||||
{
|
{
|
||||||
addPrimOp({
|
auto primOpAdjusted = primOp;
|
||||||
.fun = primOp.fun,
|
primOpAdjusted.arity = std::max(primOp.args.size(), primOp.arity);
|
||||||
.arity = std::max(primOp.args.size(), primOp.arity),
|
addPrimOp(std::move(primOpAdjusted));
|
||||||
.name = primOp.name,
|
|
||||||
.args = primOp.args,
|
|
||||||
.doc = primOp.doc,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add a wrapper around the derivation primop that computes the
|
/* Add a wrapper around the derivation primop that computes the
|
||||||
`drvPath' and `outPath' attributes lazily. */
|
`drvPath' and `outPath' attributes lazily.
|
||||||
|
|
||||||
|
Null docs because it is documented separately.
|
||||||
|
*/
|
||||||
auto vDerivation = allocValue();
|
auto vDerivation = allocValue();
|
||||||
addConstant("derivation", vDerivation);
|
addConstant("derivation", vDerivation, {
|
||||||
|
.type = nFunction,
|
||||||
|
});
|
||||||
|
|
||||||
/* 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,17 +10,7 @@ namespace nix {
|
||||||
|
|
||||||
struct RegisterPrimOp
|
struct RegisterPrimOp
|
||||||
{
|
{
|
||||||
struct Info
|
typedef std::vector<PrimOp> PrimOps;
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
std::vector<std::string> args;
|
|
||||||
size_t arity = 0;
|
|
||||||
const char * doc;
|
|
||||||
PrimOpFun fun;
|
|
||||||
std::optional<ExperimentalFeature> experimentalFeature;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::vector<Info> PrimOps;
|
|
||||||
static PrimOps * primOps;
|
static PrimOps * primOps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,7 +18,7 @@ struct RegisterPrimOp
|
||||||
* will get called during EvalState initialization, so there
|
* will get called during EvalState initialization, so there
|
||||||
* may be primops not yet added and builtins is not yet sorted.
|
* may be primops not yet added and builtins is not yet sorted.
|
||||||
*/
|
*/
|
||||||
RegisterPrimOp(Info && info);
|
RegisterPrimOp(PrimOp && primOp);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* These primops are disabled without enableNativeCode, but plugins
|
/* These primops are disabled without enableNativeCode, but plugins
|
||||||
|
|
|
@ -154,9 +154,6 @@ static RegisterPrimOp primop_fetchClosure({
|
||||||
specifying a binary cache from which the path can be fetched.
|
specifying a binary cache from which the path can be fetched.
|
||||||
Also, requiring a content-addressed final store path avoids the
|
Also, requiring a content-addressed final store path avoids the
|
||||||
need for users to configure binary cache public keys.
|
need for users to configure binary cache public keys.
|
||||||
|
|
||||||
This function is only available if you enable the experimental
|
|
||||||
feature `fetch-closure`.
|
|
||||||
)",
|
)",
|
||||||
.fun = prim_fetchClosure,
|
.fun = prim_fetchClosure,
|
||||||
.experimentalFeature = Xp::FetchClosure,
|
.experimentalFeature = Xp::FetchClosure,
|
||||||
|
|
|
@ -218,8 +218,11 @@ public:
|
||||||
/**
|
/**
|
||||||
* Returns the normal type of a Value. This only returns nThunk if
|
* Returns the normal type of a Value. This only returns nThunk if
|
||||||
* the Value hasn't been forceValue'd
|
* the Value hasn't been forceValue'd
|
||||||
|
*
|
||||||
|
* @param invalidIsThunk Instead of aborting an an invalid (probably
|
||||||
|
* 0, so uninitialized) internal type, return `nThunk`.
|
||||||
*/
|
*/
|
||||||
inline ValueType type() const
|
inline ValueType type(bool invalidIsThunk = false) const
|
||||||
{
|
{
|
||||||
switch (internalType) {
|
switch (internalType) {
|
||||||
case tInt: return nInt;
|
case tInt: return nInt;
|
||||||
|
@ -234,6 +237,9 @@ public:
|
||||||
case tFloat: return nFloat;
|
case tFloat: return nFloat;
|
||||||
case tThunk: case tApp: case tBlackhole: return nThunk;
|
case tThunk: case tApp: case tBlackhole: return nThunk;
|
||||||
}
|
}
|
||||||
|
if (invalidIsThunk)
|
||||||
|
return nThunk;
|
||||||
|
else
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -352,7 +352,7 @@ void mainWrapped(int argc, char * * argv)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 2 && std::string(argv[1]) == "__dump-builtins") {
|
if (argc == 2 && std::string(argv[1]) == "__dump-language") {
|
||||||
experimentalFeatureSettings.experimentalFeatures = {
|
experimentalFeatureSettings.experimentalFeatures = {
|
||||||
Xp::Flakes,
|
Xp::Flakes,
|
||||||
Xp::FetchClosure,
|
Xp::FetchClosure,
|
||||||
|
@ -360,6 +360,8 @@ void mainWrapped(int argc, char * * argv)
|
||||||
evalSettings.pureEval = false;
|
evalSettings.pureEval = false;
|
||||||
EvalState state({}, openStore("dummy://"));
|
EvalState state({}, openStore("dummy://"));
|
||||||
auto res = nlohmann::json::object();
|
auto res = nlohmann::json::object();
|
||||||
|
res["builtins"] = ({
|
||||||
|
auto builtinsJson = nlohmann::json::object();
|
||||||
auto builtins = state.baseEnv.values[0]->attrs;
|
auto builtins = state.baseEnv.values[0]->attrs;
|
||||||
for (auto & builtin : *builtins) {
|
for (auto & builtin : *builtins) {
|
||||||
auto b = nlohmann::json::object();
|
auto b = nlohmann::json::object();
|
||||||
|
@ -369,8 +371,23 @@ void mainWrapped(int argc, char * * argv)
|
||||||
b["arity"] = primOp->arity;
|
b["arity"] = primOp->arity;
|
||||||
b["args"] = primOp->args;
|
b["args"] = primOp->args;
|
||||||
b["doc"] = trim(stripIndentation(primOp->doc));
|
b["doc"] = trim(stripIndentation(primOp->doc));
|
||||||
res[state.symbols[builtin.name]] = std::move(b);
|
b["experimental-feature"] = primOp->experimentalFeature;
|
||||||
|
builtinsJson[state.symbols[builtin.name]] = std::move(b);
|
||||||
}
|
}
|
||||||
|
std::move(builtinsJson);
|
||||||
|
});
|
||||||
|
res["constants"] = ({
|
||||||
|
auto constantsJson = nlohmann::json::object();
|
||||||
|
for (auto & [name, info] : state.constantInfos) {
|
||||||
|
auto c = nlohmann::json::object();
|
||||||
|
if (!info.doc) continue;
|
||||||
|
c["doc"] = trim(stripIndentation(info.doc));
|
||||||
|
c["type"] = showType(info.type, false);
|
||||||
|
c["impure-only"] = info.impureOnly;
|
||||||
|
constantsJson[name] = std::move(c);
|
||||||
|
}
|
||||||
|
std::move(constantsJson);
|
||||||
|
});
|
||||||
logger->cout("%s", res);
|
logger->cout("%s", res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue