about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libexpr/eval.cc16
-rw-r--r--src/libexpr/eval.hh1
-rw-r--r--src/libexpr/lexer.l1
-rw-r--r--src/libexpr/nixexpr-ast.def1
-rw-r--r--src/libexpr/parser.y2
-rw-r--r--src/libexpr/primops.cc18
-rw-r--r--tests/lang/eval-okay-concat.exp1
-rw-r--r--tests/lang/eval-okay-concat.nix1
8 files changed, 29 insertions, 12 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index d4593b5c0e..9a6d414db7 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -140,6 +140,15 @@ bool evalBool(EvalState & state, Expr e)
 }
 
 
+ATermList evalList(EvalState & state, Expr e)
+{
+    e = evalExpr(state, e);
+    ATermList list;
+    if (!matchList(e, list)) throw Error("list expected");
+    return list;
+}
+
+
 Expr evalExpr2(EvalState & state, Expr e)
 {
     Expr e1, e2, e3, e4;
@@ -336,6 +345,13 @@ Expr evalExpr2(EvalState & state, Expr e)
         else throw Error("wrong argument types in `+' operator");
     }
 
+    /* List concatenation. */
+    if (matchOpConcat(e, e1, e2)) {
+        ATermList l1 = evalList(state, e1);
+        ATermList l2 = evalList(state, e2);
+        return makeList(ATconcat(l1, l2));
+    }
+
     /* Barf. */
     throw badTerm("invalid expression", e);
 }
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 8ea0aec06c..5562066fda 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -46,6 +46,7 @@ Expr evalFile(EvalState & state, const Path & path);
 /* Specific results. */
 string evalString(EvalState & state, Expr e);
 Path evalPath(EvalState & state, Expr e);
+ATermList evalList(EvalState & state, Expr e);
 
 /* Print statistics. */
 void printEvalStats(EvalState & state);
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 38d403c9fa..d5a14f517a 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -59,6 +59,7 @@ inherit     { return INHERIT; }
 \|\|        { return OR; }
 \-\>        { return IMPL; }
 \/\/        { return UPDATE; }
+\+\+        { return CONCAT; }
 
 {ID}        { yylval->t = ATmake("<str>", yytext); return ID; /* !!! alloc */ }
 {INT}       { int n = atoi(yytext); /* !!! overflow */
diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def
index 19601847cf..b384ff7c08 100644
--- a/src/libexpr/nixexpr-ast.def
+++ b/src/libexpr/nixexpr-ast.def
@@ -18,6 +18,7 @@ OpUpdate | Expr Expr | Expr |
 SubPath | Expr Expr | Expr |
 OpHasAttr | Expr string | Expr |
 OpPlus | Expr Expr | Expr |
+OpConcat | Expr Expr | Expr |
 Call | Expr Expr | Expr |
 Select | Expr string | Expr |
 Var | string | Expr |
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 1f204a2280..23f1965ecd 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -64,6 +64,7 @@ static Pos makeCurPos(YYLTYPE * loc, void * data)
 %right UPDATE
 %left NEG
 %left '+'
+%left CONCAT
 %nonassoc '?'
 %nonassoc '~'
 
@@ -102,6 +103,7 @@ expr_op
   | expr_op '~' expr_op { $$ = makeSubPath($1, $3); }
   | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); }
   | expr_op '+' expr_op { $$ = makeOpPlus($1, $3); }
+  | expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); }
   | expr_app
   ;
 
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index b0986028ee..5736a7f916 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -414,17 +414,13 @@ static Expr primIsNull(EvalState & state, const ATermVector & args)
 static Expr primMap(EvalState & state, const ATermVector & args)
 {
     Expr fun = evalExpr(state, args[0]);
-    Expr list = evalExpr(state, args[1]);
+    ATermList list = evalList(state, args[1]);
 
-    ATermList list2;
-    if (!matchList(list, list2))
-        throw Error("`map' expects a list as its second argument");
-
-    ATermList list3 = ATempty;
-    for (ATermIterator i(list2); i; ++i)
-        list3 = ATinsert(list3, makeCall(fun, *i));
+    ATermList res = ATempty;
+    for (ATermIterator i(list); i; ++i)
+        res = ATinsert(res, makeCall(fun, *i));
 
-    return makeList(ATreverse(list3));
+    return makeList(ATreverse(res));
 }
 
 
@@ -449,9 +445,7 @@ static Expr primRemoveAttrs(EvalState & state, const ATermVector & args)
     ATermMap attrs;
     queryAllAttrs(evalExpr(state, args[0]), attrs, true);
     
-    ATermList list;
-    if (!matchList(evalExpr(state, args[1]), list))
-        throw Error("`removeAttrs' expects a list as its second argument");
+    ATermList list = evalList(state, args[1]);
 
     for (ATermIterator i(list); i; ++i)
         /* It's not an error for *i not to exist. */
diff --git a/tests/lang/eval-okay-concat.exp b/tests/lang/eval-okay-concat.exp
new file mode 100644
index 0000000000..359ccef86f
--- /dev/null
+++ b/tests/lang/eval-okay-concat.exp
@@ -0,0 +1 @@
+List([Int(1),Int(2),Int(3),Int(4),Int(5),Int(6),Int(7),Int(8),Int(9)])
diff --git a/tests/lang/eval-okay-concat.nix b/tests/lang/eval-okay-concat.nix
new file mode 100644
index 0000000000..d158a9bf05
--- /dev/null
+++ b/tests/lang/eval-okay-concat.nix
@@ -0,0 +1 @@
+[1 2 3] ++ [4 5 6] ++ [7 8 9]