[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
06/118: Add primop ‘scopedImport’
From: |
Ludovic Courtès |
Subject: |
06/118: Add primop ‘scopedImport’ |
Date: |
Tue, 19 May 2015 14:45:09 +0000 |
civodul pushed a commit to branch nix
in repository guix.
commit c273c15cb13bb86420dda1e5341a4e19517532b5
Author: Eelco Dolstra <address@hidden>
Date: Mon May 26 13:46:11 2014 +0200
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.
---
src/libexpr/eval.hh | 1 +
src/libexpr/nixexpr.hh | 1 -
src/libexpr/parser.y | 10 ++++++++--
src/libexpr/primops.cc | 25 +++++++++++++++++++++++++
tests/lang/eval-okay-import.exp | 1 +
tests/lang/eval-okay-import.nix | 11 +++++++++++
tests/lang/imported.nix | 3 +++
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 6d4cb8a..ad4c6a4 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 813efbe..fbd5bad 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 dbcffff..06d6d64 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 f402478..533ae37 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 0000000..c508125
--- /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 0000000..0b18d94
--- /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 0000000..fb39ee4
--- /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 0000000..6d0a299
--- /dev/null
+++ b/tests/lang/imported2.nix
@@ -0,0 +1 @@
+range 6 10
- 03/118: Disable parallel.sh test, (continued)
- 03/118: Disable parallel.sh test, Ludovic Courtès, 2015/05/19
- 07/118: Add constant ‘nixPath’, Ludovic Courtès, 2015/05/19
- 14/118: Use std::unordered_set, Ludovic Courtès, 2015/05/19
- 13/118: nix-build: --add-root also takes 1 parameter, Ludovic Courtès, 2015/05/19
- 16/118: Fix test, Ludovic Courtès, 2015/05/19
- 15/118: Sort nixPath attributes, Ludovic Courtès, 2015/05/19
- 10/118: Remove ExprBuiltin, Ludovic Courtès, 2015/05/19
- 09/118: Make the Nix search path declarative, Ludovic Courtès, 2015/05/19
- 04/118: Ugly hack to allow --argstr values starting with a dash, Ludovic Courtès, 2015/05/19
- 19/118: Report daemon OOM better, Ludovic Courtès, 2015/05/19
- 06/118: Add primop ‘scopedImport’,
Ludovic Courtès <=
- 21/118: Add autoloads, make code more concise & idiomatic, Ludovic Courtès, 2015/05/19
- 25/118: Fix bogus warnings about dumping large paths, Ludovic Courtès, 2015/05/19
- 20/118: == operator: Ignore string context, Ludovic Courtès, 2015/05/19
- 17/118: nix-env -qa --json: Generate valid JSON even if there are invalid meta attrs, Ludovic Courtès, 2015/05/19
- 23/118: findFile: Realise the context of the path attributes, Ludovic Courtès, 2015/05/19
- 11/118: Rephrase @ operator description, Ludovic Courtès, 2015/05/19
- 29/118: Merge branch 'shlevy-import-native', Ludovic Courtès, 2015/05/19
- 12/118: dev-shell is a bash script, not sh, Ludovic Courtès, 2015/05/19
- 22/118: Share code between scopedImport and import, Ludovic Courtès, 2015/05/19
- 26/118: Don't use member initialisers, Ludovic Courtès, 2015/05/19