diff options
author | Eelco Dolstra <eelco.dolstra@logicblox.com> | 2014-05-26 13:46:11 +0200 |
---|---|---|
committer | Eelco Dolstra <eelco.dolstra@logicblox.com> | 2014-05-26 14:26:29 +0200 |
commit | c273c15cb13bb86420dda1e5341a4e19517532b5 (patch) | |
tree | 38b670d6fbd64735502fd60525a625c18d8372ad | |
parent | f0fdbd0897ce63c138ec663ed89a94709a8441a7 (diff) |
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.
-rw-r--r-- | src/libexpr/eval.hh | 1 | ||||
-rw-r--r-- | src/libexpr/nixexpr.hh | 1 | ||||
-rw-r--r-- | src/libexpr/parser.y | 10 | ||||
-rw-r--r-- | src/libexpr/primops.cc | 25 | ||||
-rw-r--r-- | tests/lang/eval-okay-import.exp | 1 | ||||
-rw-r--r-- | tests/lang/eval-okay-import.nix | 11 | ||||
-rw-r--r-- | tests/lang/imported.nix | 3 | ||||
-rw-r--r-- | tests/lang/imported2.nix | 1 |
8 files changed, 50 insertions, 3 deletions
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 6d4cb8abe..ad4c6a4b5 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -124,6 +124,7 @@ public: /* Parse a Nix expression from the specified file. */ Expr * parseExprFromFile(const Path & path); + Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv); /* Parse a Nix expression from the specified string. */ Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 813efbe21..fbd5bad81 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -336,5 +336,4 @@ struct StaticEnv }; - } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index dbcffff99..06d6d643f 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -592,7 +592,13 @@ Path resolveExprPath(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('='); string prefix; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index f402478dd..533ae3768 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -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. */ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { @@ -1247,6 +1271,7 @@ void EvalState::createBaseEnv() // Miscellaneous addPrimOp("import", 1, prim_import); + addPrimOp("scopedImport", 2, prim_scopedImport); addPrimOp("__typeOf", 1, prim_typeOf); addPrimOp("isNull", 1, prim_isNull); addPrimOp("__isFunction", 1, prim_isFunction); diff --git a/tests/lang/eval-okay-import.exp b/tests/lang/eval-okay-import.exp new file mode 100644 index 000000000..c508125b5 --- /dev/null +++ b/tests/lang/eval-okay-import.exp @@ -0,0 +1 @@ +[ 1 2 3 4 5 6 7 8 9 10 ] diff --git a/tests/lang/eval-okay-import.nix b/tests/lang/eval-okay-import.nix new file mode 100644 index 000000000..0b18d9413 --- /dev/null +++ b/tests/lang/eval-okay-import.nix @@ -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 diff --git a/tests/lang/imported.nix b/tests/lang/imported.nix new file mode 100644 index 000000000..fb39ee4ef --- /dev/null +++ b/tests/lang/imported.nix @@ -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 diff --git a/tests/lang/imported2.nix b/tests/lang/imported2.nix new file mode 100644 index 000000000..6d0a2992b --- /dev/null +++ b/tests/lang/imported2.nix @@ -0,0 +1 @@ +range 6 10 |