diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index bcd3670dfe..f87113e597 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 9c3c869bf3..d934470108 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 46cbb48ac5..ab4fa6cba3 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 0422e5cf46..f9ed34f58a 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 };