Add primop ‘scopedImport’
‘scopedImport’ works like ‘import’, except that it takes a set of attributes to be added to the lexical scope of the expression, essentially extending or overriding the builtin variables. For instance, the expression scopedImport { x = 1; } ./foo.nix where foo.nix contains ‘x’, will evaluate to 1. This has a few applications: * It allows getting rid of function argument specifications in package expressions. For instance, a package expression like: { stdenv, fetchurl, libfoo }: stdenv.mkDerivation { ... buildInputs = [ libfoo ]; } can now we written as just stdenv.mkDerivation { ... buildInputs = [ libfoo ]; } and imported in all-packages.nix as: bar = scopedImport pkgs ./bar.nix; So whereas we once had dependencies listed in three places (buildInputs, the function, and the call site), they now only need to appear in one place. * It allows overriding builtin functions. For instance, to trace all calls to ‘map’: let overrides = { map = f: xs: builtins.trace "map called!" (map f xs); # Ensure that our override gets propagated by calls to # import/scopedImport. import = fn: scopedImport overrides fn; scopedImport = attrs: fn: scopedImport (overrides // attrs) fn; # Also update ‘builtins’. builtins = builtins // overrides; }; in scopedImport overrides ./bla.nix * Similarly, it allows extending the set of builtin functions. For instance, during Nixpkgs/NixOS evaluation, the Nixpkgs library functions could be added to the default scope. There is a downside: calls to scopedImport are not memoized, unlike import. So importing a file multiple times leads to multiple parsings / evaluations. It would be possible to construct the AST only once, but that would require careful handling of variables/environments.
This commit is contained in:
parent
f0fdbd0897
commit
c273c15cb1
8 changed files with 50 additions and 3 deletions
|
@ -124,6 +124,7 @@ public:
|
||||||
|
|
||||||
/* Parse a Nix expression from the specified file. */
|
/* Parse a Nix expression from the specified file. */
|
||||||
Expr * parseExprFromFile(const Path & path);
|
Expr * parseExprFromFile(const Path & path);
|
||||||
|
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
|
||||||
|
|
||||||
/* Parse a Nix expression from the specified string. */
|
/* Parse a Nix expression from the specified string. */
|
||||||
Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv);
|
Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv);
|
||||||
|
|
|
@ -336,5 +336,4 @@ struct StaticEnv
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -592,7 +592,13 @@ Path resolveExprPath(Path path)
|
||||||
|
|
||||||
Expr * EvalState::parseExprFromFile(const Path & path)
|
Expr * EvalState::parseExprFromFile(const Path & path)
|
||||||
{
|
{
|
||||||
return parse(readFile(path).c_str(), path, dirOf(path), staticBaseEnv);
|
return parseExprFromFile(path, staticBaseEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
|
||||||
|
{
|
||||||
|
return parse(readFile(path).c_str(), path, dirOf(path), staticEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -608,7 +614,7 @@ Expr * EvalState::parseExprFromString(const string & s, const Path & basePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::addToSearchPath(const string & s, bool warn)
|
void EvalState::addToSearchPath(const string & s, bool warn)
|
||||||
{
|
{
|
||||||
size_t pos = s.find('=');
|
size_t pos = s.find('=');
|
||||||
string prefix;
|
string prefix;
|
||||||
|
|
|
@ -93,6 +93,30 @@ static void prim_import(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
|
{
|
||||||
|
PathSet context;
|
||||||
|
state.forceAttrs(*args[0]);
|
||||||
|
Path path = resolveExprPath(state.coerceToPath(pos, *args[1], context));
|
||||||
|
|
||||||
|
Env * env = &state.allocEnv(args[0]->attrs->size());
|
||||||
|
env->up = &state.baseEnv;
|
||||||
|
|
||||||
|
StaticEnv staticEnv(false, &state.staticBaseEnv);
|
||||||
|
|
||||||
|
unsigned int displ = 0;
|
||||||
|
for (auto & attr : *args[0]->attrs) {
|
||||||
|
staticEnv.vars[attr.name] = displ;
|
||||||
|
env->values[displ++] = attr.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
|
||||||
|
Expr * e = state.parseExprFromFile(path, staticEnv);
|
||||||
|
|
||||||
|
e->eval(state, *env, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Return a string representing the type of the expression. */
|
/* Return a string representing the type of the expression. */
|
||||||
static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
|
@ -1247,6 +1271,7 @@ void EvalState::createBaseEnv()
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
addPrimOp("import", 1, prim_import);
|
addPrimOp("import", 1, prim_import);
|
||||||
|
addPrimOp("scopedImport", 2, prim_scopedImport);
|
||||||
addPrimOp("__typeOf", 1, prim_typeOf);
|
addPrimOp("__typeOf", 1, prim_typeOf);
|
||||||
addPrimOp("isNull", 1, prim_isNull);
|
addPrimOp("isNull", 1, prim_isNull);
|
||||||
addPrimOp("__isFunction", 1, prim_isFunction);
|
addPrimOp("__isFunction", 1, prim_isFunction);
|
||||||
|
|
1
tests/lang/eval-okay-import.exp
Normal file
1
tests/lang/eval-okay-import.exp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[ 1 2 3 4 5 6 7 8 9 10 ]
|
11
tests/lang/eval-okay-import.nix
Normal file
11
tests/lang/eval-okay-import.nix
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
let
|
||||||
|
|
||||||
|
overrides = {
|
||||||
|
import = fn: scopedImport overrides fn;
|
||||||
|
|
||||||
|
scopedImport = attrs: fn: scopedImport (overrides // attrs) fn;
|
||||||
|
|
||||||
|
builtins = builtins // overrides;
|
||||||
|
} // import ./lib.nix;
|
||||||
|
|
||||||
|
in scopedImport overrides ./imported.nix
|
3
tests/lang/imported.nix
Normal file
3
tests/lang/imported.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# The function ‘range’ comes from lib.nix and was added to the lexical
|
||||||
|
# scope by scopedImport.
|
||||||
|
range 1 5 ++ import ./imported2.nix
|
1
tests/lang/imported2.nix
Normal file
1
tests/lang/imported2.nix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
range 6 10
|
Loading…
Reference in a new issue