about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libexpr/primops.cc66
-rw-r--r--tests/lang/eval-okay-functionargs.exp.xml15
-rw-r--r--tests/lang/eval-okay-functionargs.nix80
3 files changed, 161 insertions, 0 deletions
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 783d26c448..6408ca9569 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -812,6 +812,70 @@ static Expr prim_isAttrs(EvalState & state, const ATermVector & args)
 }
 
 
+/* Return the right-biased intersection of two attribute sets as1 and
+   as2, i.e. a set that contains every attribute from as2 that is also
+   a member of as1. */
+static Expr prim_intersectAttrs(EvalState & state, const ATermVector & args)
+{
+    ATermMap as1, as2;
+    queryAllAttrs(evalExpr(state, args[0]), as1, true);
+    queryAllAttrs(evalExpr(state, args[1]), as2, true);
+
+    ATermMap res;
+    foreach (ATermMap::const_iterator, i, as2)
+        if (as1[i->key]) res.set(i->key, i->value);
+
+    return makeAttrs(res);
+}
+
+
+static void attrsInPattern(ATermMap & map, Pattern pat)
+{
+    ATerm name;
+    ATermList formals;
+    Pattern pat1, pat2;
+    ATermBool ellipsis;
+    if (matchAttrsPat(pat, formals, ellipsis)) { 
+        for (ATermIterator i(formals); i; ++i) {
+            ATerm def;
+            if (!matchFormal(*i, name, def)) abort();
+            map.set(name, makeAttrRHS(makeBool(def != constNoDefaultValue), makeNoPos()));
+        }
+    }
+    else if (matchAtPat(pat, pat1, pat2)) {
+        attrsInPattern(map, pat1);
+        attrsInPattern(map, pat2);
+    }
+}
+
+
+/* Return a set containing the names of the formal arguments expected
+   by the function `f'.  The value of each attribute is a Boolean
+   denoting whether has a default value.  For instance,
+
+      functionArgs ({ x, y ? 123}: ...)
+   => { x = false; y = true; }
+
+   "Formal argument" here refers to the attributes pattern-matched by
+   the function.  Plain lambdas are not included, e.g.
+
+      functionArgs (x: ...)
+   => { }
+*/
+static Expr prim_functionArgs(EvalState & state, const ATermVector & args)
+{
+    Expr f = evalExpr(state, args[0]);
+    ATerm pat, body, pos;
+    if (!matchFunction(f, pat, body, pos))
+        throw TypeError("`functionArgs' required a function");
+    
+    ATermMap as;
+    attrsInPattern(as, pat);
+
+    return makeAttrs(as);
+}
+
+
 /*************************************************************
  * Lists
  *************************************************************/
@@ -1070,6 +1134,8 @@ void EvalState::addPrimOps()
     addPrimOp("__isAttrs", 1, prim_isAttrs);
     addPrimOp("removeAttrs", 2, prim_removeAttrs);
     addPrimOp("__listToAttrs", 1, prim_listToAttrs);
+    addPrimOp("__intersectAttrs", 2, prim_intersectAttrs);
+    addPrimOp("__functionArgs", 1, prim_functionArgs);
 
     // Lists
     addPrimOp("__isList", 1, prim_isList);
diff --git a/tests/lang/eval-okay-functionargs.exp.xml b/tests/lang/eval-okay-functionargs.exp.xml
new file mode 100644
index 0000000000..651f54c363
--- /dev/null
+++ b/tests/lang/eval-okay-functionargs.exp.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <list>
+    <string value="stdenv" />
+    <string value="fetchurl" />
+    <string value="aterm-stdenv" />
+    <string value="aterm-stdenv2" />
+    <string value="libX11" />
+    <string value="libXv" />
+    <string value="mplayer-stdenv2.libXv-libX11" />
+    <string value="mplayer-stdenv2.libXv-libX11_2" />
+    <string value="nix-stdenv-aterm-stdenv" />
+    <string value="nix-stdenv2-aterm2-stdenv2" />
+  </list>
+</expr>
diff --git a/tests/lang/eval-okay-functionargs.nix b/tests/lang/eval-okay-functionargs.nix
new file mode 100644
index 0000000000..68dca62ee1
--- /dev/null
+++ b/tests/lang/eval-okay-functionargs.nix
@@ -0,0 +1,80 @@
+let
+
+  stdenvFun = { }: { name = "stdenv"; };
+  stdenv2Fun = { }: { name = "stdenv2"; };
+  fetchurlFun = { stdenv }: assert stdenv.name == "stdenv"; { name = "fetchurl"; };
+  atermFun = { stdenv, fetchurl }: { name = "aterm-${stdenv.name}"; };
+  aterm2Fun = { stdenv, fetchurl }: { name = "aterm2-${stdenv.name}"; };
+  nixFun = { stdenv, fetchurl, aterm }: { name = "nix-${stdenv.name}-${aterm.name}"; };
+  
+  mplayerFun =
+    { stdenv, fetchurl, enableX11 ? false, xorg ? null, enableFoo ? true, foo ? null  }:
+    assert stdenv.name == "stdenv2";
+    assert enableX11 -> xorg.libXv.name == "libXv";
+    assert enableFoo -> foo != null;
+    { name = "mplayer-${stdenv.name}.${xorg.libXv.name}-${xorg.libX11.name}"; };
+
+  makeOverridable = f: origArgs: f origArgs //
+    { override = newArgs:
+        makeOverridable f (origArgs // (if builtins.isFunction newArgs then newArgs origArgs else newArgs));
+    };
+    
+  callPackage_ = pkgs: f: args:
+    makeOverridable f ((builtins.intersectAttrs (builtins.functionArgs f) pkgs) // args);
+
+  allPackages =
+    { overrides ? (pkgs: pkgsPrev: { }) }:
+    let
+      callPackage = callPackage_ pkgs;
+      pkgs = pkgsStd // (overrides pkgs pkgsStd);
+      pkgsStd = {
+        inherit pkgs;
+        stdenv = callPackage stdenvFun { };
+        stdenv2 = callPackage stdenv2Fun { };
+        fetchurl = callPackage fetchurlFun { };
+        aterm = callPackage atermFun { };
+        xorg = callPackage xorgFun { };
+        mplayer = callPackage mplayerFun { stdenv = pkgs.stdenv2; enableFoo = false; };
+        nix = callPackage nixFun { };
+      };
+    in pkgs;
+
+  libX11Fun = { stdenv, fetchurl }: { name = "libX11"; };
+  libX11_2Fun = { stdenv, fetchurl }: { name = "libX11_2"; };
+  libXvFun = { stdenv, fetchurl, libX11 }: { name = "libXv"; };
+  
+  xorgFun =
+    { pkgs }:
+    let callPackage = callPackage_ (pkgs // pkgs.xorg); in
+    {
+      libX11 = callPackage libX11Fun { };
+      libXv = callPackage libXvFun { };
+    };
+
+in
+
+let
+
+  pkgs = allPackages { };
+  
+  pkgs2 = allPackages {
+    overrides = pkgs: pkgsPrev: {
+      stdenv = pkgs.stdenv2;
+      nix = pkgsPrev.nix.override { aterm = aterm2Fun { inherit (pkgs) stdenv fetchurl; }; };
+      xorg = pkgsPrev.xorg // { libX11 = libX11_2Fun { inherit (pkgs) stdenv fetchurl; }; };
+    };
+  };
+  
+in
+
+  [ pkgs.stdenv.name
+    pkgs.fetchurl.name
+    pkgs.aterm.name
+    pkgs2.aterm.name
+    pkgs.xorg.libX11.name
+    pkgs.xorg.libXv.name
+    pkgs.mplayer.name
+    pkgs2.mplayer.name
+    pkgs.nix.name
+    pkgs2.nix.name
+  ]