about summary refs log tree commit diff
path: root/src/libexpr/eval-test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr/eval-test.cc')
-rw-r--r--src/libexpr/eval-test.cc67
1 files changed, 55 insertions, 12 deletions
diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc
index db54011384d6..6f686bd6b604 100644
--- a/src/libexpr/eval-test.cc
+++ b/src/libexpr/eval-test.cc
@@ -29,6 +29,8 @@ typedef enum {
     tInt = 1,
     tBool,
     tString,
+    tPath,
+    tNull,
     tAttrs,
     tList,
     tThunk,
@@ -157,6 +159,23 @@ std::ostream & operator << (std::ostream & str, Value & v)
 static void eval(Env & env, Expr e, Value & v);
 
 
+string showType(Value & v)
+{
+    switch (v.type) {
+        case tString: return "a string";
+        case tPath: return "a path";
+        case tNull: return "null";
+        case tInt: return "an integer";
+        case tBool: return "a boolean";
+        case tLambda: return "a function";
+        case tAttrs: return "an attribute set";
+        case tList: return "a list";
+        case tPrimOpApp: return "a partially applied built-in function";
+        default: throw Error("unknown type");
+    }
+}
+
+
 static void forceValue(Value & v)
 {
     if (v.type == tThunk) {
@@ -172,6 +191,30 @@ static void forceValue(Value & v)
 }
 
 
+static void forceInt(Value & v)
+{
+    forceValue(v);
+    if (v.type != tInt)
+        throw TypeError(format("value is %1% while an integer was expected") % showType(v));
+}
+
+
+static void forceAttrs(Value & v)
+{
+    forceValue(v);
+    if (v.type != tAttrs)
+        throw TypeError(format("value is %1% while an attribute set was expected") % showType(v));
+}
+
+
+static void forceList(Value & v)
+{
+    forceValue(v);
+    if (v.type != tList)
+        throw TypeError(format("value is %1% while a list was expected") % showType(v));
+}
+
+
 static Value * lookupWith(Env * env, Sym name)
 {
     if (!env) return 0;
@@ -247,7 +290,7 @@ static bool eqValues(Value & v1, Value & v2)
 }
 
 
-unsigned long nrValues = 0, nrEnvs = 0;
+unsigned long nrValues = 0, nrEnvs = 0, nrEvaluated = 0;
 
 static Value * allocValues(unsigned int count)
 {
@@ -272,6 +315,8 @@ static void eval(Env & env, Expr e, Value & v)
 
     printMsg(lvlError, format("eval: %1%") % e);
 
+    nrEvaluated++;
+
     Sym name;
     if (matchVar(e, name)) {
         Value * v2 = lookupVar(&env, name);
@@ -328,7 +373,7 @@ static void eval(Env & env, Expr e, Value & v)
     Expr e1, e2;
     if (matchSelect(e, e2, name)) {
         eval(env, e2, v);
-        if (v.type != tAttrs) throw TypeError("expected attribute set");
+        forceAttrs(v); // !!! eval followed by force is slightly inefficient
         Bindings::iterator i = v.attrs->find(name);
         if (i == v.attrs->end()) throw TypeError("attribute not found");
         forceValue(i->second);
@@ -409,7 +454,7 @@ static void eval(Env & env, Expr e, Value & v)
             }                
 
             eval(env, arg, *vArg);
-            if (vArg->type != tAttrs) throw TypeError("expected attribute set");
+            forceAttrs(*vArg);
             
             /* For each formal argument, get the actual argument.  If
                there is no matching actual argument but the formal
@@ -459,7 +504,7 @@ static void eval(Env & env, Expr e, Value & v)
         Value & vAttrs = env2.bindings[sWith];
         nrValues++;
         eval(env, attrs, vAttrs);
-        if (vAttrs.type != tAttrs) throw TypeError("`with' should evaluate to an attribute set");
+        forceAttrs(vAttrs);
         
         eval(env2, body, v);
         return;
@@ -490,9 +535,9 @@ static void eval(Env & env, Expr e, Value & v)
 
     if (matchOpConcat(e, e1, e2)) {
         Value v1; eval(env, e1, v1);
-        if (v1.type != tList) throw TypeError("list expected");
+        forceList(v1);
         Value v2; eval(env, e2, v2);
-        if (v2.type != tList) throw TypeError("list expected");
+        forceList(v2);
         v.type = tList;
         v.list.length = v1.list.length + v2.list.length;
         v.list.elems = allocValues(v.list.length);
@@ -546,8 +591,7 @@ static void strictEval(Env & env, Expr e, Value & v)
 
 static void prim_head(Value * * args, Value & v)
 {
-    forceValue(*args[0]);
-    if (args[0]->type != tList) throw TypeError("list expected");
+    forceList(*args[0]);
     if (args[0]->list.length == 0)
         throw Error("`head' called on an empty list");
     forceValue(args[0]->list.elems[0]);
@@ -557,10 +601,8 @@ static void prim_head(Value * * args, Value & v)
 
 static void prim_add(Value * * args, Value & v)
 {
-    forceValue(*args[0]);
-    if (args[0]->type != tInt) throw TypeError("integer expected");
-    forceValue(*args[1]);
-    if (args[1]->type != tInt) throw TypeError("integer expected");
+    forceInt(*args[0]);
+    forceInt(*args[1]);
     mkInt(v, args[0]->integer + args[1]->integer);
 }
 
@@ -649,6 +691,7 @@ void run(Strings args)
     
     printMsg(lvlError, format("alloced %1% values") % nrValues);
     printMsg(lvlError, format("alloced %1% environments") % nrEnvs);
+    printMsg(lvlError, format("evaluated %1% expressions") % nrEvaluated);
     printMsg(lvlError, format("each eval() uses %1% bytes of stack space") % (p1 - p2));
 }