From 37d7abd69402f0e7a78d4d2f2d78996409a8563a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 25 Oct 2004 16:54:56 +0000 Subject: [PATCH] * New language feature: with expressions. The expression `with E1; E2' evaluates to E2 with all bindings in the attribute set E1 substituted. E.g., with {x = 123;}; x evaluates to 123. That is, the attribute set E1 is in scope in E2. This is particularly useful when importing files containing lots definitions. E.g., instead of let { inherit (import ./foo.nix) a b c d e f; body = ... a ... f ...; } we can now say with import ./foo.nix; ... a ... f ... I.e., we don't have to say what variables should be brought into scope. --- corepkgs/buildenv/builder.pl.in | 2 +- doc/manual/package-management.xml | 34 +++++++++++++++++++------------ src/libexpr/eval.cc | 28 +++++++++++++++++++++---- src/libexpr/lexer.l | 1 + src/libexpr/nixexpr.cc | 9 +++++++- src/libexpr/parser.y | 12 +++++------ src/libexpr/primops.cc | 2 +- 7 files changed, 61 insertions(+), 27 deletions(-) diff --git a/corepkgs/buildenv/builder.pl.in b/corepkgs/buildenv/builder.pl.in index e53b383f19..d6dd2f4cf9 100755 --- a/corepkgs/buildenv/builder.pl.in +++ b/corepkgs/buildenv/builder.pl.in @@ -27,7 +27,7 @@ sub createLinks { $srcFile =~ /\/nix-support$/ || $srcFile =~ /\/log$/) { - # Do noting. + # Do nothing. } elsif (-d $srcFile) { diff --git a/doc/manual/package-management.xml b/doc/manual/package-management.xml index 470cb80a09..b717844b11 100644 --- a/doc/manual/package-management.xml +++ b/doc/manual/package-management.xml @@ -17,18 +17,12 @@ available for installation. In Nix, different users can have different views on the set of installed applications. That is, there might be lots of applications present on the system (possibly in many different -versions), but users can have a specific selection of those -active — where active just means that it appears -in a directory in the user's PATH. - -Such a view on the set of installed applications is called a -user environment, which is just a directory tree -consisting of symlinks to the files of the active applications. In -Nix, operations such as upgrading or removing components never -overwrite or remove the files of those components, and they don't even -touch the user environments that point to them. Rather, they cause a -new user environment to be constructed based on -the old one. +versions), but users can have a specific selection of those active — +where active just means that it appears in a directory +in the user's PATH. Such a view on the set of +installed applications is called a user +environment, which is just a directory tree consisting of +symlinks to the files of the active applications. Components are installed from a set of Nix expressions that tell Nix how to build those components, @@ -168,7 +162,21 @@ set. Profiles -Bla +In Nix, operations such as upgrading or removing components +never overwrite or remove the files of those components, and they +don't even touch the user environments that point to them. Rather, +they cause a new user environment to be +constructed based on the old one. This is illustrated in Figure +bla. + +
User environments + + + + + +
+
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 89cc8254d2..215692aeb3 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -230,7 +230,7 @@ Expr evalExpr2(EvalState & state, Expr e) try { return evalExpr(state, substArgs(e4, formals, e2)); } catch (Error & e) { - throw Error(format("while evaluating function at %1%:\n%2%") + throw Error(format("while evaluating the function at %1%:\n%2%") % showPos(pos) % e.msg()); } } @@ -241,7 +241,7 @@ Expr evalExpr2(EvalState & state, Expr e) subs.set(name, e2); return evalExpr(state, substitute(subs, e4)); } catch (Error & e) { - throw Error(format("while evaluating function at %1%:\n%2%") + throw Error(format("while evaluating the function at %1%:\n%2%") % showPos(pos) % e.msg()); } } @@ -258,7 +258,7 @@ Expr evalExpr2(EvalState & state, Expr e) try { return evalExpr(state, a); } catch (Error & e) { - throw Error(format("while evaluating attribute `%1%' at %2%:\n%3%") + throw Error(format("while evaluating the attribute `%1%' at %2%:\n%3%") % s1 % showPos(pos) % e.msg()); } } @@ -283,6 +283,26 @@ Expr evalExpr2(EvalState & state, Expr e) return evalExpr(state, e2); } + /* Withs. */ + if (atMatch(m, e) >> "With" >> e1 >> e2 >> pos) { + ATermMap attrs; + try { + e1 = evalExpr(state, e1); + queryAllAttrs(e1, attrs); + } catch (Error & e) { + throw Error(format("while evaluating the `with' definitions at %1%:\n%2%") + % showPos(pos) % e.msg()); + } + try { + e2 = substitute(attrs, e2); + checkVarDefs(state.primOps, e2); + return evalExpr(state, e2); + } catch (Error & e) { + throw Error(format("while evaluating the `with' body at %1%:\n%2%") + % showPos(pos) % e.msg()); + } + } + /* Generic equality. */ if (atMatch(m, e) >> "OpEq" >> e1 >> e2) return makeBool(evalExpr(state, e1) == evalExpr(state, e2)); @@ -357,7 +377,7 @@ Expr evalFile(EvalState & state, const Path & path) try { return evalExpr(state, e); } catch (Error & e) { - throw Error(format("while evaluating file `%1%':\n%2%") + throw Error(format("while evaluating the file `%1%':\n%2%") % path % e.msg()); } } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 9637fd304d..78d08d0725 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -48,6 +48,7 @@ if { return IF; } then { return THEN; } else { return ELSE; } assert { return ASSERT; } +with { return WITH; } let { return LET; } rec { return REC; } inherit { return INHERIT; } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index dec734e466..78f89db5e5 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -296,7 +296,7 @@ void checkVarDefs(const ATermMap & defs, Expr e) ATMatcher m; ATerm name; ATermList formals; - ATerm body; + ATerm with, body; ATermList rbnds, nrbnds; if (atMatch(m, e) >> "Var" >> name) { @@ -340,6 +340,13 @@ void checkVarDefs(const ATermMap & defs, Expr e) } checkVarDefs(defs2, (ATerm) rbnds); } + + else if (atMatch(m, e) >> "With" >> with >> body) { + /* We can't check the body without evaluating the definitions + (which is an arbitrary expression), so we don't do that + here but only when actually evaluating the `with'. */ + checkVarDefs(defs, with); + } else if (ATgetType(e) == AT_APPL) { int arity = ATgetArity(ATgetAFun(e)); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 347516f692..88ee8326b8 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -41,11 +41,11 @@ ATerm makePos(YYLTYPE * loc, void * data) ATermList ts; } -%type start expr expr_function expr_assert expr_if expr_op +%type start expr expr_function expr_if expr_op %type expr_app expr_select expr_simple bind inheritsrc formal %type binds ids expr_list formals %token ID INT STR PATH URI -%token IF THEN ELSE ASSERT LET REC INHERIT EQ NEQ AND OR IMPL +%token IF THEN ELSE ASSERT WITH LET REC INHERIT EQ NEQ AND OR IMPL %nonassoc IMPL %left OR @@ -67,12 +67,10 @@ expr_function { $$ = ATmake("Function(, , )", $2, $5, CUR_POS); } | ID ':' expr_function { $$ = ATmake("Function1(, , )", $1, $3, CUR_POS); } - | expr_assert - ; - -expr_assert - : ASSERT expr ';' expr_assert + | ASSERT expr ';' expr_function { $$ = ATmake("Assert(, , )", $2, $4, CUR_POS); } + | WITH expr ';' expr_function + { $$ = ATmake("With(, , )", $2, $4, CUR_POS); } | expr_if ; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 070ed1b54a..938d9bb8c1 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -205,7 +205,7 @@ static Expr primDerivation(EvalState & state, const ATermVector & _args) try { processBinding(state, value, ne, ss); } catch (Error & e) { - throw Error(format("while processing derivation attribute `%1%' at %2%:\n%3%") + throw Error(format("while processing the derivation attribute `%1%' at %2%:\n%3%") % key % showPos(pos) % e.msg()); }