about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2010-04-14T15·01+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2010-04-14T15·01+0000
commit110d1557782fac4f8cafa27e5cbbcdebefb7a4c7 (patch)
tree4ddae428d11619534363f4e9be8dc10e35e7e5ce
parent9985230c00226826949473c3862c0c3afea74aaf (diff)
* Implemented withs.
-rw-r--r--src/libexpr/eval-test.cc3
-rw-r--r--src/libexpr/eval.cc37
-rw-r--r--src/libexpr/nixexpr.cc12
-rw-r--r--src/libexpr/nixexpr.hh1
4 files changed, 38 insertions, 15 deletions
diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc
index bcd3670dfedc..f87113e59704 100644
--- a/src/libexpr/eval-test.cc
+++ b/src/libexpr/eval-test.cc
@@ -72,7 +72,8 @@ void run(Strings args)
     doTest(state, "let { x = 1; body = x; }");
     doTest(state, "with { x = 1; }; x");
     doTest(state, "let x = 2; in with { x = 1; }; x"); // => 2
-    doTest(state, "with { x = 1; }; with { x = 2; }; x"); // => 1
+    doTest(state, "with { x = 1; }; with { x = 2; }; x"); // => 2
+    doTest(state, "with { x = 1; }; with { y = 2; }; x"); // => 1
     doTest(state, "[ 1 2 3 ]");
     doTest(state, "[ 1 2 ] ++ [ 3 4 5 ]");
     doTest(state, "123 == 123");
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 9c3c869bf3f2..d93447010830 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -253,8 +253,6 @@ Value * EvalState::lookupVar(Env * env, const Symbol & name)
         Bindings::iterator j = i->second.attrs->find(name);
         if (j != i->second.attrs->end()) return &j->second;
     }
-    
-    throwEvalError("urgh! undefined variable `%1%'", name);
 #endif
 }
 
@@ -483,13 +481,15 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
 
 void ExprVar::eval(EvalState & state, Env & env, Value & v)
 {
-    printMsg(lvlError, format("eval var %1% %2% %3%") % fromWith % level % displ);
-
+    Env * env2 = &env;
+    for (unsigned int l = level; l; --l, env2 = env2->up) ;
+    
     if (fromWith) {
-        abort();
+        Bindings::iterator j = env2->values[0].attrs->find(name);
+        if (j == env2->values[0].attrs->end())
+            throwEvalError("undefined variable `%1%'", name);
+        v = j->second;
     } else {
-        Env * env2 = &env;
-        for (unsigned int l = level; l; --l, env2 = env2->up) ;
         state.forceValue(env2->values[displ]);
         v = env2->values[displ];
     }
@@ -655,17 +655,26 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res
 
 void ExprWith::eval(EvalState & state, Env & env, Value & v)
 {
-    abort();
-#if 0
-    Env & env2(state.allocEnv());
+    Env & env2(state.allocEnv(1));
     env2.up = &env;
 
-    Value & vAttrs = env2.bindings[state.sWith];
-    state.eval(env, attrs, vAttrs);
-    state.forceAttrs(vAttrs);
+    state.eval(env, attrs, env2.values[0]);
+    state.forceAttrs(env2.values[0]);
+
+    /* If there is an enclosing `with', copy all attributes that don't
+       appear in this `with'. */
+    if (prevWith != -1) {
+        Env * env3 = &env;
+        for (unsigned int l = prevWith; l; --l, env3 = env3->up) ;
+
+        foreach (Bindings::iterator, i, *env3->values[0].attrs) {
+            Bindings::iterator j = env2.values[0].attrs->find(i->first);
+            if (j == env2.values[0].attrs->end())
+                (*env2.values[0].attrs)[i->first] = i->second; // !!! sharing
+        }
+    }
         
     state.eval(env2, body, v);
-#endif
 }
 
 
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 46cbb48ac5e5..ab4fa6cba3cb 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -264,6 +264,18 @@ void ExprLet::bindVars(const StaticEnv & env)
 
 void ExprWith::bindVars(const StaticEnv & env)
 {
+    /* Does this `with' have an enclosing `with'?  If so, record its
+       level so that we can copy the attributes of the enclosing
+       `with'. */
+    const StaticEnv * curEnv;
+    unsigned int level;
+    prevWith = -1;
+    for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++)
+        if (curEnv->isWith) {
+            prevWith = level;
+            break;
+        }
+    
     attrs->bindVars(env);    
     StaticEnv newEnv(true, &env);
     body->bindVars(newEnv);
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 0422e5cf4638..f9ed34f58a3c 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -170,6 +170,7 @@ struct ExprWith : Expr
 {
     Pos pos;
     Expr * attrs, * body;
+    int prevWith;
     ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
     COMMON_METHODS
 };