about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2006-07-24T16·35+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2006-07-24T16·35+0000
commitf4c5531d928caadee75d66bba40994f55f839be7 (patch)
tree04ed6655b0cf1043761b6d3978ff35b3794a6c42
parent88acffa20ac45fd13b65a1d9143e0400ae0bde47 (diff)
* New language feature: domain checks, which check whether a function
  argument has a valid value, i.e., is in a certain domain.  E.g.,

    { foo : [true false]
    , bar : ["a" "b" "c"]
    }: ...

  This previously could be done using assertions, but domain checks
  will allow the buildfarm to automatically extract the configuration
  space from functions.

-rw-r--r--src/libexpr/eval.cc35
-rw-r--r--src/libexpr/nixexpr.cc9
-rw-r--r--src/libexpr/parser.y2
-rw-r--r--tests/lang/parse-fail-undef-var-2.nix7
4 files changed, 40 insertions, 13 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index e45de729bebd..06bf671e2a20 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -22,7 +22,8 @@ void EvalState::addPrimOp(const string & name,
 
 
 /* Substitute an argument set into the body of a function. */
-static Expr substArgs(Expr body, ATermList formals, Expr arg)
+static Expr substArgs(EvalState & state,
+    Expr body, ATermList formals, Expr arg)
 {
     unsigned int nrFormals = ATgetLength(formals);
     ATermMap subs(nrFormals);
@@ -37,17 +38,35 @@ static Expr substArgs(Expr body, ATermList formals, Expr arg)
     ATermVector defsUsed;
     ATermList recAttrs = ATempty;
     for (ATermIterator i(formals); i; ++i) {
-        Expr name, def; DefaultValue def2; ATerm dummy;
-        if (!matchFormal(*i, name, dummy, def2)) abort(); /* can't happen */
-        if (!matchDefaultValue(def2, def)) def = 0;
-        if (subs[name] == 0) {
+        Expr name, def;
+        ValidValues valids2;
+        DefaultValue def2;
+        if (!matchFormal(*i, name, valids2, def2)) abort(); /* can't happen */
+
+        Expr value = subs[name];
+        
+        if (value == 0) {
+            if (!matchDefaultValue(def2, def)) def = 0;
             if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
                 % aterm2String(name));
+            value = def;
             defsUsed.push_back(name);
             recAttrs = ATinsert(recAttrs, makeBind(name, def, makeNoPos()));
         }
-        /* !!! check that the argument are in the valid values list,
-           if present */
+
+        ATermList valids;
+        if (matchValidValues(valids2, valids)) {
+            bool found = false;
+            for (ATermIterator j(valids); j; ++j) {
+                Expr v = evalExpr(state, *j);
+                if (value == v) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) throw TypeError(format("the argument named `%1%' has an illegal value")
+                % aterm2String(name));            
+        }
     }
 
     /* Make a recursive attribute set out of the (argument-name,
@@ -340,7 +359,7 @@ Expr evalExpr2(EvalState & state, Expr e)
         else if (matchFunction(e1, formals, e4, pos)) {
             e2 = evalExpr(state, e2);
             try {
-                return evalExpr(state, substArgs(e4, formals, e2));
+                return evalExpr(state, substArgs(state, e4, formals, e2));
             } catch (Error & e) {
                 e.addPrefix(format("while evaluating the function at %1%:\n")
                     % showPos(pos));
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index cbcaec584339..56b23cc62211 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -220,15 +220,16 @@ static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
     else if (matchFunction(e, formals, body, pos)) {
         ATermMap defs2(defs);
         for (ATermIterator i(formals); i; ++i) {
-            Expr d1, d2;
+            ATerm d1, d2;
             if (!matchFormal(*i, name, d1, d2)) abort();
             defs2.set(name, (ATerm) ATempty);
         }
         for (ATermIterator i(formals); i; ++i) {
-            Expr dummy, deflt;
+            ATerm valids, deflt;
             set<Expr> done2;
-            if (matchFormal(*i, name, dummy, deflt)) /* !!! check dummy */
-                checkVarDefs2(done2, defs2, deflt);
+            matchFormal(*i, name, valids, deflt);
+            checkVarDefs2(done, defs, valids);
+            checkVarDefs2(done2, defs2, deflt);
         }
         set<Expr> done2;
         checkVarDefs2(done2, defs2, body);
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index b28aa27601bd..c1e3b48a79a1 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -206,7 +206,7 @@ formals
 
 formal
   : ID { $$ = makeFormal($1, makeUnrestrictedValues(), makeNoDefaultValue()); }
-//  | ID ':' '[' expr_list ']' { $$ = makeDefFormal($1, $3); }
+  | ID ':' '[' expr_list ']' { $$ = makeFormal($1, makeValidValues($4), makeNoDefaultValue()); }
   | ID '?' expr { $$ = makeFormal($1, makeUnrestrictedValues(), makeDefaultValue($3)); }
   ;
   
diff --git a/tests/lang/parse-fail-undef-var-2.nix b/tests/lang/parse-fail-undef-var-2.nix
new file mode 100644
index 000000000000..c10a52b1ea42
--- /dev/null
+++ b/tests/lang/parse-fail-undef-var-2.nix
@@ -0,0 +1,7 @@
+let {
+
+  f = {x, y : ["baz" "bar" z "bat"]}: x + y;
+
+  body = f {x = "foo"; y = "bar";};
+
+}