about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--NEWS5
-rw-r--r--src/libexpr/eval.cc14
-rw-r--r--src/libexpr/nixexpr.cc12
-rw-r--r--src/libexpr/nixexpr.hh6
-rw-r--r--src/libexpr/parser.y2
-rw-r--r--src/libexpr/primops.cc9
6 files changed, 43 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index b130b4c50064..c4d84740bd7f 100644
--- a/NEWS
+++ b/NEWS
@@ -49,8 +49,13 @@ Major changes include the following:
 
 * Nix expression language changes:
 
+  * New language construct: `with E1; E2' brings all attributes
+    defined in the attribute set E1 in scope in E2.
+
   * Added a `map' function.
 
+  * Various new operators (e.g., string concatenation).
+
 * An Emacs mode for editing Nix expressions (with syntax highlighting
   and indentation) has been added.
 
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 215692aeb313..4454aaec452f 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -338,6 +338,20 @@ Expr evalExpr2(EvalState & state, Expr e)
         return makeBool(attrs.get(name) != 0);
     }
 
+    /* String or path concatenation. */
+    if (atMatch(m, e) >> "OpPlus" >> e1 >> e2) {
+        e1 = evalExpr(state, e1);
+        e2 = evalExpr(state, e2);
+        string s1, s2;
+        if (atMatch(m, e1) >> "Str" >> s1 &&
+            atMatch(m, e2) >> "Str" >> s2)
+            return makeString(s1 + s2);
+        else if (atMatch(m, e1) >> "Path" >> s1 &&
+            atMatch(m, e2) >> "Path" >> s2)
+            return makeString(canonPath(s1 + "/" + s2));
+        else throw Error("wrong argument types in `+' operator");
+    }
+
     /* Barf. */
     throw badTerm("invalid expression", e);
 }
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 78f89db5e5d3..37288804d0eb 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -364,3 +364,15 @@ Expr makeBool(bool b)
 {
     return b ? ATmake("Bool(True)") : ATmake("Bool(False)");
 }
+
+
+Expr makeString(const string & s)
+{
+    return ATmake("Str(<str>)", s.c_str());
+}
+
+
+Expr makePath(const Path & path)
+{
+    return ATmake("Path(<str>)", path.c_str());
+}
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 30145c87b344..657e6055c40e 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -92,5 +92,11 @@ void checkVarDefs(const ATermMap & def, Expr e);
 /* Create an expression representing a boolean. */
 Expr makeBool(bool b);
 
+/* Create an expression representing a string. */
+Expr makeString(const string & s);
+
+/* Create an expression representing a path. */
+Expr makePath(const Path & path);
+
 
 #endif /* !__NIXEXPR_H */
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 88ee8326b8d3..c56d89809f49 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -53,6 +53,7 @@ ATerm makePos(YYLTYPE * loc, void * data)
 %nonassoc EQ NEQ
 %right UPDATE
 %left NEG
+%left '+'
 %nonassoc '?'
 %nonassoc '~'
 
@@ -90,6 +91,7 @@ expr_op
   | expr_op UPDATE expr_op { $$ = ATmake("OpUpdate(<term>, <term>)", $1, $3); }
   | expr_op '~' expr_op { $$ = ATmake("SubPath(<term>, <term>)", $1, $3); }
   | expr_op '?' ID { $$ = ATmake("OpHasAttr(<term>, <term>)", $1, $3); }
+  | expr_op '+' expr_op { $$ = ATmake("OpPlus(<term>, <term>)", $1, $3); }
   | expr_app
   ;
 
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 938d9bb8c13b..ade483dc703d 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -273,8 +273,8 @@ static Expr primDerivation(EvalState & state, const ATermVector & _args)
 
     attrs.set("outPath", ATmake("(Path(<str>), NoPos)", outPath.c_str()));
     attrs.set("drvPath", ATmake("(Path(<str>), NoPos)", drvPath.c_str()));
-    attrs.set("drvHash", ATmake("(Str(<str>), NoPos)", ((string) drvHash).c_str()));
-    attrs.set("type", ATmake("(Str(\"derivation\"), NoPos)"));
+    attrs.set("drvHash", ATmake("(<term>, NoPos)", makeString(drvHash)));
+    attrs.set("type", ATmake("(<term>, NoPos)", makeString("derivation")));
 
     return makeAttrs(attrs);
 }
@@ -284,8 +284,7 @@ static Expr primDerivation(EvalState & state, const ATermVector & _args)
    following the last slash. */
 static Expr primBaseNameOf(EvalState & state, const ATermVector & args)
 {
-    string s = evalString(state, args[0]);
-    return ATmake("Str(<str>)", baseNameOf(s).c_str());
+    return makeString(baseNameOf(evalString(state, args[0])));
 }
 
 
@@ -298,7 +297,7 @@ static Expr primToString(EvalState & state, const ATermVector & args)
     if (atMatch(m, arg) >> "Str" >> s ||
         atMatch(m, arg) >> "Path" >> s ||
         atMatch(m, arg) >> "Uri" >> s)
-        return ATmake("Str(<str>)", s.c_str());
+        return makeString(s);
     else throw Error("cannot coerce value to string");
 }