about summary refs log tree commit diff
path: root/src/libexpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/eval-test.cc1
-rw-r--r--src/libexpr/eval.cc248
-rw-r--r--src/libexpr/eval.hh18
-rw-r--r--src/libexpr/primops.cc55
4 files changed, 138 insertions, 184 deletions
diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc
index e4e70c38caa0..ff94e3ce1612 100644
--- a/src/libexpr/eval-test.cc
+++ b/src/libexpr/eval-test.cc
@@ -72,6 +72,7 @@ void run(Strings args)
     //doTest("import ./foo.nix");
     doTest("map (x: __add 1 x) [ 1 2 3 ]");
     doTest("map (builtins.add 1) [ 1 2 3 ]");
+    doTest("builtins.hasAttr \"x\" { x = 1; }");
 }
 
 
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 34c420339096..47a2b93b307f 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -167,6 +167,28 @@ static void mkThunk(Value & v, Env & env, Expr expr)
 }
 
 
+void mkString(Value & v, const char * s)
+{
+    v.type = tString;
+    v.string.s = strdup(s);
+    v.string.context = 0;
+}
+
+
+void mkString(Value & v, const string & s, const PathSet & context)
+{
+    mkString(v, s.c_str());
+    // !!! context
+}
+
+
+void mkPath(Value & v, const char * s)
+{
+    v.type = tPath;
+    v.path = strdup(s);
+}
+
+
 static Value * lookupWith(Env * env, Sym name)
 {
     if (!env) return 0;
@@ -206,7 +228,7 @@ static Value * lookupVar(Env * env, Sym name)
     }
 #endif
     
-    throw Error("undefined variable");
+    throw Error(format("undefined variable `%1%'") % aterm2String(name));
 }
 
 
@@ -257,38 +279,35 @@ void EvalState::eval(Env & env, Expr e, Value & v)
     char x;
     if (&x < deepestStack) deepestStack = &x;
     
-    printMsg(lvlError, format("eval: %1%") % e);
+    debug(format("eval: %1%") % e);
 
     nrEvaluated++;
 
     Sym name;
+    int n;
+    ATerm s; ATermList context, es;
+    ATermList rbnds, nrbnds;
+    Expr e1, e2, e3, fun, arg, attrs;
+    Pattern pat; Expr body; Pos pos;
+    
     if (matchVar(e, name)) {
         Value * v2 = lookupVar(&env, name);
         forceValue(*v2);
         v = *v2;
-        return;
     }
 
-    int n;
-    if (matchInt(e, n)) {
+    else if (matchInt(e, n))
         mkInt(v, n);
-        return;
-    }
 
-    ATerm s; ATermList context;
-    if (matchStr(e, s, context)) {
+    else if (matchStr(e, s, context)) {
         assert(context == ATempty);
-        mkString(v, strdup(ATgetName(ATgetAFun(s))));
-        return;
+        mkString(v, ATgetName(ATgetAFun(s)));
     }
 
-    if (matchPath(e, s)) {
-        mkPath(v, strdup(ATgetName(ATgetAFun(s))));
-        return;
-    }
+    else if (matchPath(e, s))
+        mkPath(v, ATgetName(ATgetAFun(s)));
 
-    ATermList es;
-    if (matchAttrs(e, es)) {
+    else if (matchAttrs(e, es)) {
         v.type = tAttrs;
         v.attrs = new Bindings;
         ATerm e2, pos;
@@ -298,11 +317,9 @@ void EvalState::eval(Env & env, Expr e, Value & v)
             nrValues++;
             mkThunk(v2, env, e2);
         }
-        return;
     }
 
-    ATermList rbnds, nrbnds;
-    if (matchRec(e, rbnds, nrbnds)) {
+    else if (matchRec(e, rbnds, nrbnds)) {
         Env & env2(allocEnv());
         env2.up = &env;
         
@@ -315,12 +332,9 @@ void EvalState::eval(Env & env, Expr e, Value & v)
             nrValues++;
             mkThunk(v2, env2, e2);
         }
-        
-        return;
     }
 
-    Expr e1, e2;
-    if (matchSelect(e, e2, name)) {
+    else if (matchSelect(e, e2, name)) {
         eval(env, e2, v);
         forceAttrs(v); // !!! eval followed by force is slightly inefficient
         Bindings::iterator i = v.attrs->find(name);
@@ -333,29 +347,23 @@ void EvalState::eval(Env & env, Expr e, Value & v)
             throw;
         }
         v = i->second;
-        return;
     }
 
-    Pattern pat; Expr body; Pos pos;
-    if (matchFunction(e, pat, body, pos)) {
+    else if (matchFunction(e, pat, body, pos)) {
         v.type = tLambda;
         v.lambda.env = &env;
         v.lambda.pat = pat;
         v.lambda.body = body;
-        return;
     }
 
-    Expr fun, arg;
-    if (matchCall(e, fun, arg)) {
+    else if (matchCall(e, fun, arg)) {
         eval(env, fun, v);
         Value vArg;
         mkThunk(vArg, env, arg); // !!! should this be on the heap?
         callFunction(v, vArg, v);
-        return;
     }
 
-    Expr attrs;
-    if (matchWith(e, attrs, body, pos)) {
+    else if (matchWith(e, attrs, body, pos)) {
         Env & env2(allocEnv());
         env2.up = &env;
 
@@ -365,31 +373,27 @@ void EvalState::eval(Env & env, Expr e, Value & v)
         forceAttrs(vAttrs);
         
         eval(env2, body, v);
-        return;
     }
 
-    if (matchList(e, es)) {
+    else if (matchList(e, es)) {
         mkList(v, ATgetLength(es));
         for (unsigned int n = 0; n < v.list.length; ++n, es = ATgetNext(es))
             mkThunk(v.list.elems[n], env, ATgetFirst(es));
-        return;
     }
 
-    if (matchOpEq(e, e1, e2)) {
+    else if (matchOpEq(e, e1, e2)) {
         Value v1; eval(env, e1, v1);
         Value v2; eval(env, e2, v2);
         mkBool(v, eqValues(v1, v2));
-        return;
     }
 
-    if (matchOpNEq(e, e1, e2)) {
+    else if (matchOpNEq(e, e1, e2)) {
         Value v1; eval(env, e1, v1);
         Value v2; eval(env, e2, v2);
         mkBool(v, !eqValues(v1, v2));
-        return;
     }
 
-    if (matchOpConcat(e, e1, e2)) {
+    else if (matchOpConcat(e, e1, e2)) {
         Value v1; eval(env, e1, v1);
         forceList(v1);
         Value v2; eval(env, e2, v2);
@@ -401,10 +405,9 @@ void EvalState::eval(Env & env, Expr e, Value & v)
             v.list.elems[n] = v1.list.elems[n];
         for (unsigned int n = 0; n < v2.list.length; ++n)
             v.list.elems[n + v1.list.length] = v2.list.elems[n];
-        return;
     }
 
-    if (matchConcatStrings(e, es)) {
+    else if (matchConcatStrings(e, es)) {
         PathSet context;
         std::ostringstream s;
         
@@ -431,24 +434,62 @@ void EvalState::eval(Env & env, Expr e, Value & v)
                 % s.str());
 
         if (isPath)
-            mkPath(v, strdup(s.str().c_str()));
+            mkPath(v, s.str().c_str());
         else
-            mkString(v, strdup(s.str().c_str())); // !!! context
-        return;
+            mkString(v, s.str().c_str()); // !!! context
     }
 
-    Expr e3;
-    if (matchIf(e, e1, e2, e3)) {
+    /* Conditionals. */
+    else if (matchIf(e, e1, e2, e3))
         eval(env, evalBool(env, e1) ? e2 : e3, v);
-        return;
+
+    /* Assertions. */
+    else if (matchAssert(e, e1, e2, pos)) {
+        if (!evalBool(env, e1))
+            throw AssertionError(format("assertion failed at %1%") % showPos(pos));
+        eval(env, e2, v);
     }
 
-    if (matchOpOr(e, e1, e2)) {
+    /* Negation. */
+    else if (matchOpNot(e, e1))
+        mkBool(v, !evalBool(env, e1));
+
+    /* Implication. */
+    else if (matchOpImpl(e, e1, e2))
+        return mkBool(v, !evalBool(env, e1) || evalBool(env, e2));
+    
+    /* Conjunction (logical AND). */
+    else if (matchOpAnd(e, e1, e2))
+        mkBool(v, evalBool(env, e1) && evalBool(env, e2));
+    
+    /* Disjunction (logical OR). */
+    else if (matchOpOr(e, e1, e2))
         mkBool(v, evalBool(env, e1) || evalBool(env, e2));
-        return;
+
+    /* Attribute set update (//). */
+    else if (matchOpUpdate(e, e1, e2)) {
+        v.type = tAttrs;
+        v.attrs = new Bindings;
+        
+        Value v2;
+        eval(env, e2, v2);
+        foreach (Bindings::iterator, i, *v2.attrs)
+            (*v.attrs)[i->first] = i->second;
+        
+        eval(env, e1, v2);
+        foreach (Bindings::iterator, i, *v2.attrs)
+            if (v.attrs->find(i->first) == v.attrs->end())
+                (*v.attrs)[i->first] = i->second;
     }
 
-    throw Error("unsupported term");
+    /* Attribute existence test (?). */
+    else if (matchOpHasAttr(e, e1, name)) {
+        eval(env, e1, v);
+        forceAttrs(v);
+        mkBool(v, v.attrs->find(name) != v.attrs->end());
+    }
+
+    else throw Error("unsupported term");
 }
 
 
@@ -637,6 +678,18 @@ void EvalState::forceFunction(Value & v)
 }
 
 
+string EvalState::forceStringNoCtx(Value & v)
+{
+    forceValue(v);
+    if (v.type != tString)
+        throw TypeError(format("value is %1% while a string was expected") % showType(v));
+    if (v.string.context)
+        throw EvalError(format("the string `%1%' is not allowed to refer to a store path (such as `%2%')")
+            % v.string.s % v.string.context[0]);
+    return string(v.string.s);
+}
+
+
 string EvalState::coerceToString(Value & v, PathSet & context,
     bool coerceMore, bool copyToStore)
 {
@@ -901,68 +954,6 @@ LocalNoInline(ATerm expandRec(EvalState & state, ATerm e, ATermList rbnds, ATerm
 }
 
 
-LocalNoInline(Expr updateAttrs(Expr e1, Expr e2))
-{
-    /* Note: e1 and e2 should be in normal form. */
-
-    ATermMap attrs;
-    queryAllAttrs(e1, attrs, true);
-    queryAllAttrs(e2, attrs, true);
-
-    return makeAttrs(attrs);
-}
-
-
-string evalString(EvalState & state, Expr e, PathSet & context)
-{
-    e = evalExpr(state, e);
-    string s;
-    if (!matchStr(e, s, context))
-        throwTypeError("value is %1% while a string was expected", showType(e));
-    return s;
-}
-
-
-string evalStringNoCtx(EvalState & state, Expr e)
-{
-    PathSet context;
-    string s = evalString(state, e, context);
-    if (!context.empty())
-        throw EvalError(format("the string `%1%' is not allowed to refer to a store path (such as `%2%')")
-            % s % *(context.begin()));
-    return s;
-}
-
-
-int evalInt(EvalState & state, Expr e)
-{
-    e = evalExpr(state, e);
-    int i;
-    if (!matchInt(e, i))
-        throwTypeError("value is %1% while an integer was expected", showType(e));
-    return i;
-}
-
-
-bool evalBool(EvalState & state, Expr e)
-{
-    e = evalExpr(state, e);
-    if (e == eTrue) return true;
-    else if (e == eFalse) return false;
-    else throwTypeError("value is %1% while a boolean was expected", showType(e));
-}
-
-
-ATermList evalList(EvalState & state, Expr e)
-{
-    e = evalExpr(state, e);
-    ATermList list;
-    if (!matchList(e, list))
-        throwTypeError("value is %1% while a list was expected", showType(e));
-    return list;
-}
-
-
 static void flattenList(EvalState & state, Expr e, ATermList & result)
 {
     ATermList es;
@@ -1078,14 +1069,6 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
 }
 
 
-LocalNoInline(Expr evalAssert(EvalState & state, Expr cond, Expr body, ATerm pos))
-{
-    if (!evalBool(state, cond))
-        throw AssertionError(format("assertion failed at %1%") % showPos(pos));
-    return evalExpr(state, body);
-}
-
-
 LocalNoInline(Expr evalWith(EvalState & state, Expr defs, Expr body, ATerm pos))
 {
     ATermMap attrs;
@@ -1109,14 +1092,6 @@ LocalNoInline(Expr evalWith(EvalState & state, Expr defs, Expr body, ATerm pos))
 }
 
 
-LocalNoInline(Expr evalHasAttr(EvalState & state, Expr e, ATerm name))
-{
-    ATermMap attrs;
-    queryAllAttrs(evalExpr(state, e), attrs);
-    return makeBool(attrs.get(name) != 0);
-}
-
-
 LocalNoInline(Expr evalPlusConcat(EvalState & state, Expr e))
 {
     Expr e1, e2;
@@ -1177,19 +1152,6 @@ LocalNoInline(Expr evalSubPath(EvalState & state, Expr e1, Expr e2))
 }
 
 
-LocalNoInline(Expr evalOpConcat(EvalState & state, Expr e1, Expr e2))
-{
-    try {
-        ATermList l1 = evalList(state, e1);
-        ATermList l2 = evalList(state, e2);
-        return makeList(ATconcat(l1, l2));
-    } catch (Error & e) {
-        addErrorPrefix(e, "in a list concatenation:\n");
-        throw;
-    }
-}
-
-
 /* Implementation of the `==' and `!=' operators. */
 LocalNoInline(bool areEqual(EvalState & state, Expr e1, Expr e2))
 {
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 28ec2e3989a7..87ab7733ada2 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -104,19 +104,9 @@ static inline void mkBool(Value & v, bool b)
 }
 
 
-static inline void mkString(Value & v, const char * s)
-{
-    v.type = tString;
-    v.string.s = s;
-    v.string.context = 0;
-}
-
-
-static inline void mkPath(Value & v, const char * s)
-{
-    v.type = tPath;
-    v.path = s;
-}
+void mkString(Value & v, const char * s);
+void mkString(Value & v, const string & s, const PathSet & context);
+void mkPath(Value & v, const char * s);
 
 
 typedef std::map<Path, PathSet> DrvRoots;
@@ -177,6 +167,7 @@ public:
     void forceAttrs(Value & v);
     void forceList(Value & v);
     void forceFunction(Value & v); // either lambda or primop
+    string forceStringNoCtx(Value & v);
 
     /* String coercion.  Converts strings, paths and derivations to a
        string.  If `coerceMore' is set, also converts nulls, integers,
@@ -234,7 +225,6 @@ Expr strictEvalExpr(EvalState & state, Expr e);
 
 /* Specific results. */
 string evalString(EvalState & state, Expr e, PathSet & context);
-string evalStringNoCtx(EvalState & state, Expr e);
 int evalInt(EvalState & state, Expr e);
 bool evalBool(EvalState & state, Expr e);
 ATermList evalList(EvalState & state, Expr e);
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index bb1a854989e3..e097a9284a6e 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -687,24 +687,32 @@ static void prim_attrNames(EvalState & state, Value * * args, Value & v)
 
     return makeList(list);
 }
+#endif
 
 
 /* Dynamic version of the `.' operator. */
 static void prim_getAttr(EvalState & state, Value * * args, Value & v)
 {
-    string attr = evalStringNoCtx(state, args[0]);
-    return evalExpr(state, makeSelect(args[1], toATerm(attr)));
+    string attr = state.forceStringNoCtx(*args[0]);
+    state.forceAttrs(*args[1]);
+    Bindings::iterator i = args[1]->attrs->find(toATerm(attr));
+    if (i == args[1]->attrs->end())
+        throw EvalError(format("attribute `%1%' missing") % attr);
+    state.forceValue(i->second);
+    v = i->second;
 }
 
 
 /* Dynamic version of the `?' operator. */
 static void prim_hasAttr(EvalState & state, Value * * args, Value & v)
 {
-    string attr = evalStringNoCtx(state, args[0]);
-    return evalExpr(state, makeOpHasAttr(args[1], toATerm(attr)));
+    string attr = state.forceStringNoCtx(*args[0]);
+    state.forceAttrs(*args[1]);
+    mkBool(v, args[1]->attrs->find(toATerm(attr)) != args[1]->attrs->end());
 }
 
 
+#if 0
 /* Builds an attribute set from a list specifying (name, value)
    pairs.  To be precise, a list [{name = "name1"; value = value1;}
    ... {name = "nameN"; value = valueN;}] is transformed to {name1 =
@@ -896,31 +904,24 @@ static void prim_add(EvalState & state, Value * * args, Value & v)
 }
 
 
-#if 0
 static void prim_sub(EvalState & state, Value * * args, Value & v)
 {
-    int i1 = evalInt(state, args[0]);
-    int i2 = evalInt(state, args[1]);
-    return makeInt(i1 - i2);
+    mkInt(v, state.forceInt(*args[0]) - state.forceInt(*args[1]));
 }
 
 
 static void prim_mul(EvalState & state, Value * * args, Value & v)
 {
-    int i1 = evalInt(state, args[0]);
-    int i2 = evalInt(state, args[1]);
-    return makeInt(i1 * i2);
+    mkInt(v, state.forceInt(*args[0]) * state.forceInt(*args[1]));
 }
 
 
 static void prim_div(EvalState & state, Value * * args, Value & v)
 {
-    int i1 = evalInt(state, args[0]);
-    int i2 = evalInt(state, args[1]);
+    int i2 = state.forceInt(*args[1]);
     if (i2 == 0) throw EvalError("division by zero");
-    return makeInt(i1 / i2);
+    mkInt(v, state.forceInt(*args[0]) / i2);
 }
-#endif
 
 
 static void prim_lessThan(EvalState & state, Value * * args, Value & v)
@@ -941,36 +942,36 @@ static void prim_toString(EvalState & state, Value * * args, Value & v)
 {
     PathSet context;
     string s = state.coerceToString(*args[0], context, true, false);
-    mkString(v, strdup(s.c_str())); // !!! context
+    mkString(v, s.c_str()); // !!! context
 }
 
 
-#if 0
 /* `substring start len str' returns the substring of `str' starting
    at character position `min(start, stringLength str)' inclusive and
    ending at `min(start + len, stringLength str)'.  `start' must be
    non-negative. */
 static void prim_substring(EvalState & state, Value * * args, Value & v)
 {
-    int start = evalInt(state, args[0]);
-    int len = evalInt(state, args[1]);
+    int start = state.forceInt(*args[0]);
+    int len = state.forceInt(*args[1]);
     PathSet context;
-    string s = coerceToString(state, args[2], context);
+    string s = state.coerceToString(*args[2], context);
 
     if (start < 0) throw EvalError("negative start position in `substring'");
 
-    return makeStr(string(s, start, len), context);
+    mkString(v, string(s, start, len), context);
 }
 
 
 static void prim_stringLength(EvalState & state, Value * * args, Value & v)
 {
     PathSet context;
-    string s = coerceToString(state, args[0], context);
-    return makeInt(s.size());
+    string s = state.coerceToString(*args[0], context);
+    mkInt(v, s.size());
 }
 
 
+#if 0
 static void prim_unsafeDiscardStringContext(EvalState & state, Value * * args, Value & v)
 {
     PathSet context;
@@ -1078,7 +1079,7 @@ void EvalState::createBaseEnv()
     mkInt(v, time(0));
     addConstant("__currentTime", v);
 
-    mkString(v, strdup(thisSystem.c_str()));
+    mkString(v, thisSystem.c_str());
     addConstant("__currentSystem", v);
 
     // Miscellaneous
@@ -1120,8 +1121,10 @@ void EvalState::createBaseEnv()
 
     // Attribute sets
     addPrimOp("__attrNames", 1, prim_attrNames);
+#endif
     addPrimOp("__getAttr", 2, prim_getAttr);
     addPrimOp("__hasAttr", 2, prim_hasAttr);
+#if 0
     addPrimOp("__isAttrs", 1, prim_isAttrs);
     addPrimOp("removeAttrs", 2, prim_removeAttrs);
     addPrimOp("__listToAttrs", 1, prim_listToAttrs);
@@ -1140,18 +1143,16 @@ void EvalState::createBaseEnv()
     
     // Integer arithmetic
     addPrimOp("__add", 2, prim_add);
-#if 0
     addPrimOp("__sub", 2, prim_sub);
     addPrimOp("__mul", 2, prim_mul);
     addPrimOp("__div", 2, prim_div);
-#endif
     addPrimOp("__lessThan", 2, prim_lessThan);
 
     // String manipulation
     addPrimOp("toString", 1, prim_toString);
-#if 0    
     addPrimOp("__substring", 3, prim_substring);
     addPrimOp("__stringLength", 1, prim_stringLength);
+#if 0    
     addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
     addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);