summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2008-08-14 12:53:29 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2008-08-14 12:53:29 +0000
commit1b962fc7206bf3134b2a2097d3db0ee6d2863c47 (patch)
tree9e259b7df5f0fa3ca748fa9b9035f2dd35b1a44d
parente8188384129bda7c8cdd5e17023ab05047551e6e (diff)
downloadguix-1b962fc7206bf3134b2a2097d3db0ee6d2863c47.tar.gz
* @-patterns as in Haskell. For instance, in a function definition
    f = args @ {x, y, z}: ...;

  `args' refers to the argument as a whole, which is further
  pattern-matched against the attribute set pattern {x, y, z}.

-rw-r--r--src/libexpr/eval.cc89
-rw-r--r--src/libexpr/expr-to-xml.cc10
-rw-r--r--src/libexpr/nixexpr-ast.def1
-rw-r--r--src/libexpr/nixexpr.cc7
-rw-r--r--src/libexpr/parser.y13
-rw-r--r--tests/lang/eval-okay-patterns.exp1
-rw-r--r--tests/lang/eval-okay-patterns.nix16
-rw-r--r--tests/lang/eval-okay-xml.exp.xml12
-rw-r--r--tests/lang/eval-okay-xml.nix2
-rw-r--r--tests/lang/parse-fail-patterns-1.nix1
10 files changed, 106 insertions, 46 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 95a70ac273..ebcbac5391 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -74,11 +74,16 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2,
 }
 
 
+/* Pattern-match `pat' against `arg'.  The result is a set of
+   substitutions (`subs') and a set of recursive substitutions
+   (`subsRecursive').  The latter can refer to the variables bound by
+   both `subs' and `subsRecursive'. */
 static void patternMatch(EvalState & state,
-    Pattern pat, Expr arg, ATermMap & subs)
+    Pattern pat, Expr arg, ATermMap & subs, ATermMap & subsRecursive)
 {
     ATerm name;
     ATermList formals;
+    Pattern pat1, pat2;
     
     if (matchVarPat(pat, name)) 
         subs.set(name, arg);
@@ -87,60 +92,45 @@ static void patternMatch(EvalState & state,
 
         arg = evalExpr(state, arg);
 
-        unsigned int nrFormals = ATgetLength(formals);
+        /* Get the actual arguments. */
+        ATermMap attrs;
+        queryAllAttrs(arg, attrs);
+        unsigned int nrAttrs = attrs.size();
 
-        /* Get the actual arguments and put them in the substitution.
-           !!! shouldn't do this once we add `...'.*/
-        ATermMap args;
-        queryAllAttrs(arg, args);
-        for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
-            subs.set(i->key, i->value);
-        
-        /* Get the formal arguments. */
-        ATermVector defsUsed;
-        ATermList recAttrs = ATempty;
+        /* For each formal argument, get the actual argument.  If
+           there is no matching actual argument but the formal
+           argument has a default, use the default. */
+        unsigned int attrsUsed = 0;
         for (ATermIterator i(formals); i; ++i) {
             Expr name, def;
             DefaultValue def2;
             if (!matchFormal(*i, name, def2)) abort(); /* can't happen */
 
-            Expr value = subs[name];
-        
+            Expr value = attrs[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()));
+                subsRecursive.set(name, def);
+            } else {
+                attrsUsed++;
+                attrs.remove(name);
+                subs.set(name, value);
             }
 
         }
-        
-        /* Make a recursive attribute set out of the (argument-name,
-           value) tuples.  This is so that we can support default
-           parameters that refer to each other, e.g.  ({x, y ? x + x}:
-           y) {x = "foo";} evaluates to "foofoo". */
-        if (defsUsed.size() != 0) {
-            for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
-                recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
-            Expr rec = makeRec(recAttrs, ATempty);
-            for (ATermVector::iterator i = defsUsed.begin(); i != defsUsed.end(); ++i)
-                subs.set(*i, makeSelect(rec, *i));
-        }
-    
-        if (subs.size() != nrFormals) {
-            /* One or more actual arguments were not declared as
-               formal arguments.  Find out which. */
-            for (ATermIterator i(formals); i; ++i) {
-                Expr name; ATerm d1;
-                if (!matchFormal(*i, name, d1)) abort();
-                subs.remove(name);
-            }
+
+        /* Check that each actual argument is listed as a formal
+           argument. */
+        if (attrsUsed != nrAttrs)
             throw TypeError(format("the function does not expect an argument named `%1%'")
-                % aterm2String(subs.begin()->key));
-        }
+                % aterm2String(attrs.begin()->key));
+    }
 
+    else if (matchAtPat(pat, pat1, pat2)) {
+        patternMatch(state, pat1, arg, subs, subsRecursive);
+        patternMatch(state, pat2, arg, subs, subsRecursive);
     }
 
     else abort();
@@ -151,9 +141,24 @@ static void patternMatch(EvalState & state,
 static Expr substArgs(EvalState & state,
     Expr body, Pattern pat, Expr arg)
 {
-    ATermMap subs(16);
+    ATermMap subs(16), subsRecursive(16);
     
-    patternMatch(state, pat, arg, subs);
+    patternMatch(state, pat, arg, subs, subsRecursive);
+
+    /* If we used any default values, make a recursive attribute set
+       out of the (argument-name, value) tuples.  This is so that we
+       can support default values that refer to each other, e.g.  ({x,
+       y ? x + x}: y) {x = "foo";} evaluates to "foofoo". */
+    if (subsRecursive.size() != 0) {
+        ATermList recAttrs = ATempty;
+        foreach (ATermMap::const_iterator, i, subs)
+            recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
+        foreach (ATermMap::const_iterator, i, subsRecursive)
+            recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
+        Expr rec = makeRec(recAttrs, ATempty);
+        foreach (ATermMap::const_iterator, i, subsRecursive)
+            subs.set(i->key, makeSelect(rec, i->key));
+    }
 
     return substitute(Substitution(0, &subs), body);
 }
diff --git a/src/libexpr/expr-to-xml.cc b/src/libexpr/expr-to-xml.cc
index 5aa537b200..6ec906356a 100644
--- a/src/libexpr/expr-to-xml.cc
+++ b/src/libexpr/expr-to-xml.cc
@@ -40,10 +40,11 @@ static void showAttrs(const ATermMap & attrs, XMLWriter & doc,
 }
 
 
-static void printPatternAsXML(Pattern pat, XMLWriter & doc, PathSet & context)
+static void printPatternAsXML(Pattern pat, XMLWriter & doc)
 {
     ATerm name;
     ATermList formals;
+    Pattern pat1, pat2;
     if (matchVarPat(pat, name))
         doc.writeEmptyElement("varpat", singletonAttrs("name", aterm2String(name)));
     else if (matchAttrsPat(pat, formals)) {
@@ -54,6 +55,11 @@ static void printPatternAsXML(Pattern pat, XMLWriter & doc, PathSet & context)
             doc.writeEmptyElement("attr", singletonAttrs("name", aterm2String(name)));
         }
     }
+    else if (matchAtPat(pat, pat1, pat2)) {
+        XMLOpenElement _(doc, "at");
+        printPatternAsXML(pat1, doc);
+        printPatternAsXML(pat2, doc);
+    }
 }
 
 
@@ -128,7 +134,7 @@ static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
 
     else if (matchFunction(e, pat, body, pos)) {
         XMLOpenElement _(doc, "function");
-        printPatternAsXML(pat, doc, context);
+        printPatternAsXML(pat, doc);
     }
 
     else
diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def
index 6b9cf95faf..670db1976f 100644
--- a/src/libexpr/nixexpr-ast.def
+++ b/src/libexpr/nixexpr-ast.def
@@ -77,6 +77,7 @@ Scope | | Expr |
 
 VarPat | string | Pattern |
 AttrsPat | ATermList | Pattern |
+AtPat | Pattern Pattern | Pattern |
 
 Formal | string DefaultValue | ATerm |
 
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 4744cdde39..b2d775abbc 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -114,6 +114,7 @@ static void varsBoundByPattern(ATermMap & map, Pattern pat)
 {
     ATerm name;
     ATermList formals;
+    Pattern pat1, pat2;    
     /* Use makeRemoved() so that it can be used directly in
        substitute(). */
     if (matchVarPat(pat, name))
@@ -125,6 +126,10 @@ static void varsBoundByPattern(ATermMap & map, Pattern pat)
             map.set(name, makeRemoved());
         }
     }
+    else if (matchAtPat(pat, pat1, pat2)) {
+        varsBoundByPattern(map, pat1);
+        varsBoundByPattern(map, pat2);
+    }
     else abort();
 }
 
@@ -354,7 +359,7 @@ Expr makeStr(const string & s, const PathSet & context)
 
 string showType(Expr e)
 {
-    ATerm t1, t2, t3;
+    ATerm t1, t2;
     ATermList l1;
     ATermBlob b1;
     int i1;
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 7713021a90..c48aa34aaf 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -211,7 +211,8 @@ static void freeAndUnprotect(void * p)
 }
 
 %type <t> start expr expr_function expr_if expr_op
-%type <t> expr_app expr_select expr_simple bind inheritsrc formal pattern
+%type <t> expr_app expr_select expr_simple bind inheritsrc formal
+%type <t> pattern pattern2
 %type <ts> binds ids expr_list formals string_parts ind_string_parts
 %token <t> ID INT STR IND_STR PATH URI
 %token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
@@ -319,6 +320,11 @@ ind_string_parts
   ;
 
 pattern
+  : pattern2 '@' pattern { $$ = makeAtPat($1, $3); }
+  | pattern2
+  ;
+
+pattern2
   : ID { $$ = makeVarPat($1); }
   | '{' formals '}' { $$ = makeAttrsPat($2); }
   ;
@@ -394,6 +400,7 @@ static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
 {
     ATerm name;
     ATermList formals;
+    Pattern pat1, pat2;
     if (matchVarPat(pat, name)) {
         if (map.get(name))
             throw EvalError(format("duplicate formal function argument `%1%' at %2%")
@@ -410,6 +417,10 @@ static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
             map.set(name, name);
         }
     }
+    else if (matchAtPat(pat, pat1, pat2)) {
+        checkPatternVars(pos, map, pat1);
+        checkPatternVars(pos, map, pat2);
+    }
     else abort();
 }
 
diff --git a/tests/lang/eval-okay-patterns.exp b/tests/lang/eval-okay-patterns.exp
new file mode 100644
index 0000000000..382a0bd26f
--- /dev/null
+++ b/tests/lang/eval-okay-patterns.exp
@@ -0,0 +1 @@
+Str("abcxyzDDDDEFgh",[])
diff --git a/tests/lang/eval-okay-patterns.nix b/tests/lang/eval-okay-patterns.nix
new file mode 100644
index 0000000000..bcb9f3842e
--- /dev/null
+++ b/tests/lang/eval-okay-patterns.nix
@@ -0,0 +1,16 @@
+let
+
+  f = args@{x, y, z}: x + args.y + z;
+
+  g = {x, y, z}@args: f args;
+
+  h = {x ? "d", y ? x, z ? args.x}@args: x + y + z;
+
+  i = args@args2: args.x + args2.y;
+
+in
+  f {x = "a"; y = "b"; z = "c";} +
+  g {x = "x"; y = "y"; z = "z";} +
+  h {x = "D";} +
+  h {x = "D"; y = "E"; z = "F";} +
+  i {x = "g"; y = "h";}
diff --git a/tests/lang/eval-okay-xml.exp.xml b/tests/lang/eval-okay-xml.exp.xml
index 4798e87f87..4f55c69a7c 100644
--- a/tests/lang/eval-okay-xml.exp.xml
+++ b/tests/lang/eval-okay-xml.exp.xml
@@ -4,6 +4,18 @@
     <attr name="a">
       <string value="foo" />
     </attr>
+    <attr name="at">
+      <function>
+        <at>
+          <varpat name="args" />
+          <attrspat>
+            <attr name="x" />
+            <attr name="y" />
+            <attr name="z" />
+          </attrspat>
+        </at>
+      </function>
+    </attr>
     <attr name="b">
       <string value="bar" />
     </attr>
diff --git a/tests/lang/eval-okay-xml.nix b/tests/lang/eval-okay-xml.nix
index 75420316df..44afbff1ed 100644
--- a/tests/lang/eval-okay-xml.nix
+++ b/tests/lang/eval-okay-xml.nix
@@ -12,4 +12,6 @@ rec {
 
   id = x: x;
 
+  at = args@{x, y, z}: x;
+
 }
diff --git a/tests/lang/parse-fail-patterns-1.nix b/tests/lang/parse-fail-patterns-1.nix
new file mode 100644
index 0000000000..7b40616417
--- /dev/null
+++ b/tests/lang/parse-fail-patterns-1.nix
@@ -0,0 +1 @@
+args@{args, x, y, z}: x