From 90039e0863469da7892830b122ccab017b29e2c8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Mar 2010 15:09:29 +0000 Subject: [PATCH 001/103] * Branch for experimenting with faster / less memory-hungry Nix expression evaluation. From 0910ae95680011915211769577a4219384b695af Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Mar 2010 17:30:50 +0000 Subject: [PATCH 002/103] * Start of an evaluator that uses call-by-need (with thunk updating) instead of (memoised) call-by-name. --- src/libexpr/Makefile.am | 8 ++ src/libexpr/eval-test.cc | 191 +++++++++++++++++++++++++++++++++++++++ src/libexpr/parser.hh | 2 +- 3 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 src/libexpr/eval-test.cc diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am index e16600fbf..c5f487769 100644 --- a/src/libexpr/Makefile.am +++ b/src/libexpr/Makefile.am @@ -49,3 +49,11 @@ nix.tbl: nix.sdf test.ast: test.nix nix.tbl sglri -p nix.tbl -i test.nix -o test.ast + + +# Testing. +bin_PROGRAMS = eval-test + +eval_test_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \ + ../libstore/libstore.la ../libutil/libutil.la \ + ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@ diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc new file mode 100644 index 000000000..c4e205682 --- /dev/null +++ b/src/libexpr/eval-test.cc @@ -0,0 +1,191 @@ +#include "nixexpr.hh" +#include "parser.hh" +#include "hash.hh" +#include "util.hh" +#include "nixexpr-ast.hh" + +#include + +using namespace nix; + + +typedef struct Env_ * Env; +typedef struct Value_ * Value; + +typedef std::map Bindings; + + +struct Env_ +{ + Env up; + Bindings bindings; +}; + + +typedef enum { + tInt = 1, + tAttrs, + tThunk +} ValueType; + + +struct Value_ +{ + ValueType type; + union + { + int integer; + Bindings * attrs; + struct { + Env env; + Expr expr; + } thunk; + }; +}; + + +std::ostream & operator << (std::ostream & str, Value_ & v) +{ + switch (v.type) { + case tInt: + str << v.integer; + break; + case tAttrs: + str << "{ "; + foreach (Bindings::iterator, i, *v.attrs) { + str << i->first << " = " << *i->second << "; "; + } + str << "}"; + break; + case tThunk: + str << ""; + break; + default: + abort(); + } + return str; +} + + +Value eval(Env env, Expr e); + + +void forceValue(Value v) +{ + if (v->type != tThunk) return; + Value v2 = eval(v->thunk.env, v->thunk.expr); + *v = *v2; // !!! slightly inefficient +} + + +Value lookupVar(Env env, const string & name) +{ + for ( ; env; env = env->up) { + Value v = env->bindings[name]; + if (v) return v; + } + throw Error("undefined variable"); +} + + +Value eval(Env env, Expr e) +{ + printMsg(lvlError, format("eval: %1%") % e); + + ATerm name; + if (matchVar(e, name)) { + Value v = lookupVar(env, aterm2String(name)); + forceValue(v); + return v; + } + + int n; + if (matchInt(e, n)) { + Value v = new Value_; + v->type = tInt; + v->integer = n; + return v; + } + + ATermList es; + if (matchAttrs(e, es)) { + Value v = new Value_; + v->type = tAttrs; + v->attrs = new Bindings; + ATerm e2, pos; + for (ATermIterator i(es); i; ++i) { + if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ + Value v2 = new Value_; + v2->type = tThunk; + v2->thunk.env = env; + v2->thunk.expr = e2; + (*v->attrs)[aterm2String(name)] = v2; + } + return v; + } + + ATermList rbnds, nrbnds; + if (matchRec(e, rbnds, nrbnds)) { + Env env2 = new Env_; + env2->up = env; + + Value v = new Value_; + v->type = tAttrs; + v->attrs = &env2->bindings; + ATerm name, e2, pos; + for (ATermIterator i(rbnds); i; ++i) { + if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ + Value v2 = new Value_; + v2->type = tThunk; + v2->thunk.env = env2; + v2->thunk.expr = e2; + env2->bindings[aterm2String(name)] = v2; + } + return v; + } + + ATerm e2; + if (matchSelect(e, e2, name)) { + Value v = eval(env, e2); + if (v->type != tAttrs) throw TypeError("expected attribute set"); + Value v2 = (*v->attrs)[aterm2String(name)]; + if (!v2) throw TypeError("attribute not found"); + forceValue(v2); + return v2; + } + + abort(); +} + + +void doTest(string s) +{ + EvalState state; + Expr e = parseExprFromString(state, s, "/"); + printMsg(lvlError, format("%1%") % e); + Value v = eval(0, e); + printMsg(lvlError, format("result: %1%") % *v); +} + + +void run(Strings args) +{ + printMsg(lvlError, format("size of value: %1% bytes") % sizeof(Value_)); + + doTest("123"); + doTest("{ x = 1; y = 2; }"); + doTest("{ x = 1; y = 2; }.y"); + doTest("rec { x = 1; y = x; }.y"); + //Expr e = parseExprFromString(state, "let x = \"a\"; in x + \"b\"", "/"); + //Expr e = parseExprFromString(state, "(x: x + \"b\") \"a\"", "/"); + //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/"); + //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/"); +} + + +void printHelp() +{ +} + + +string programId = "eval-test"; diff --git a/src/libexpr/parser.hh b/src/libexpr/parser.hh index 334822b5e..3f430eb32 100644 --- a/src/libexpr/parser.hh +++ b/src/libexpr/parser.hh @@ -8,7 +8,7 @@ namespace nix { /* Parse a Nix expression from the specified file. If `path' refers - to a directory, the "/default.nix" is appended. */ + to a directory, then "/default.nix" is appended. */ Expr parseExprFromFile(EvalState & state, Path path); /* Parse a Nix expression from the specified string. */ From e8f7978274708a8e0c29903910f4942da955c926 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Mar 2010 19:19:52 +0000 Subject: [PATCH 003/103] --- src/libutil/util.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index f609e8944..f4478b449 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -15,7 +15,7 @@ namespace nix { #define foreach(it_type, it, collection) \ - for (it_type it = collection.begin(); it != collection.end(); ++it) + for (it_type it = (collection).begin(); it != (collection).end(); ++it) /* Return an environment variable. */ From d31c59eb17d181082c955391f4e83a2cff1850a6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Mar 2010 11:06:05 +0000 Subject: [PATCH 004/103] * Plain lambdas. --- src/libexpr/eval-test.cc | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index c4e205682..c4fb7d184 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -25,7 +25,8 @@ struct Env_ typedef enum { tInt = 1, tAttrs, - tThunk + tThunk, + tLambda } ValueType; @@ -40,6 +41,11 @@ struct Value_ Env env; Expr expr; } thunk; + struct { + Env env; + Pattern pat; + Expr body; + } lambda; }; }; @@ -144,7 +150,7 @@ Value eval(Env env, Expr e) return v; } - ATerm e2; + Expr e2; if (matchSelect(e, e2, name)) { Value v = eval(env, e2); if (v->type != tAttrs) throw TypeError("expected attribute set"); @@ -154,6 +160,34 @@ Value eval(Env env, Expr e) return v2; } + Pattern pat; Expr body; Pos pos; + if (matchFunction(e, pat, body, pos)) { + Value v = new Value_; + v->type = tLambda; + v->lambda.env = env; + v->lambda.pat = pat; + v->lambda.body = body; + return v; + } + + Expr fun, arg; + if (matchCall(e, fun, arg)) { + Value fun_ = eval(env, fun); + if (fun_->type != tLambda) throw TypeError("expected function"); + if (!matchVarPat(fun_->lambda.pat, name)) throw Error("not implemented"); + + Value arg_ = new Value_; + arg_->type = tThunk; + arg_->thunk.env = env; + arg_->thunk.expr = arg; + + Env env2 = new Env_; + env2->up = env; + env2->bindings[aterm2String(name)] = arg_; + + return eval(env2, fun_->lambda.body); + } + abort(); } @@ -176,6 +210,8 @@ void run(Strings args) doTest("{ x = 1; y = 2; }"); doTest("{ x = 1; y = 2; }.y"); doTest("rec { x = 1; y = x; }.y"); + doTest("(x: x) 1"); + doTest("(x: y: y) 1 2"); //Expr e = parseExprFromString(state, "let x = \"a\"; in x + \"b\"", "/"); //Expr e = parseExprFromString(state, "(x: x + \"b\") \"a\"", "/"); //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/"); From b70bd8fe566a40f3b7c1b4dd96a7d5032d6da9c7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Mar 2010 12:11:38 +0000 Subject: [PATCH 005/103] * Reduce the number of value allocations in eval() by moving responsibility for allocation of the result to the caller. --- src/libexpr/eval-test.cc | 64 ++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index c4fb7d184..ee95fc1e7 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -73,14 +73,13 @@ std::ostream & operator << (std::ostream & str, Value_ & v) } -Value eval(Env env, Expr e); +void eval(Env env, Expr e, Value v); void forceValue(Value v) { if (v->type != tThunk) return; - Value v2 = eval(v->thunk.env, v->thunk.expr); - *v = *v2; // !!! slightly inefficient + eval(v->thunk.env, v->thunk.expr, v); } @@ -94,40 +93,48 @@ Value lookupVar(Env env, const string & name) } -Value eval(Env env, Expr e) +unsigned long nrValues = 0; + +Value allocValue() +{ + nrValues++; + return new Value_; +} + + +void eval(Env env, Expr e, Value v) { printMsg(lvlError, format("eval: %1%") % e); ATerm name; if (matchVar(e, name)) { - Value v = lookupVar(env, aterm2String(name)); - forceValue(v); - return v; + Value v2 = lookupVar(env, aterm2String(name)); + forceValue(v2); + *v = *v2; + return; } int n; if (matchInt(e, n)) { - Value v = new Value_; v->type = tInt; v->integer = n; - return v; + return; } ATermList es; if (matchAttrs(e, es)) { - Value v = new Value_; v->type = tAttrs; v->attrs = new Bindings; ATerm e2, pos; for (ATermIterator i(es); i; ++i) { if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value v2 = new Value_; + Value v2 = allocValue(); v2->type = tThunk; v2->thunk.env = env; v2->thunk.expr = e2; (*v->attrs)[aterm2String(name)] = v2; } - return v; + return; } ATermList rbnds, nrbnds; @@ -135,48 +142,48 @@ Value eval(Env env, Expr e) Env env2 = new Env_; env2->up = env; - Value v = new Value_; v->type = tAttrs; v->attrs = &env2->bindings; ATerm name, e2, pos; for (ATermIterator i(rbnds); i; ++i) { if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value v2 = new Value_; + Value v2 = allocValue(); v2->type = tThunk; v2->thunk.env = env2; v2->thunk.expr = e2; env2->bindings[aterm2String(name)] = v2; } - return v; + + return; } Expr e2; if (matchSelect(e, e2, name)) { - Value v = eval(env, e2); + eval(env, e2, v); if (v->type != tAttrs) throw TypeError("expected attribute set"); Value v2 = (*v->attrs)[aterm2String(name)]; if (!v2) throw TypeError("attribute not found"); forceValue(v2); - return v2; + *v = *v2; + return; } Pattern pat; Expr body; Pos pos; if (matchFunction(e, pat, body, pos)) { - Value v = new Value_; v->type = tLambda; v->lambda.env = env; v->lambda.pat = pat; v->lambda.body = body; - return v; + return; } Expr fun, arg; if (matchCall(e, fun, arg)) { - Value fun_ = eval(env, fun); - if (fun_->type != tLambda) throw TypeError("expected function"); - if (!matchVarPat(fun_->lambda.pat, name)) throw Error("not implemented"); + eval(env, fun, v); + if (v->type != tLambda) throw TypeError("expected function"); + if (!matchVarPat(v->lambda.pat, name)) throw Error("not implemented"); - Value arg_ = new Value_; + Value arg_ = allocValue(); arg_->type = tThunk; arg_->thunk.env = env; arg_->thunk.expr = arg; @@ -185,7 +192,8 @@ Value eval(Env env, Expr e) env2->up = env; env2->bindings[aterm2String(name)] = arg_; - return eval(env2, fun_->lambda.body); + eval(env2, v->lambda.body, v); + return; } abort(); @@ -197,8 +205,9 @@ void doTest(string s) EvalState state; Expr e = parseExprFromString(state, s, "/"); printMsg(lvlError, format("%1%") % e); - Value v = eval(0, e); - printMsg(lvlError, format("result: %1%") % *v); + Value_ v; + eval(0, e, &v); + printMsg(lvlError, format("result: %1%") % v); } @@ -212,10 +221,13 @@ void run(Strings args) doTest("rec { x = 1; y = x; }.y"); doTest("(x: x) 1"); doTest("(x: y: y) 1 2"); + //Expr e = parseExprFromString(state, "let x = \"a\"; in x + \"b\"", "/"); //Expr e = parseExprFromString(state, "(x: x + \"b\") \"a\"", "/"); //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/"); //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/"); + + printMsg(lvlError, format("alloced %1% values") % nrValues); } From 0fd3648d34b3ce52d4b55c7d7aedcc81ae32754d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Mar 2010 12:41:08 +0000 Subject: [PATCH 006/103] * Store values in environments. --- src/libexpr/eval-test.cc | 130 ++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index ee95fc1e7..68a7380e5 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -9,15 +9,15 @@ using namespace nix; -typedef struct Env_ * Env; -typedef struct Value_ * Value; +struct Env; +struct Value; typedef std::map Bindings; -struct Env_ +struct Env { - Env up; + Env * up; Bindings bindings; }; @@ -30,7 +30,7 @@ typedef enum { } ValueType; -struct Value_ +struct Value { ValueType type; union @@ -38,11 +38,11 @@ struct Value_ int integer; Bindings * attrs; struct { - Env env; + Env * env; Expr expr; } thunk; struct { - Env env; + Env * env; Pattern pat; Expr body; } lambda; @@ -50,7 +50,7 @@ struct Value_ }; -std::ostream & operator << (std::ostream & str, Value_ & v) +std::ostream & operator << (std::ostream & str, Value & v) { switch (v.type) { case tInt: @@ -58,9 +58,8 @@ std::ostream & operator << (std::ostream & str, Value_ & v) break; case tAttrs: str << "{ "; - foreach (Bindings::iterator, i, *v.attrs) { - str << i->first << " = " << *i->second << "; "; - } + foreach (Bindings::iterator, i, *v.attrs) + str << i->first << " = " << i->second << "; "; str << "}"; break; case tThunk: @@ -73,21 +72,21 @@ std::ostream & operator << (std::ostream & str, Value_ & v) } -void eval(Env env, Expr e, Value v); +void eval(Env * env, Expr e, Value & v); -void forceValue(Value v) +void forceValue(Value & v) { - if (v->type != tThunk) return; - eval(v->thunk.env, v->thunk.expr, v); + if (v.type != tThunk) return; + eval(v.thunk.env, v.thunk.expr, v); } -Value lookupVar(Env env, const string & name) +Value * lookupVar(Env * env, const string & name) { for ( ; env; env = env->up) { - Value v = env->bindings[name]; - if (v) return v; + Bindings::iterator i = env->bindings.find(name); + if (i != env->bindings.end()) return &i->second; } throw Error("undefined variable"); } @@ -95,63 +94,65 @@ Value lookupVar(Env env, const string & name) unsigned long nrValues = 0; -Value allocValue() +unsigned long nrEnvs = 0; + +Env * allocEnv() { - nrValues++; - return new Value_; + nrEnvs++; + return new Env; } -void eval(Env env, Expr e, Value v) +void eval(Env * env, Expr e, Value & v) { printMsg(lvlError, format("eval: %1%") % e); ATerm name; if (matchVar(e, name)) { - Value v2 = lookupVar(env, aterm2String(name)); - forceValue(v2); - *v = *v2; + Value * v2 = lookupVar(env, aterm2String(name)); + forceValue(*v2); + v = *v2; return; } int n; if (matchInt(e, n)) { - v->type = tInt; - v->integer = n; + v.type = tInt; + v.integer = n; return; } ATermList es; if (matchAttrs(e, es)) { - v->type = tAttrs; - v->attrs = new Bindings; + v.type = tAttrs; + v.attrs = new Bindings; ATerm e2, pos; for (ATermIterator i(es); i; ++i) { if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value v2 = allocValue(); - v2->type = tThunk; - v2->thunk.env = env; - v2->thunk.expr = e2; - (*v->attrs)[aterm2String(name)] = v2; + Value & v2 = (*v.attrs)[aterm2String(name)]; + nrValues++; + v2.type = tThunk; + v2.thunk.env = env; + v2.thunk.expr = e2; } return; } ATermList rbnds, nrbnds; if (matchRec(e, rbnds, nrbnds)) { - Env env2 = new Env_; + Env * env2 = allocEnv(); env2->up = env; - v->type = tAttrs; - v->attrs = &env2->bindings; + v.type = tAttrs; + v.attrs = &env2->bindings; ATerm name, e2, pos; for (ATermIterator i(rbnds); i; ++i) { if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value v2 = allocValue(); - v2->type = tThunk; - v2->thunk.env = env2; - v2->thunk.expr = e2; - env2->bindings[aterm2String(name)] = v2; + Value & v2 = env2->bindings[aterm2String(name)]; + nrValues++; + v2.type = tThunk; + v2.thunk.env = env2; + v2.thunk.expr = e2; } return; @@ -160,39 +161,39 @@ void eval(Env env, Expr e, Value v) Expr e2; if (matchSelect(e, e2, name)) { eval(env, e2, v); - if (v->type != tAttrs) throw TypeError("expected attribute set"); - Value v2 = (*v->attrs)[aterm2String(name)]; - if (!v2) throw TypeError("attribute not found"); - forceValue(v2); - *v = *v2; + if (v.type != tAttrs) throw TypeError("expected attribute set"); + Bindings::iterator i = v.attrs->find(aterm2String(name)); + if (i == v.attrs->end()) throw TypeError("attribute not found"); + forceValue(i->second); + v = i->second; return; } Pattern pat; Expr body; Pos pos; if (matchFunction(e, pat, body, pos)) { - v->type = tLambda; - v->lambda.env = env; - v->lambda.pat = pat; - v->lambda.body = body; + v.type = tLambda; + v.lambda.env = env; + v.lambda.pat = pat; + v.lambda.body = body; return; } Expr fun, arg; if (matchCall(e, fun, arg)) { eval(env, fun, v); - if (v->type != tLambda) throw TypeError("expected function"); - if (!matchVarPat(v->lambda.pat, name)) throw Error("not implemented"); + if (v.type != tLambda) throw TypeError("expected function"); + if (!matchVarPat(v.lambda.pat, name)) throw Error("not implemented"); - Value arg_ = allocValue(); - arg_->type = tThunk; - arg_->thunk.env = env; - arg_->thunk.expr = arg; - - Env env2 = new Env_; + Env * env2 = allocEnv(); env2->up = env; - env2->bindings[aterm2String(name)] = arg_; + + Value & arg_ = env2->bindings[aterm2String(name)]; + nrValues++; + arg_.type = tThunk; + arg_.thunk.env = env; + arg_.thunk.expr = arg; - eval(env2, v->lambda.body, v); + eval(env2, v.lambda.body, v); return; } @@ -205,15 +206,15 @@ void doTest(string s) EvalState state; Expr e = parseExprFromString(state, s, "/"); printMsg(lvlError, format("%1%") % e); - Value_ v; - eval(0, e, &v); + Value v; + eval(0, e, v); printMsg(lvlError, format("result: %1%") % v); } void run(Strings args) { - printMsg(lvlError, format("size of value: %1% bytes") % sizeof(Value_)); + printMsg(lvlError, format("size of value: %1% bytes") % sizeof(Value)); doTest("123"); doTest("{ x = 1; y = 2; }"); @@ -228,6 +229,7 @@ void run(Strings args) //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/"); printMsg(lvlError, format("alloced %1% values") % nrValues); + printMsg(lvlError, format("alloced %1% environments") % nrEnvs); } From 7482349fe8ba9f285f6c7e53d8573fc367ecff8b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Mar 2010 23:40:00 +0000 Subject: [PATCH 007/103] * Implemented attribute set pattern matches. --- src/libexpr/eval-test.cc | 130 +++++++++++++++++++++++++++++++++++---- 1 file changed, 117 insertions(+), 13 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 68a7380e5..27cf685f9 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -26,7 +26,8 @@ typedef enum { tInt = 1, tAttrs, tThunk, - tLambda + tLambda, + tCopy } ValueType; @@ -46,6 +47,7 @@ struct Value Pattern pat; Expr body; } lambda; + Value * val; }; }; @@ -65,6 +67,9 @@ std::ostream & operator << (std::ostream & str, Value & v) case tThunk: str << ""; break; + case tLambda: + str << ""; + break; default: abort(); } @@ -72,13 +77,16 @@ std::ostream & operator << (std::ostream & str, Value & v) } -void eval(Env * env, Expr e, Value & v); +static void eval(Env * env, Expr e, Value & v); void forceValue(Value & v) { - if (v.type != tThunk) return; - eval(v.thunk.env, v.thunk.expr, v); + if (v.type == tThunk) eval(v.thunk.env, v.thunk.expr, v); + else if (v.type == tCopy) { + forceValue(*v.val); + v = *v.val; + } } @@ -103,7 +111,92 @@ Env * allocEnv() } -void eval(Env * env, Expr e, Value & v) +static bool patternIsStrict(Pattern pat) +{ + ATerm name, ellipsis, pat1, pat2; + ATermList formals; + if (matchVarPat(pat, name)) return false; + else if (matchAttrsPat(pat, formals, ellipsis)) return true; + else if (matchAtPat(pat, pat1, pat2)) + return patternIsStrict(pat1) || patternIsStrict(pat2); + else abort(); +} + + +static void bindVarPats(Pattern pat, Env & newEnv, + Env * argEnv, Expr argExpr, Value * & vArg) +{ + Pattern pat1, pat2; + if (matchAtPat(pat, pat1, pat2)) { + bindVarPats(pat1, newEnv, argEnv, argExpr, vArg); + bindVarPats(pat2, newEnv, argEnv, argExpr, vArg); + return; + } + + ATerm name; + if (!matchVarPat(pat, name)) abort(); + + if (vArg) { + Value & v = newEnv.bindings[aterm2String(name)]; + v.type = tCopy; + v.val = vArg; + } else { + vArg = &newEnv.bindings[aterm2String(name)]; + vArg->type = tThunk; + vArg->thunk.env = argEnv; + vArg->thunk.expr = argExpr; + } +} + + +static void bindAttrPats(Pattern pat, Env & newEnv, + Value & vArg, Value * & vArgInEnv) +{ + Pattern pat1, pat2; + if (matchAtPat(pat, pat1, pat2)) { + bindAttrPats(pat1, newEnv, vArg, vArgInEnv); + bindAttrPats(pat2, newEnv, vArg, vArgInEnv); + return; + } + + ATerm name; + if (matchVarPat(pat, name)) { + if (vArgInEnv) { + Value & v = newEnv.bindings[aterm2String(name)]; + v.type = tCopy; + v.val = vArgInEnv; + } else { + vArgInEnv = &newEnv.bindings[aterm2String(name)]; + *vArgInEnv = vArg; + } + return; + } + + ATerm ellipsis; + ATermList formals; + if (matchAttrsPat(pat, formals, ellipsis)) { + for (ATermIterator i(formals); i; ++i) { + Expr name, def; + DefaultValue def2; + if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ + + Bindings::iterator j = vArg.attrs->find(aterm2String(name)); + if (j == vArg.attrs->end()) + throw TypeError(format("the argument named `%1%' required by the function is missing") + % aterm2String(name)); + + Value & v = newEnv.bindings[aterm2String(name)]; + v.type = tCopy; + v.val = &j->second; + } + return; + } + + abort(); +} + + +static void eval(Env * env, Expr e, Value & v) { printMsg(lvlError, format("eval: %1%") % e); @@ -182,17 +275,21 @@ void eval(Env * env, Expr e, Value & v) if (matchCall(e, fun, arg)) { eval(env, fun, v); if (v.type != tLambda) throw TypeError("expected function"); - if (!matchVarPat(v.lambda.pat, name)) throw Error("not implemented"); Env * env2 = allocEnv(); env2->up = env; - - Value & arg_ = env2->bindings[aterm2String(name)]; - nrValues++; - arg_.type = tThunk; - arg_.thunk.env = env; - arg_.thunk.expr = arg; + if (patternIsStrict(v.lambda.pat)) { + Value vArg; + eval(env, arg, vArg); + if (vArg.type != tAttrs) throw TypeError("expected attribute set"); + Value * vArg2 = 0; + bindAttrPats(v.lambda.pat, *env2, vArg, vArg2); + } else { + Value * vArg = 0; + bindVarPats(v.lambda.pat, *env2, env, arg, vArg); + } + eval(env2, v.lambda.body, v); return; } @@ -205,7 +302,7 @@ void doTest(string s) { EvalState state; Expr e = parseExprFromString(state, s, "/"); - printMsg(lvlError, format("%1%") % e); + printMsg(lvlError, format(">>>>> %1%") % e); Value v; eval(0, e, v); printMsg(lvlError, format("result: %1%") % v); @@ -222,6 +319,13 @@ void run(Strings args) doTest("rec { x = 1; y = x; }.y"); doTest("(x: x) 1"); doTest("(x: y: y) 1 2"); + doTest("(x@y: x) 1"); + doTest("(x@y: y) 2"); + doTest("(x@y@z: y) 3"); + doTest("x: x"); + doTest("({x, y}: x) { x = 1; y = 2; }"); + doTest("({x, y}@args: args.x) { x = 1; y = 2; }"); + doTest("({x, y}@args@args2: args2.x) { x = 1; y = 2; }"); //Expr e = parseExprFromString(state, "let x = \"a\"; in x + \"b\"", "/"); //Expr e = parseExprFromString(state, "(x: x + \"b\") \"a\"", "/"); From 8a10360c912bc344ea9ce7f8871a47a6e036552f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 25 Mar 2010 12:19:41 +0000 Subject: [PATCH 008/103] * Simplify @-patterns: only `{attrs}@name' or `name@{attrs}' are now allowed. So `name1@name2', `{attrs1}@{attrs2}' and so on are now no longer legal. This is no big loss because they were not useful anyway. This also changes the output of builtins.toXML for @-patterns slightly. --- src/libexpr/eval-test.cc | 136 +++++------------- src/libexpr/eval.cc | 15 +- src/libexpr/expr-to-xml.cc | 14 +- src/libexpr/nixexpr-ast.def | 4 +- src/libexpr/nixexpr.cc | 8 +- src/libexpr/parser.y | 45 +++--- src/libexpr/primops.cc | 7 +- tests/lang/eval-okay-patterns.exp | 2 +- tests/lang/eval-okay-patterns.nix | 3 - tests/lang/eval-okay-xml.exp.xml | 16 +-- tests/lang/parse-okay-1.exp | 2 +- tests/lang/parse-okay-regression-20041027.exp | 2 +- tests/lang/parse-okay-subversion.exp | 2 +- 13 files changed, 83 insertions(+), 173 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 27cf685f9..71b16384b 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -111,91 +111,6 @@ Env * allocEnv() } -static bool patternIsStrict(Pattern pat) -{ - ATerm name, ellipsis, pat1, pat2; - ATermList formals; - if (matchVarPat(pat, name)) return false; - else if (matchAttrsPat(pat, formals, ellipsis)) return true; - else if (matchAtPat(pat, pat1, pat2)) - return patternIsStrict(pat1) || patternIsStrict(pat2); - else abort(); -} - - -static void bindVarPats(Pattern pat, Env & newEnv, - Env * argEnv, Expr argExpr, Value * & vArg) -{ - Pattern pat1, pat2; - if (matchAtPat(pat, pat1, pat2)) { - bindVarPats(pat1, newEnv, argEnv, argExpr, vArg); - bindVarPats(pat2, newEnv, argEnv, argExpr, vArg); - return; - } - - ATerm name; - if (!matchVarPat(pat, name)) abort(); - - if (vArg) { - Value & v = newEnv.bindings[aterm2String(name)]; - v.type = tCopy; - v.val = vArg; - } else { - vArg = &newEnv.bindings[aterm2String(name)]; - vArg->type = tThunk; - vArg->thunk.env = argEnv; - vArg->thunk.expr = argExpr; - } -} - - -static void bindAttrPats(Pattern pat, Env & newEnv, - Value & vArg, Value * & vArgInEnv) -{ - Pattern pat1, pat2; - if (matchAtPat(pat, pat1, pat2)) { - bindAttrPats(pat1, newEnv, vArg, vArgInEnv); - bindAttrPats(pat2, newEnv, vArg, vArgInEnv); - return; - } - - ATerm name; - if (matchVarPat(pat, name)) { - if (vArgInEnv) { - Value & v = newEnv.bindings[aterm2String(name)]; - v.type = tCopy; - v.val = vArgInEnv; - } else { - vArgInEnv = &newEnv.bindings[aterm2String(name)]; - *vArgInEnv = vArg; - } - return; - } - - ATerm ellipsis; - ATermList formals; - if (matchAttrsPat(pat, formals, ellipsis)) { - for (ATermIterator i(formals); i; ++i) { - Expr name, def; - DefaultValue def2; - if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ - - Bindings::iterator j = vArg.attrs->find(aterm2String(name)); - if (j == vArg.attrs->end()) - throw TypeError(format("the argument named `%1%' required by the function is missing") - % aterm2String(name)); - - Value & v = newEnv.bindings[aterm2String(name)]; - v.type = tCopy; - v.val = &j->second; - } - return; - } - - abort(); -} - - static void eval(Env * env, Expr e, Value & v) { printMsg(lvlError, format("eval: %1%") % e); @@ -279,16 +194,44 @@ static void eval(Env * env, Expr e, Value & v) Env * env2 = allocEnv(); env2->up = env; - if (patternIsStrict(v.lambda.pat)) { - Value vArg; - eval(env, arg, vArg); - if (vArg.type != tAttrs) throw TypeError("expected attribute set"); - Value * vArg2 = 0; - bindAttrPats(v.lambda.pat, *env2, vArg, vArg2); - } else { - Value * vArg = 0; - bindVarPats(v.lambda.pat, *env2, env, arg, vArg); + ATermList formals; ATerm ellipsis; + + if (matchVarPat(v.lambda.pat, name)) { + Value & vArg = env2->bindings[aterm2String(name)]; + vArg.type = tThunk; + vArg.thunk.env = env; + vArg.thunk.expr = arg; } + + else if (matchAttrsPat(v.lambda.pat, formals, ellipsis, name)) { + Value * vArg; + Value vArg_; + + if (name == sNoAlias) + vArg = &vArg_; + else + vArg = &env2->bindings[aterm2String(name)]; + + eval(env, arg, *vArg); + if (vArg->type != tAttrs) throw TypeError("expected attribute set"); + + for (ATermIterator i(formals); i; ++i) { + Expr name, def; + DefaultValue def2; + if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ + + Bindings::iterator j = vArg->attrs->find(aterm2String(name)); + if (j == vArg->attrs->end()) + throw TypeError(format("the argument named `%1%' required by the function is missing") + % aterm2String(name)); + + Value & v = env2->bindings[aterm2String(name)]; + v.type = tCopy; + v.val = &j->second; + } + } + + else abort(); eval(env2, v.lambda.body, v); return; @@ -319,13 +262,10 @@ void run(Strings args) doTest("rec { x = 1; y = x; }.y"); doTest("(x: x) 1"); doTest("(x: y: y) 1 2"); - doTest("(x@y: x) 1"); - doTest("(x@y: y) 2"); - doTest("(x@y@z: y) 3"); doTest("x: x"); doTest("({x, y}: x) { x = 1; y = 2; }"); doTest("({x, y}@args: args.x) { x = 1; y = 2; }"); - doTest("({x, y}@args@args2: args2.x) { x = 1; y = 2; }"); + doTest("(args@{x, y}: args.x) { x = 1; y = 2; }"); //Expr e = parseExprFromString(state, "let x = \"a\"; in x + \"b\"", "/"); //Expr e = parseExprFromString(state, "(x: x + \"b\") \"a\"", "/"); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index cd9c64594..1501fc048 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -85,16 +85,17 @@ static void patternMatch(EvalState & state, { ATerm name; ATermList formals; - Pattern pat1, pat2; ATermBool ellipsis; if (matchVarPat(pat, name)) subs.set(name, arg); - else if (matchAttrsPat(pat, formals, ellipsis)) { + else if (matchAttrsPat(pat, formals, ellipsis, name)) { arg = evalExpr(state, arg); + if (name != sNoAlias) subs.set(name, arg); + /* Get the actual arguments. */ ATermMap attrs; queryAllAttrs(arg, attrs); @@ -131,11 +132,6 @@ static void patternMatch(EvalState & state, % aterm2String(attrs.begin()->key)); } - else if (matchAtPat(pat, pat1, pat2)) { - patternMatch(state, pat1, arg, subs, subsRecursive); - patternMatch(state, pat2, arg, subs, subsRecursive); - } - else abort(); } @@ -425,12 +421,11 @@ Path coerceToPath(EvalState & state, Expr e, PathSet & context) Expr autoCallFunction(Expr e, const ATermMap & args) { Pattern pat; - ATerm body, pos; + ATerm body, pos, name; ATermList formals; ATermBool ellipsis; - /* !!! this should be more general */ - if (matchFunction(e, pat, body, pos) && matchAttrsPat(pat, formals, ellipsis)) { + if (matchFunction(e, pat, body, pos) && matchAttrsPat(pat, formals, ellipsis, name)) { ATermMap actualArgs(ATgetLength(formals)); for (ATermIterator i(formals); i; ++i) { diff --git a/src/libexpr/expr-to-xml.cc b/src/libexpr/expr-to-xml.cc index e401001ea..9b3062804 100644 --- a/src/libexpr/expr-to-xml.cc +++ b/src/libexpr/expr-to-xml.cc @@ -44,23 +44,19 @@ static void printPatternAsXML(Pattern pat, XMLWriter & doc) { ATerm name; ATermList formals; - Pattern pat1, pat2; ATermBool ellipsis; if (matchVarPat(pat, name)) doc.writeEmptyElement("varpat", singletonAttrs("name", aterm2String(name))); - else if (matchAttrsPat(pat, formals, ellipsis)) { - XMLOpenElement _(doc, "attrspat"); + else if (matchAttrsPat(pat, formals, ellipsis, name)) { + XMLAttrs attrs; + if (name != sNoAlias) attrs["name"] = aterm2String(name); + if (ellipsis == eTrue) attrs["ellipsis"] = "1"; + XMLOpenElement _(doc, "attrspat", attrs); for (ATermIterator i(formals); i; ++i) { Expr name; ATerm dummy; if (!matchFormal(*i, name, dummy)) abort(); doc.writeEmptyElement("attr", singletonAttrs("name", aterm2String(name))); } - if (ellipsis == eTrue) doc.writeEmptyElement("ellipsis"); - } - else if (matchAtPat(pat, pat1, pat2)) { - XMLOpenElement _(doc, "at"); - printPatternAsXML(pat1, doc); - printPatternAsXML(pat2, doc); } } diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def index 00b5f5a13..2bf8492dd 100644 --- a/src/libexpr/nixexpr-ast.def +++ b/src/libexpr/nixexpr-ast.def @@ -77,8 +77,7 @@ Inherit | Expr ATermList Pos | ATerm | Scope | | Expr | VarPat | string | Pattern | -AttrsPat | ATermList ATermBool | Pattern | # bool = `...' -AtPat | Pattern Pattern | Pattern | +AttrsPat | ATermList ATermBool string | Pattern | # bool = `...' Formal | string DefaultValue | ATerm | @@ -95,3 +94,4 @@ AttrRHS | Expr Pos | ATerm | eTrue = makeBool(makeTrue()) eFalse = makeBool(makeFalse()) sOverrides = toATerm("__overrides") +sNoAlias = toATerm("") diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 3675dccea..e581b1329 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -114,23 +114,19 @@ static void varsBoundByPattern(ATermMap & map, Pattern pat) { ATerm name; ATermList formals; - Pattern pat1, pat2; ATermBool ellipsis; /* Use makeRemoved() so that it can be used directly in substitute(). */ if (matchVarPat(pat, name)) map.set(name, makeRemoved()); - else if (matchAttrsPat(pat, formals, ellipsis)) { + else if (matchAttrsPat(pat, formals, ellipsis, name)) { + if (name != sNoAlias) map.set(name, makeRemoved()); for (ATermIterator i(formals); i; ++i) { ATerm d1; if (!matchFormal(*i, name, d1)) abort(); map.set(name, makeRemoved()); } } - else if (matchAtPat(pat, pat1, pat2)) { - varsBoundByPattern(map, pat1); - varsBoundByPattern(map, pat2); - } else abort(); } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 8706ce025..e55dfd76f 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -140,31 +140,29 @@ static Expr fixAttrs(bool recursive, ATermList as) static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat) { - ATerm name; + ATerm name = sNoAlias; ATermList formals; - Pattern pat1, pat2; ATermBool ellipsis; - if (matchVarPat(pat, name)) { + + if (matchAttrsPat(pat, formals, ellipsis, name)) { + for (ATermIterator i(formals); i; ++i) { + ATerm d1, name2; + if (!matchFormal(*i, name2, d1)) abort(); + if (map.get(name2)) + throw ParseError(format("duplicate formal function argument `%1%' at %2%") + % aterm2String(name2) % showPos(pos)); + map.set(name2, name2); + } + } + + else matchVarPat(pat, name); + + if (name != sNoAlias) { if (map.get(name)) throw ParseError(format("duplicate formal function argument `%1%' at %2%") % aterm2String(name) % showPos(pos)); map.set(name, name); } - else if (matchAttrsPat(pat, formals, ellipsis)) { - for (ATermIterator i(formals); i; ++i) { - ATerm d1; - if (!matchFormal(*i, name, d1)) abort(); - if (map.get(name)) - throw ParseError(format("duplicate formal function argument `%1%' at %2%") - % aterm2String(name) % showPos(pos)); - map.set(name, name); - } - } - else if (matchAtPat(pat, pat1, pat2)) { - checkPatternVars(pos, map, pat1); - checkPatternVars(pos, map, pat2); - } - else abort(); } @@ -323,7 +321,7 @@ static void freeAndUnprotect(void * p) %type start expr expr_function expr_if expr_op %type expr_app expr_select expr_simple bind inheritsrc formal -%type pattern pattern2 +%type pattern %type binds ids attrpath expr_list string_parts ind_string_parts %type formals %token ID INT STR IND_STR PATH URI @@ -433,13 +431,10 @@ ind_string_parts ; pattern - : pattern2 '@' pattern { $$ = makeAtPat($1, $3); } - | pattern2 - ; - -pattern2 : ID { $$ = makeVarPat($1); } - | '{' formals '}' { $$ = makeAttrsPat($2.formals, $2.ellipsis ? eTrue : eFalse); } + | '{' formals '}' { $$ = makeAttrsPat($2.formals, $2.ellipsis ? eTrue : eFalse, sNoAlias); } + | '{' formals '}' '@' ID { $$ = makeAttrsPat($2.formals, $2.ellipsis ? eTrue : eFalse, $5); } + | ID '@' '{' formals '}' { $$ = makeAttrsPat($4.formals, $4.ellipsis ? eTrue : eFalse, $1); } ; binds diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7dddc91a8..52292f3f8 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -846,19 +846,14 @@ static void attrsInPattern(ATermMap & map, Pattern pat) { ATerm name; ATermList formals; - Pattern pat1, pat2; ATermBool ellipsis; - if (matchAttrsPat(pat, formals, ellipsis)) { + if (matchAttrsPat(pat, formals, ellipsis, name)) { for (ATermIterator i(formals); i; ++i) { ATerm def; if (!matchFormal(*i, name, def)) abort(); map.set(name, makeAttrRHS(makeBool(def != constNoDefaultValue), makeNoPos())); } } - else if (matchAtPat(pat, pat1, pat2)) { - attrsInPattern(map, pat1); - attrsInPattern(map, pat2); - } } diff --git a/tests/lang/eval-okay-patterns.exp b/tests/lang/eval-okay-patterns.exp index 8422900dd..d497e9f2b 100644 --- a/tests/lang/eval-okay-patterns.exp +++ b/tests/lang/eval-okay-patterns.exp @@ -1 +1 @@ -Str("abcxyzDDDDEFghijk",[]) +Str("abcxyzDDDDEFijk",[]) diff --git a/tests/lang/eval-okay-patterns.nix b/tests/lang/eval-okay-patterns.nix index c233c406c..96fd25a01 100644 --- a/tests/lang/eval-okay-patterns.nix +++ b/tests/lang/eval-okay-patterns.nix @@ -6,8 +6,6 @@ let h = {x ? "d", y ? x, z ? args.x}@args: x + y + z; - i = args@args2: args.x + args2.y; - j = {x, y, z, ...}: x + y + z; in @@ -15,5 +13,4 @@ in g {x = "x"; y = "y"; z = "z";} + h {x = "D";} + h {x = "D"; y = "E"; z = "F";} + - i {x = "g"; y = "h";} + j {x = "i"; y = "j"; z = "k"; bla = "bla"; foo = "bar";} diff --git a/tests/lang/eval-okay-xml.exp.xml b/tests/lang/eval-okay-xml.exp.xml index 72a96d54c..f124f939e 100644 --- a/tests/lang/eval-okay-xml.exp.xml +++ b/tests/lang/eval-okay-xml.exp.xml @@ -6,14 +6,11 @@ - - - - - - - - + + + + + @@ -24,11 +21,10 @@ - + - diff --git a/tests/lang/parse-okay-1.exp b/tests/lang/parse-okay-1.exp index bb8746ec4..2a6f05dd5 100644 --- a/tests/lang/parse-okay-1.exp +++ b/tests/lang/parse-okay-1.exp @@ -1 +1 @@ -Function(AttrsPat([Formal("x",NoDefaultValue),Formal("y",NoDefaultValue),Formal("z",NoDefaultValue)],Bool(False)),OpPlus(OpPlus(Var("x"),Var("y")),Var("z")),NoPos) +Function(AttrsPat([Formal("x",NoDefaultValue),Formal("y",NoDefaultValue),Formal("z",NoDefaultValue)],Bool(False),""),OpPlus(OpPlus(Var("x"),Var("y")),Var("z")),NoPos) diff --git a/tests/lang/parse-okay-regression-20041027.exp b/tests/lang/parse-okay-regression-20041027.exp index a302e2488..48251516d 100644 --- a/tests/lang/parse-okay-regression-20041027.exp +++ b/tests/lang/parse-okay-regression-20041027.exp @@ -1 +1 @@ -Function(AttrsPat([Formal("stdenv",NoDefaultValue),Formal("fetchurl",NoDefaultValue)],Bool(False)),Call(Select(Var("stdenv"),"mkDerivation"),Attrs([Bind("name",Str("libXi-6.0.1",[]),NoPos),Bind("src",Call(Var("fetchurl"),Attrs([Bind("md5",Str("7e935a42428d63a387b3c048be0f2756",[]),NoPos),Bind("url",Str("http://freedesktop.org/~xlibs/release/libXi-6.0.1.tar.bz2",[]),NoPos)])),NoPos)])),NoPos) +Function(AttrsPat([Formal("stdenv",NoDefaultValue),Formal("fetchurl",NoDefaultValue)],Bool(False),""),Call(Select(Var("stdenv"),"mkDerivation"),Attrs([Bind("name",Str("libXi-6.0.1",[]),NoPos),Bind("src",Call(Var("fetchurl"),Attrs([Bind("md5",Str("7e935a42428d63a387b3c048be0f2756",[]),NoPos),Bind("url",Str("http://freedesktop.org/~xlibs/release/libXi-6.0.1.tar.bz2",[]),NoPos)])),NoPos)])),NoPos) diff --git a/tests/lang/parse-okay-subversion.exp b/tests/lang/parse-okay-subversion.exp index 102153c18..b54e3af78 100644 --- a/tests/lang/parse-okay-subversion.exp +++ b/tests/lang/parse-okay-subversion.exp @@ -1 +1 @@ -Function(AttrsPat([Formal("localServer",DefaultValue(Var("false"))),Formal("httpServer",DefaultValue(Var("false"))),Formal("sslSupport",DefaultValue(Var("false"))),Formal("pythonBindings",DefaultValue(Var("false"))),Formal("javaSwigBindings",DefaultValue(Var("false"))),Formal("javahlBindings",DefaultValue(Var("false"))),Formal("stdenv",NoDefaultValue),Formal("fetchurl",NoDefaultValue),Formal("openssl",DefaultValue(Var("null"))),Formal("httpd",DefaultValue(Var("null"))),Formal("db4",DefaultValue(Var("null"))),Formal("expat",NoDefaultValue),Formal("swig",DefaultValue(Var("null"))),Formal("j2sdk",DefaultValue(Var("null")))],Bool(False)),Assert(OpNEq(Var("expat"),Var("null")),Assert(OpImpl(Var("localServer"),OpNEq(Var("db4"),Var("null"))),Assert(OpImpl(Var("httpServer"),OpAnd(OpNEq(Var("httpd"),Var("null")),OpEq(Select(Var("httpd"),"expat"),Var("expat")))),Assert(OpImpl(Var("sslSupport"),OpAnd(OpNEq(Var("openssl"),Var("null")),OpImpl(Var("httpServer"),OpEq(Select(Var("httpd"),"openssl"),Var("openssl"))))),Assert(OpImpl(Var("pythonBindings"),OpAnd(OpNEq(Var("swig"),Var("null")),Select(Var("swig"),"pythonSupport"))),Assert(OpImpl(Var("javaSwigBindings"),OpAnd(OpNEq(Var("swig"),Var("null")),Select(Var("swig"),"javaSupport"))),Assert(OpImpl(Var("javahlBindings"),OpNEq(Var("j2sdk"),Var("null"))),Call(Select(Var("stdenv"),"mkDerivation"),Attrs([Bind("builder",Path("/foo/bar"),NoPos),Bind("db4",If(Var("localServer"),Var("db4"),Var("null")),NoPos),Bind("expat",Var("expat"),NoPos),Bind("httpServer",Var("httpServer"),NoPos),Bind("httpd",If(Var("httpServer"),Var("httpd"),Var("null")),NoPos),Bind("j2sdk",If(Var("javaSwigBindings"),Select(Var("swig"),"j2sdk"),If(Var("javahlBindings"),Var("j2sdk"),Var("null"))),NoPos),Bind("javaSwigBindings",Var("javaSwigBindings"),NoPos),Bind("javahlBindings",Var("javahlBindings"),NoPos),Bind("localServer",Var("localServer"),NoPos),Bind("name",Str("subversion-1.1.1",[]),NoPos),Bind("openssl",If(Var("sslSupport"),Var("openssl"),Var("null")),NoPos),Bind("patches",If(Var("javahlBindings"),List([Path("/javahl.patch")]),List([])),NoPos),Bind("python",If(Var("pythonBindings"),Select(Var("swig"),"python"),Var("null")),NoPos),Bind("pythonBindings",Var("pythonBindings"),NoPos),Bind("src",Call(Var("fetchurl"),Attrs([Bind("md5",Str("a180c3fe91680389c210c99def54d9e0",[]),NoPos),Bind("url",Str("http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2",[]),NoPos)])),NoPos),Bind("sslSupport",Var("sslSupport"),NoPos),Bind("swig",If(OpOr(Var("pythonBindings"),Var("javaSwigBindings")),Var("swig"),Var("null")),NoPos)])),NoPos),NoPos),NoPos),NoPos),NoPos),NoPos),NoPos),NoPos) +Function(AttrsPat([Formal("localServer",DefaultValue(Var("false"))),Formal("httpServer",DefaultValue(Var("false"))),Formal("sslSupport",DefaultValue(Var("false"))),Formal("pythonBindings",DefaultValue(Var("false"))),Formal("javaSwigBindings",DefaultValue(Var("false"))),Formal("javahlBindings",DefaultValue(Var("false"))),Formal("stdenv",NoDefaultValue),Formal("fetchurl",NoDefaultValue),Formal("openssl",DefaultValue(Var("null"))),Formal("httpd",DefaultValue(Var("null"))),Formal("db4",DefaultValue(Var("null"))),Formal("expat",NoDefaultValue),Formal("swig",DefaultValue(Var("null"))),Formal("j2sdk",DefaultValue(Var("null")))],Bool(False),""),Assert(OpNEq(Var("expat"),Var("null")),Assert(OpImpl(Var("localServer"),OpNEq(Var("db4"),Var("null"))),Assert(OpImpl(Var("httpServer"),OpAnd(OpNEq(Var("httpd"),Var("null")),OpEq(Select(Var("httpd"),"expat"),Var("expat")))),Assert(OpImpl(Var("sslSupport"),OpAnd(OpNEq(Var("openssl"),Var("null")),OpImpl(Var("httpServer"),OpEq(Select(Var("httpd"),"openssl"),Var("openssl"))))),Assert(OpImpl(Var("pythonBindings"),OpAnd(OpNEq(Var("swig"),Var("null")),Select(Var("swig"),"pythonSupport"))),Assert(OpImpl(Var("javaSwigBindings"),OpAnd(OpNEq(Var("swig"),Var("null")),Select(Var("swig"),"javaSupport"))),Assert(OpImpl(Var("javahlBindings"),OpNEq(Var("j2sdk"),Var("null"))),Call(Select(Var("stdenv"),"mkDerivation"),Attrs([Bind("builder",Path("/foo/bar"),NoPos),Bind("db4",If(Var("localServer"),Var("db4"),Var("null")),NoPos),Bind("expat",Var("expat"),NoPos),Bind("httpServer",Var("httpServer"),NoPos),Bind("httpd",If(Var("httpServer"),Var("httpd"),Var("null")),NoPos),Bind("j2sdk",If(Var("javaSwigBindings"),Select(Var("swig"),"j2sdk"),If(Var("javahlBindings"),Var("j2sdk"),Var("null"))),NoPos),Bind("javaSwigBindings",Var("javaSwigBindings"),NoPos),Bind("javahlBindings",Var("javahlBindings"),NoPos),Bind("localServer",Var("localServer"),NoPos),Bind("name",Str("subversion-1.1.1",[]),NoPos),Bind("openssl",If(Var("sslSupport"),Var("openssl"),Var("null")),NoPos),Bind("patches",If(Var("javahlBindings"),List([Path("/javahl.patch")]),List([])),NoPos),Bind("python",If(Var("pythonBindings"),Select(Var("swig"),"python"),Var("null")),NoPos),Bind("pythonBindings",Var("pythonBindings"),NoPos),Bind("src",Call(Var("fetchurl"),Attrs([Bind("md5",Str("a180c3fe91680389c210c99def54d9e0",[]),NoPos),Bind("url",Str("http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2",[]),NoPos)])),NoPos),Bind("sslSupport",Var("sslSupport"),NoPos),Bind("swig",If(OpOr(Var("pythonBindings"),Var("javaSwigBindings")),Var("swig"),Var("null")),NoPos)])),NoPos),NoPos),NoPos),NoPos),NoPos),NoPos),NoPos),NoPos) From ef8bd919fc8e895df402502ffb1727bad0db07b2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 25 Mar 2010 12:45:23 +0000 Subject: [PATCH 009/103] * Implement `...' and default function arguments. --- src/libexpr/eval-test.cc | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 71b16384b..432d78874 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -215,20 +215,40 @@ static void eval(Env * env, Expr e, Value & v) eval(env, arg, *vArg); if (vArg->type != tAttrs) throw TypeError("expected attribute set"); + /* For each formal argument, get the actual argument. If + there is no matching actual argument but the formal + argument has a default, use the default. */ + unsigned int attrsUsed = 0; for (ATermIterator i(formals); i; ++i) { Expr name, def; DefaultValue def2; if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ Bindings::iterator j = vArg->attrs->find(aterm2String(name)); - if (j == vArg->attrs->end()) - throw TypeError(format("the argument named `%1%' required by the function is missing") - % aterm2String(name)); - + Value & v = env2->bindings[aterm2String(name)]; - v.type = tCopy; - v.val = &j->second; - } + + if (j == vArg->attrs->end()) { + if (!matchDefaultValue(def2, def)) def = 0; + if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") + % aterm2String(name)); + v.type = tThunk; + v.thunk.env = env2; + v.thunk.expr = def; + } else { + attrsUsed++; + v.type = tCopy; + v.val = &j->second; + } + + } + + /* Check that each actual argument is listed as a formal + argument (unless the attribute match specifies a + `...'). TODO: show the names of the + expected/unexpected arguments. */ + if (ellipsis == eFalse && attrsUsed != vArg->attrs->size()) + throw TypeError("function called with unexpected argument"); } else abort(); @@ -266,6 +286,10 @@ void run(Strings args) doTest("({x, y}: x) { x = 1; y = 2; }"); doTest("({x, y}@args: args.x) { x = 1; y = 2; }"); doTest("(args@{x, y}: args.x) { x = 1; y = 2; }"); + doTest("({x ? 1}: x) { }"); + doTest("({x ? 1, y ? x}: y) { x = 2; }"); + doTest("({x, y, ...}: x) { x = 1; y = 2; z = 3; }"); + doTest("({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); //Expr e = parseExprFromString(state, "let x = \"a\"; in x + \"b\"", "/"); //Expr e = parseExprFromString(state, "(x: x + \"b\") \"a\"", "/"); From f450384ded92cb68cf4b60b5bd9be64556aff339 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 25 Mar 2010 12:51:14 +0000 Subject: [PATCH 010/103] * Implement blackholing. --- src/libexpr/eval-test.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 432d78874..5b85065af 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -27,7 +27,8 @@ typedef enum { tAttrs, tThunk, tLambda, - tCopy + tCopy, + tBlackhole } ValueType; @@ -82,11 +83,16 @@ static void eval(Env * env, Expr e, Value & v); void forceValue(Value & v) { - if (v.type == tThunk) eval(v.thunk.env, v.thunk.expr, v); + if (v.type == tThunk) { + v.type = tBlackhole; + eval(v.thunk.env, v.thunk.expr, v); + } else if (v.type == tCopy) { forceValue(*v.val); v = *v.val; } + else if (v.type == tBlackhole) + throw EvalError("infinite recursion encountered"); } @@ -240,7 +246,6 @@ static void eval(Env * env, Expr e, Value & v) v.type = tCopy; v.val = &j->second; } - } /* Check that each actual argument is listed as a formal @@ -290,12 +295,8 @@ void run(Strings args) doTest("({x ? 1, y ? x}: y) { x = 2; }"); doTest("({x, y, ...}: x) { x = 1; y = 2; z = 3; }"); doTest("({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); + //doTest("({x ? y, y ? x}: y) { }"); - //Expr e = parseExprFromString(state, "let x = \"a\"; in x + \"b\"", "/"); - //Expr e = parseExprFromString(state, "(x: x + \"b\") \"a\"", "/"); - //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/"); - //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/"); - printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% environments") % nrEnvs); } From 3c9f8fc9b609b13986a1d59d08a7a79ad42d0bd5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 25 Mar 2010 13:10:04 +0000 Subject: [PATCH 011/103] * Don't convert variable names to strings. --- src/libexpr/eval-test.cc | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 5b85065af..d4ed3e7e9 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -12,7 +12,9 @@ using namespace nix; struct Env; struct Value; -typedef std::map Bindings; +typedef ATerm Sym; + +typedef std::map Bindings; struct Env @@ -96,7 +98,7 @@ void forceValue(Value & v) } -Value * lookupVar(Env * env, const string & name) +Value * lookupVar(Env * env, Sym name) { for ( ; env; env = env->up) { Bindings::iterator i = env->bindings.find(name); @@ -121,9 +123,9 @@ static void eval(Env * env, Expr e, Value & v) { printMsg(lvlError, format("eval: %1%") % e); - ATerm name; + Sym name; if (matchVar(e, name)) { - Value * v2 = lookupVar(env, aterm2String(name)); + Value * v2 = lookupVar(env, name); forceValue(*v2); v = *v2; return; @@ -143,7 +145,7 @@ static void eval(Env * env, Expr e, Value & v) ATerm e2, pos; for (ATermIterator i(es); i; ++i) { if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value & v2 = (*v.attrs)[aterm2String(name)]; + Value & v2 = (*v.attrs)[name]; nrValues++; v2.type = tThunk; v2.thunk.env = env; @@ -162,7 +164,7 @@ static void eval(Env * env, Expr e, Value & v) ATerm name, e2, pos; for (ATermIterator i(rbnds); i; ++i) { if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value & v2 = env2->bindings[aterm2String(name)]; + Value & v2 = env2->bindings[name]; nrValues++; v2.type = tThunk; v2.thunk.env = env2; @@ -176,7 +178,7 @@ static void eval(Env * env, Expr e, Value & v) if (matchSelect(e, e2, name)) { eval(env, e2, v); if (v.type != tAttrs) throw TypeError("expected attribute set"); - Bindings::iterator i = v.attrs->find(aterm2String(name)); + Bindings::iterator i = v.attrs->find(name); if (i == v.attrs->end()) throw TypeError("attribute not found"); forceValue(i->second); v = i->second; @@ -203,7 +205,7 @@ static void eval(Env * env, Expr e, Value & v) ATermList formals; ATerm ellipsis; if (matchVarPat(v.lambda.pat, name)) { - Value & vArg = env2->bindings[aterm2String(name)]; + Value & vArg = env2->bindings[name]; vArg.type = tThunk; vArg.thunk.env = env; vArg.thunk.expr = arg; @@ -216,7 +218,7 @@ static void eval(Env * env, Expr e, Value & v) if (name == sNoAlias) vArg = &vArg_; else - vArg = &env2->bindings[aterm2String(name)]; + vArg = &env2->bindings[name]; eval(env, arg, *vArg); if (vArg->type != tAttrs) throw TypeError("expected attribute set"); @@ -226,13 +228,13 @@ static void eval(Env * env, Expr e, Value & v) argument has a default, use the default. */ unsigned int attrsUsed = 0; for (ATermIterator i(formals); i; ++i) { - Expr name, def; + Expr def; Sym name; DefaultValue def2; if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ - Bindings::iterator j = vArg->attrs->find(aterm2String(name)); + Bindings::iterator j = vArg->attrs->find(name); - Value & v = env2->bindings[aterm2String(name)]; + Value & v = env2->bindings[name]; if (j == vArg->attrs->end()) { if (!matchDefaultValue(def2, def)) def = 0; @@ -296,6 +298,7 @@ void run(Strings args) doTest("({x, y, ...}: x) { x = 1; y = 2; z = 3; }"); doTest("({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); //doTest("({x ? y, y ? x}: y) { }"); + doTest("let x = 1; in x"); printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% environments") % nrEnvs); From 25eedf085d9b3a1ad7d0e6969b050a2f73234ae3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 25 Mar 2010 14:51:04 +0000 Subject: [PATCH 012/103] * Quick and dirty implementation of `with'. `with e1; e2' is basically desugared to `let = e1; e2', and `lookupVar' looks in each in the environment chain for an attribute with the specified name. --- src/libexpr/eval-test.cc | 61 +++++++++++++++++++++++++++++++++---- src/libexpr/nixexpr-ast.def | 1 + 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index d4ed3e7e9..8f02d8cc5 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -83,7 +83,7 @@ std::ostream & operator << (std::ostream & str, Value & v) static void eval(Env * env, Expr e, Value & v); -void forceValue(Value & v) +static void forceValue(Value & v) { if (v.type == tThunk) { v.type = tBlackhole; @@ -98,12 +98,45 @@ void forceValue(Value & v) } -Value * lookupVar(Env * env, Sym name) +static Value * lookupWith(Env * env, Sym name) { - for ( ; env; env = env->up) { - Bindings::iterator i = env->bindings.find(name); - if (i != env->bindings.end()) return &i->second; + if (!env) return 0; + Value * v = lookupWith(env->up, name); + if (v) return v; + Bindings::iterator i = env->bindings.find(sWith); + if (i == env->bindings.end()) return 0; + Bindings::iterator j = i->second.attrs->find(name); + if (j != i->second.attrs->end()) return &j->second; + return 0; +} + + +static Value * lookupVar(Env * env, Sym name) +{ + /* First look for a regular variable binding for `name'. */ + for (Env * env2 = env; env2; env2 = env2->up) { + Bindings::iterator i = env2->bindings.find(name); + if (i != env2->bindings.end()) return &i->second; } + + /* Otherwise, look for a `with' attribute set containing `name'. + Outer `withs' take precedence (i.e. `with {x=1;}; with {x=2;}; + x' evaluates to 1). */ + Value * v = lookupWith(env, name); + if (v) return v; + + /* Alternative implementation where the inner `withs' take + precedence (i.e. `with {x=1;}; with {x=2;}; x' evaluates to + 2). */ +#if 0 + for (Env * env2 = env; env2; env2 = env2->up) { + Bindings::iterator i = env2->bindings.find(sWith); + if (i == env2->bindings.end()) continue; + Bindings::iterator j = i->second.attrs->find(name); + if (j != i->second.attrs->end()) return &j->second; + } +#endif + throw Error("undefined variable"); } @@ -112,7 +145,7 @@ unsigned long nrValues = 0; unsigned long nrEnvs = 0; -Env * allocEnv() +static Env * allocEnv() { nrEnvs++; return new Env; @@ -264,6 +297,19 @@ static void eval(Env * env, Expr e, Value & v) return; } + Expr attrs; + if (matchWith(e, attrs, body, pos)) { + Env * env2 = allocEnv(); + env2->up = env; + + Value & vAttrs = env2->bindings[sWith]; + eval(env, attrs, vAttrs); + if (vAttrs.type != tAttrs) throw TypeError("`with' should evaluate to an attribute set"); + + eval(env2, body, v); + return; + } + abort(); } @@ -299,6 +345,9 @@ void run(Strings args) doTest("({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); //doTest("({x ? y, y ? x}: y) { }"); doTest("let x = 1; in x"); + doTest("with { x = 1; }; x"); + doTest("let x = 2; in with { x = 1; }; x"); // => 2 + doTest("with { x = 1; }; with { x = 2; }; x"); // => 1 printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% environments") % nrEnvs); diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def index 2bf8492dd..b34466543 100644 --- a/src/libexpr/nixexpr-ast.def +++ b/src/libexpr/nixexpr-ast.def @@ -95,3 +95,4 @@ eTrue = makeBool(makeTrue()) eFalse = makeBool(makeFalse()) sOverrides = toATerm("__overrides") sNoAlias = toATerm("") +sWith = toATerm("") From c2ba4313fb7f2e257f6205eb3fa366376c3010b0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 25 Mar 2010 15:38:37 +0000 Subject: [PATCH 013/103] * Implemented lists. --- src/libexpr/eval-test.cc | 70 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 8f02d8cc5..c40912c13 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -27,6 +27,7 @@ struct Env typedef enum { tInt = 1, tAttrs, + tList, tThunk, tLambda, tCopy, @@ -41,6 +42,10 @@ struct Value { int integer; Bindings * attrs; + struct { + unsigned int length; + Value * elems; + } list; struct { Env * env; Expr expr; @@ -64,9 +69,15 @@ std::ostream & operator << (std::ostream & str, Value & v) case tAttrs: str << "{ "; foreach (Bindings::iterator, i, *v.attrs) - str << i->first << " = " << i->second << "; "; + str << aterm2String(i->first) << " = " << i->second << "; "; str << "}"; break; + case tList: + str << "[ "; + for (unsigned int n = 0; n < v.list.length; ++n) + str << v.list.elems[n] << " "; + str << "]"; + break; case tThunk: str << ""; break; @@ -141,9 +152,7 @@ static Value * lookupVar(Env * env, Sym name) } -unsigned long nrValues = 0; - -unsigned long nrEnvs = 0; +unsigned long nrValues = 0, nrEnvs = 0; static Env * allocEnv() { @@ -207,7 +216,7 @@ static void eval(Env * env, Expr e, Value & v) return; } - Expr e2; + Expr e1, e2; if (matchSelect(e, e2, name)) { eval(env, e2, v); if (v.type != tAttrs) throw TypeError("expected attribute set"); @@ -310,7 +319,52 @@ static void eval(Env * env, Expr e, Value & v) return; } - abort(); + if (matchList(e, es)) { + v.type = tList; + v.list.length = ATgetLength(es); + v.list.elems = new Value[v.list.length]; // !!! check destructor + for (unsigned int n = 0; n < v.list.length; ++n, es = ATgetNext(es)) { + v.list.elems[n].type = tThunk; + v.list.elems[n].thunk.env = env; + v.list.elems[n].thunk.expr = ATgetFirst(es); + } + return; + } + + if (matchOpConcat(e, e1, e2)) { + Value v1; eval(env, e1, v1); + if (v1.type != tList) throw TypeError("list expecteed"); + Value v2; eval(env, e2, v2); + if (v2.type != tList) throw TypeError("list expecteed"); + v.type = tList; + v.list.length = v1.list.length + v2.list.length; + v.list.elems = new Value[v.list.length]; + /* !!! This loses sharing with the original lists. We could + use a tCopy node, but that would use more memory. */ + for (unsigned int n = 0; n < v1.list.length; ++n) + 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; + } + + throw Error("unsupported term"); +} + + +static void strictEval(Env * env, Expr e, Value & v) +{ + eval(env, e, v); + + if (v.type == tAttrs) { + foreach (Bindings::iterator, i, *v.attrs) + forceValue(i->second); + } + + else if (v.type == tList) { + for (unsigned int n = 0; n < v.list.length; ++n) + forceValue(v.list.elems[n]); + } } @@ -320,7 +374,7 @@ void doTest(string s) Expr e = parseExprFromString(state, s, "/"); printMsg(lvlError, format(">>>>> %1%") % e); Value v; - eval(0, e, v); + strictEval(0, e, v); printMsg(lvlError, format("result: %1%") % v); } @@ -348,6 +402,8 @@ void run(Strings args) doTest("with { x = 1; }; x"); doTest("let x = 2; in with { x = 1; }; x"); // => 2 doTest("with { x = 1; }; with { x = 2; }; x"); // => 1 + doTest("[ 1 2 3 ]"); + doTest("[ 1 2 ] ++ [ 3 4 5 ]"); printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% environments") % nrEnvs); From 8da118e4d03a9ecbd2116eadabd992cfef0479c5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 25 Mar 2010 16:35:24 +0000 Subject: [PATCH 014/103] * Measure stack usage. --- src/libexpr/eval-test.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index c40912c13..649c5e020 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -161,8 +161,14 @@ static Env * allocEnv() } +char * p1 = 0, * p2 = 0; + + static void eval(Env * env, Expr e, Value & v) { + char c; + if (!p1) p1 = &c; else if (!p2) p2 = &c; + printMsg(lvlError, format("eval: %1%") % e); Sym name; @@ -333,9 +339,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 expecteed"); + if (v1.type != tList) throw TypeError("list expected"); Value v2; eval(env, e2, v2); - if (v2.type != tList) throw TypeError("list expecteed"); + if (v2.type != tList) throw TypeError("list expected"); v.type = tList; v.list.length = v1.list.length + v2.list.length; v.list.elems = new Value[v.list.length]; @@ -347,7 +353,7 @@ static void eval(Env * env, Expr e, Value & v) v.list.elems[n + v1.list.length] = v2.list.elems[n]; return; } - + throw Error("unsupported term"); } @@ -370,6 +376,7 @@ static void strictEval(Env * env, Expr e, Value & v) void doTest(string s) { + p1 = p2 = 0; EvalState state; Expr e = parseExprFromString(state, s, "/"); printMsg(lvlError, format(">>>>> %1%") % e); @@ -407,6 +414,7 @@ void run(Strings args) printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% environments") % nrEnvs); + printMsg(lvlError, format("each eval() uses %1% bytes of stack space") % (p1 - p2)); } From cad8726b2c7fcefae6b629320283b0f1ee4072a4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Mar 2010 13:27:26 +0000 Subject: [PATCH 015/103] * Implemented the `==' and `!=' operators. These now use a deep equality test, so they also work for (finite) attribute sets and lists. --- src/libexpr/eval-test.cc | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 649c5e020..631d52a82 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -26,6 +26,7 @@ struct Env typedef enum { tInt = 1, + tBool, tAttrs, tList, tThunk, @@ -41,6 +42,7 @@ struct Value union { int integer; + bool boolean; Bindings * attrs; struct { unsigned int length; @@ -66,6 +68,9 @@ std::ostream & operator << (std::ostream & str, Value & v) case tInt: str << v.integer; break; + case tBool: + str << (v.boolean ? "true" : "false"); + break; case tAttrs: str << "{ "; foreach (Bindings::iterator, i, *v.attrs) @@ -152,6 +157,45 @@ static Value * lookupVar(Env * env, Sym name) } +static void setBoolValue(Value & v, bool b) +{ + v.type = tBool; + v.boolean = b; +} + + +static bool eqValues(Value & v1, Value & v2) +{ + forceValue(v1); + forceValue(v2); + switch (v1.type) { + + case tInt: + return v2.type == tInt && v1.integer == v2.integer; + + case tBool: + return v2.type == tBool && v1.boolean == v2.boolean; + + case tList: + if (v2.type != tList || v1.list.length != v2.list.length) return false; + for (unsigned int n = 0; n < v1.list.length; ++n) + if (!eqValues(v1.list.elems[n], v2.list.elems[n])) return false; + return true; + + case tAttrs: { + if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false; + Bindings::iterator i, j; + for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) + if (!eqValues(i->second, j->second)) return false; + return true; + } + + default: + throw Error("cannot compare given values"); + } +} + + unsigned long nrValues = 0, nrEnvs = 0; static Env * allocEnv() @@ -337,6 +381,20 @@ static void eval(Env * env, Expr e, Value & v) return; } + if (matchOpEq(e, e1, e2)) { + Value v1; eval(env, e1, v1); + Value v2; eval(env, e2, v2); + setBoolValue(v, eqValues(v1, v2)); + return; + } + + if (matchOpNEq(e, e1, e2)) { + Value v1; eval(env, e1, v1); + Value v2; eval(env, e2, v2); + setBoolValue(v, !eqValues(v1, v2)); + return; + } + if (matchOpConcat(e, e1, e2)) { Value v1; eval(env, e1, v1); if (v1.type != tList) throw TypeError("list expected"); @@ -411,6 +469,15 @@ void run(Strings args) doTest("with { x = 1; }; with { x = 2; }; x"); // => 1 doTest("[ 1 2 3 ]"); doTest("[ 1 2 ] ++ [ 3 4 5 ]"); + doTest("123 == 123"); + doTest("123 == 456"); + doTest("let id = x: x; in [1 2] == [(id 1) (id 2)]"); + doTest("let id = x: x; in [1 2] == [(id 1) (id 3)]"); + doTest("[1 2] == [3 (let x = x; in x)]"); + doTest("{ x = 1; y.z = 2; } == { y = { z = 2; }; x = 1; }"); + doTest("{ x = 1; y = 2; } == { x = 2; }"); + doTest("{ x = [ 1 2 ]; } == { x = [ 1 ] ++ [ 2 ]; }"); + doTest("1 != 1"); printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% environments") % nrEnvs); From 45d822f29c84644d1b795bd36999e97f30cfb8ba Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Mar 2010 15:45:53 +0000 Subject: [PATCH 016/103] * Primops (not yet finished). --- src/libexpr/eval-test.cc | 130 ++++++++++++++++++++++++++++++++++----- 1 file changed, 114 insertions(+), 16 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 631d52a82..90e918e00 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -32,10 +32,15 @@ typedef enum { tThunk, tLambda, tCopy, - tBlackhole + tBlackhole, + tPrimOp, + tPrimOpApp, } ValueType; +typedef void (* PrimOp_) (Value * * args, Value & v); + + struct Value { ValueType type; @@ -58,6 +63,14 @@ struct Value Expr body; } lambda; Value * val; + struct { + PrimOp_ fun; + unsigned int arity; + } primOp; + struct { + Value * left, * right; + unsigned int argsLeft; + } primOpApp; }; }; @@ -89,6 +102,12 @@ std::ostream & operator << (std::ostream & str, Value & v) case tLambda: str << ""; break; + case tPrimOp: + str << ""; + break; + case tPrimOpApp: + str << ""; + break; default: abort(); } @@ -96,14 +115,14 @@ std::ostream & operator << (std::ostream & str, Value & v) } -static void eval(Env * env, Expr e, Value & v); +static void eval(Env & env, Expr e, Value & v); static void forceValue(Value & v) { if (v.type == tThunk) { v.type = tBlackhole; - eval(v.thunk.env, v.thunk.expr, v); + eval(*v.thunk.env, v.thunk.expr, v); } else if (v.type == tCopy) { forceValue(*v.val); @@ -208,7 +227,7 @@ static Env * allocEnv() char * p1 = 0, * p2 = 0; -static void eval(Env * env, Expr e, Value & v) +static void eval(Env & env, Expr e, Value & v) { char c; if (!p1) p1 = &c; else if (!p2) p2 = &c; @@ -217,7 +236,7 @@ static void eval(Env * env, Expr e, Value & v) Sym name; if (matchVar(e, name)) { - Value * v2 = lookupVar(env, name); + Value * v2 = lookupVar(&env, name); forceValue(*v2); v = *v2; return; @@ -240,7 +259,7 @@ static void eval(Env * env, Expr e, Value & v) Value & v2 = (*v.attrs)[name]; nrValues++; v2.type = tThunk; - v2.thunk.env = env; + v2.thunk.env = &env; v2.thunk.expr = e2; } return; @@ -249,7 +268,7 @@ static void eval(Env * env, Expr e, Value & v) ATermList rbnds, nrbnds; if (matchRec(e, rbnds, nrbnds)) { Env * env2 = allocEnv(); - env2->up = env; + env2->up = &env; v.type = tAttrs; v.attrs = &env2->bindings; @@ -280,7 +299,7 @@ static void eval(Env * env, Expr e, Value & v) Pattern pat; Expr body; Pos pos; if (matchFunction(e, pat, body, pos)) { v.type = tLambda; - v.lambda.env = env; + v.lambda.env = &env; v.lambda.pat = pat; v.lambda.body = body; return; @@ -289,17 +308,47 @@ static void eval(Env * env, Expr e, Value & v) Expr fun, arg; if (matchCall(e, fun, arg)) { eval(env, fun, v); + + if (v.type == tPrimOp || v.type == tPrimOpApp) { + if ((v.type == tPrimOp && v.primOp.arity == 1) || + (v.type == tPrimOpApp && v.primOpApp.argsLeft == 1)) + { + /* We have all the arguments, so call the primop. + First find the primop. */ + Value * primOp = &v; + while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left; + assert(primOp->type == tPrimOp); + unsigned int arity = primOp->primOp.arity; + + Value vLastArg; + vLastArg.type = tThunk; + vLastArg.thunk.env = &env; + vLastArg.thunk.expr = arg; + + Value * vArgs[arity]; + unsigned int n = arity - 1; + vArgs[n--] = &vLastArg; + for (Value * arg = &v; arg->type == tPrimOpApp; arg = arg->primOpApp.left) + vArgs[n--] = arg->primOpApp.right; + + primOp->primOp.fun(vArgs, v); + } else { + throw Error("bar"); + } + return; + } + if (v.type != tLambda) throw TypeError("expected function"); Env * env2 = allocEnv(); - env2->up = env; + env2->up = &env; ATermList formals; ATerm ellipsis; if (matchVarPat(v.lambda.pat, name)) { Value & vArg = env2->bindings[name]; vArg.type = tThunk; - vArg.thunk.env = env; + vArg.thunk.env = &env; vArg.thunk.expr = arg; } @@ -352,20 +401,20 @@ static void eval(Env * env, Expr e, Value & v) else abort(); - eval(env2, v.lambda.body, v); + eval(*env2, v.lambda.body, v); return; } Expr attrs; if (matchWith(e, attrs, body, pos)) { Env * env2 = allocEnv(); - env2->up = env; + env2->up = &env; Value & vAttrs = env2->bindings[sWith]; eval(env, attrs, vAttrs); if (vAttrs.type != tAttrs) throw TypeError("`with' should evaluate to an attribute set"); - eval(env2, body, v); + eval(*env2, body, v); return; } @@ -375,7 +424,7 @@ static void eval(Env * env, Expr e, Value & v) v.list.elems = new Value[v.list.length]; // !!! check destructor for (unsigned int n = 0; n < v.list.length; ++n, es = ATgetNext(es)) { v.list.elems[n].type = tThunk; - v.list.elems[n].thunk.env = env; + v.list.elems[n].thunk.env = &env; v.list.elems[n].thunk.expr = ATgetFirst(es); } return; @@ -416,7 +465,7 @@ static void eval(Env * env, Expr e, Value & v) } -static void strictEval(Env * env, Expr e, Value & v) +static void strictEval(Env & env, Expr e, Value & v) { eval(env, e, v); @@ -432,14 +481,59 @@ 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"); + if (args[0]->list.length == 0) + throw Error("`head' called on an empty list"); + forceValue(args[0]->list.elems[0]); + v = args[0]->list.elems[0]; +} + + +static void prim_add(Value * * args, Value & v) +{ + throw Error("foo"); +} + + +static void addPrimOp(Env & env, const string & name, unsigned int arity, PrimOp_ fun) +{ + Value & v = env.bindings[toATerm(name)]; + v.type = tPrimOp; + v.primOp.arity = arity; + v.primOp.fun = fun; +} + + void doTest(string s) { + Env baseEnv; + baseEnv.up = 0; + + /* Add global constants such as `true' to the base environment. */ + { + Value & v = baseEnv.bindings[toATerm("true")]; + v.type = tBool; + v.boolean = true; + } + { + Value & v = baseEnv.bindings[toATerm("false")]; + v.type = tBool; + v.boolean = false; + } + + /* Add primops to the base environment. */ + addPrimOp(baseEnv, "__head", 1, prim_head); + addPrimOp(baseEnv, "__add", 2, prim_add); + p1 = p2 = 0; EvalState state; Expr e = parseExprFromString(state, s, "/"); printMsg(lvlError, format(">>>>> %1%") % e); Value v; - strictEval(0, e, v); + strictEval(baseEnv, e, v); printMsg(lvlError, format("result: %1%") % v); } @@ -478,6 +572,10 @@ void run(Strings args) doTest("{ x = 1; y = 2; } == { x = 2; }"); doTest("{ x = [ 1 2 ]; } == { x = [ 1 ] ++ [ 2 ]; }"); doTest("1 != 1"); + doTest("true"); + doTest("true == false"); + doTest("__head [ 1 2 3 ]"); + doTest("__add 1 2"); printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% environments") % nrEnvs); From 3d2b835f30bf97f2e9ac8a76c4e19be384803219 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 28 Mar 2010 16:37:39 +0000 Subject: [PATCH 017/103] * Implemented multi-argument primops. --- src/libexpr/eval-test.cc | 45 ++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 90e918e00..e45d682a8 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -217,6 +217,12 @@ static bool eqValues(Value & v1, Value & v2) unsigned long nrValues = 0, nrEnvs = 0; +static Value * allocValues(unsigned int count) +{ + nrValues += count; + return new Value[count];// !!! check destructor +} + static Env * allocEnv() { nrEnvs++; @@ -310,9 +316,9 @@ static void eval(Env & env, Expr e, Value & v) eval(env, fun, v); if (v.type == tPrimOp || v.type == tPrimOpApp) { - if ((v.type == tPrimOp && v.primOp.arity == 1) || - (v.type == tPrimOpApp && v.primOpApp.argsLeft == 1)) - { + unsigned int argsLeft = + v.type == tPrimOp ? v.primOp.arity : v.primOpApp.argsLeft; + if (argsLeft == 1) { /* We have all the arguments, so call the primop. First find the primop. */ Value * primOp = &v; @@ -325,15 +331,25 @@ static void eval(Env & env, Expr e, Value & v) vLastArg.thunk.env = &env; vLastArg.thunk.expr = arg; + /* Put all the arguments in an array. */ Value * vArgs[arity]; unsigned int n = arity - 1; vArgs[n--] = &vLastArg; for (Value * arg = &v; arg->type == tPrimOpApp; arg = arg->primOpApp.left) vArgs[n--] = arg->primOpApp.right; - + + /* And call the primop. */ primOp->primOp.fun(vArgs, v); } else { - throw Error("bar"); + Value * v2 = allocValues(2); + v2[0] = v; + v2[1].type = tThunk; + v2[1].thunk.env = &env; + v2[1].thunk.expr = arg; + v.type = tPrimOpApp; + v.primOpApp.left = &v2[0]; + v.primOpApp.right = &v2[1]; + v.primOpApp.argsLeft = argsLeft - 1; } return; } @@ -347,6 +363,7 @@ static void eval(Env & env, Expr e, Value & v) if (matchVarPat(v.lambda.pat, name)) { Value & vArg = env2->bindings[name]; + nrValues++; vArg.type = tThunk; vArg.thunk.env = &env; vArg.thunk.expr = arg; @@ -358,8 +375,10 @@ static void eval(Env & env, Expr e, Value & v) if (name == sNoAlias) vArg = &vArg_; - else + else { vArg = &env2->bindings[name]; + nrValues++; + } eval(env, arg, *vArg); if (vArg->type != tAttrs) throw TypeError("expected attribute set"); @@ -376,6 +395,7 @@ static void eval(Env & env, Expr e, Value & v) Bindings::iterator j = vArg->attrs->find(name); Value & v = env2->bindings[name]; + nrValues++; if (j == vArg->attrs->end()) { if (!matchDefaultValue(def2, def)) def = 0; @@ -411,6 +431,7 @@ static void eval(Env & env, Expr e, Value & v) env2->up = &env; Value & vAttrs = env2->bindings[sWith]; + nrValues++; eval(env, attrs, vAttrs); if (vAttrs.type != tAttrs) throw TypeError("`with' should evaluate to an attribute set"); @@ -421,7 +442,7 @@ static void eval(Env & env, Expr e, Value & v) if (matchList(e, es)) { v.type = tList; v.list.length = ATgetLength(es); - v.list.elems = new Value[v.list.length]; // !!! check destructor + v.list.elems = allocValues(v.list.length); for (unsigned int n = 0; n < v.list.length; ++n, es = ATgetNext(es)) { v.list.elems[n].type = tThunk; v.list.elems[n].thunk.env = &env; @@ -451,7 +472,7 @@ static void eval(Env & env, Expr e, Value & v) if (v2.type != tList) throw TypeError("list expected"); v.type = tList; v.list.length = v1.list.length + v2.list.length; - v.list.elems = new Value[v.list.length]; + v.list.elems = allocValues(v.list.length); /* !!! This loses sharing with the original lists. We could use a tCopy node, but that would use more memory. */ for (unsigned int n = 0; n < v1.list.length; ++n) @@ -494,13 +515,19 @@ static void prim_head(Value * * args, Value & v) static void prim_add(Value * * args, Value & v) { - throw Error("foo"); + forceValue(*args[0]); + if (args[0]->type != tInt) throw TypeError("integer expected"); + forceValue(*args[1]); + if (args[1]->type != tInt) throw TypeError("integer expected"); + v.type = tInt; + v.integer = args[0]->integer + args[1]->integer; } static void addPrimOp(Env & env, const string & name, unsigned int arity, PrimOp_ fun) { Value & v = env.bindings[toATerm(name)]; + nrValues++; v.type = tPrimOp; v.primOp.arity = arity; v.primOp.fun = fun; From d96cdcea6b910b9ca169bea0b0b71e65532e70cb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 28 Mar 2010 16:57:16 +0000 Subject: [PATCH 018/103] --- src/libexpr/eval-test.cc | 104 +++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index e45d682a8..aafb07d3c 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -75,6 +75,28 @@ struct Value }; +static void mkThunk(Value & v, Env & env, Expr expr) +{ + v.type = tThunk; + v.thunk.env = &env; + v.thunk.expr = expr; +} + + +static void mkInt(Value & v, int n) +{ + v.type = tInt; + v.integer = n; +} + + +static void mkBool(Value & v, bool b) +{ + v.type = tBool; + v.boolean = b; +} + + std::ostream & operator << (std::ostream & str, Value & v) { switch (v.type) { @@ -176,13 +198,6 @@ static Value * lookupVar(Env * env, Sym name) } -static void setBoolValue(Value & v, bool b) -{ - v.type = tBool; - v.boolean = b; -} - - static bool eqValues(Value & v1, Value & v2) { forceValue(v1); @@ -220,13 +235,13 @@ unsigned long nrValues = 0, nrEnvs = 0; static Value * allocValues(unsigned int count) { nrValues += count; - return new Value[count];// !!! check destructor + return new Value[count]; // !!! check destructor } -static Env * allocEnv() +static Env & allocEnv() { nrEnvs++; - return new Env; + return *(new Env); } @@ -250,8 +265,7 @@ static void eval(Env & env, Expr e, Value & v) int n; if (matchInt(e, n)) { - v.type = tInt; - v.integer = n; + mkInt(v, n); return; } @@ -264,28 +278,24 @@ static void eval(Env & env, Expr e, Value & v) if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ Value & v2 = (*v.attrs)[name]; nrValues++; - v2.type = tThunk; - v2.thunk.env = &env; - v2.thunk.expr = e2; + mkThunk(v2, env, e2); } return; } ATermList rbnds, nrbnds; if (matchRec(e, rbnds, nrbnds)) { - Env * env2 = allocEnv(); - env2->up = &env; + Env & env2(allocEnv()); + env2.up = &env; v.type = tAttrs; - v.attrs = &env2->bindings; + v.attrs = &env2.bindings; ATerm name, e2, pos; for (ATermIterator i(rbnds); i; ++i) { if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value & v2 = env2->bindings[name]; + Value & v2 = env2.bindings[name]; nrValues++; - v2.type = tThunk; - v2.thunk.env = env2; - v2.thunk.expr = e2; + mkThunk(v2, env2, e2); } return; @@ -327,9 +337,7 @@ static void eval(Env & env, Expr e, Value & v) unsigned int arity = primOp->primOp.arity; Value vLastArg; - vLastArg.type = tThunk; - vLastArg.thunk.env = &env; - vLastArg.thunk.expr = arg; + mkThunk(vLastArg, env, arg); /* Put all the arguments in an array. */ Value * vArgs[arity]; @@ -343,9 +351,7 @@ static void eval(Env & env, Expr e, Value & v) } else { Value * v2 = allocValues(2); v2[0] = v; - v2[1].type = tThunk; - v2[1].thunk.env = &env; - v2[1].thunk.expr = arg; + mkThunk(v2[1], env, arg); v.type = tPrimOpApp; v.primOpApp.left = &v2[0]; v.primOpApp.right = &v2[1]; @@ -356,17 +362,15 @@ static void eval(Env & env, Expr e, Value & v) if (v.type != tLambda) throw TypeError("expected function"); - Env * env2 = allocEnv(); - env2->up = &env; + Env & env2(allocEnv()); + env2.up = &env; ATermList formals; ATerm ellipsis; if (matchVarPat(v.lambda.pat, name)) { - Value & vArg = env2->bindings[name]; + Value & vArg = env2.bindings[name]; nrValues++; - vArg.type = tThunk; - vArg.thunk.env = &env; - vArg.thunk.expr = arg; + mkThunk(vArg, env, arg); } else if (matchAttrsPat(v.lambda.pat, formals, ellipsis, name)) { @@ -376,7 +380,7 @@ static void eval(Env & env, Expr e, Value & v) if (name == sNoAlias) vArg = &vArg_; else { - vArg = &env2->bindings[name]; + vArg = &env2.bindings[name]; nrValues++; } @@ -394,16 +398,14 @@ static void eval(Env & env, Expr e, Value & v) Bindings::iterator j = vArg->attrs->find(name); - Value & v = env2->bindings[name]; + Value & v = env2.bindings[name]; nrValues++; if (j == vArg->attrs->end()) { if (!matchDefaultValue(def2, def)) def = 0; if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") % aterm2String(name)); - v.type = tThunk; - v.thunk.env = env2; - v.thunk.expr = def; + mkThunk(v, env2, def); } else { attrsUsed++; v.type = tCopy; @@ -421,21 +423,21 @@ static void eval(Env & env, Expr e, Value & v) else abort(); - eval(*env2, v.lambda.body, v); + eval(env2, v.lambda.body, v); return; } Expr attrs; if (matchWith(e, attrs, body, pos)) { - Env * env2 = allocEnv(); - env2->up = &env; + Env & env2(allocEnv()); + env2.up = &env; - Value & vAttrs = env2->bindings[sWith]; + Value & vAttrs = env2.bindings[sWith]; nrValues++; eval(env, attrs, vAttrs); if (vAttrs.type != tAttrs) throw TypeError("`with' should evaluate to an attribute set"); - eval(*env2, body, v); + eval(env2, body, v); return; } @@ -443,25 +445,22 @@ static void eval(Env & env, Expr e, Value & v) v.type = tList; v.list.length = ATgetLength(es); v.list.elems = allocValues(v.list.length); - for (unsigned int n = 0; n < v.list.length; ++n, es = ATgetNext(es)) { - v.list.elems[n].type = tThunk; - v.list.elems[n].thunk.env = &env; - v.list.elems[n].thunk.expr = ATgetFirst(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)) { Value v1; eval(env, e1, v1); Value v2; eval(env, e2, v2); - setBoolValue(v, eqValues(v1, v2)); + mkBool(v, eqValues(v1, v2)); return; } if (matchOpNEq(e, e1, e2)) { Value v1; eval(env, e1, v1); Value v2; eval(env, e2, v2); - setBoolValue(v, !eqValues(v1, v2)); + mkBool(v, !eqValues(v1, v2)); return; } @@ -519,8 +518,7 @@ static void prim_add(Value * * args, Value & v) if (args[0]->type != tInt) throw TypeError("integer expected"); forceValue(*args[1]); if (args[1]->type != tInt) throw TypeError("integer expected"); - v.type = tInt; - v.integer = args[0]->integer + args[1]->integer; + mkInt(v, args[0]->integer + args[1]->integer); } From 392811eb8fb8125a6ae9661d5a0fb2a8ced31a94 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 28 Mar 2010 18:27:07 +0000 Subject: [PATCH 019/103] * Strings. --- src/libexpr/eval-test.cc | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index aafb07d3c..db5401138 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -5,6 +5,7 @@ #include "nixexpr-ast.hh" #include +#include using namespace nix; @@ -27,6 +28,7 @@ struct Env typedef enum { tInt = 1, tBool, + tString, tAttrs, tList, tThunk, @@ -48,6 +50,10 @@ struct Value { int integer; bool boolean; + struct { + const char * s; + const char * * context; + } string; Bindings * attrs; struct { unsigned int length; @@ -97,6 +103,14 @@ static void mkBool(Value & v, bool b) } +static void mkString(Value & v, const char * s) +{ + v.type = tString; + v.string.s = s; + v.string.context = 0; +} + + std::ostream & operator << (std::ostream & str, Value & v) { switch (v.type) { @@ -106,6 +120,9 @@ std::ostream & operator << (std::ostream & str, Value & v) case tBool: str << (v.boolean ? "true" : "false"); break; + case tString: + str << "\"" << v.string.s << "\""; // !!! escaping + break; case tAttrs: str << "{ "; foreach (Bindings::iterator, i, *v.attrs) @@ -269,6 +286,13 @@ static void eval(Env & env, Expr e, Value & v) return; } + ATerm s; ATermList context; + if (matchStr(e, s, context)) { + assert(context == ATempty); + mkString(v, ATgetName(ATgetAFun(s))); + return; + } + ATermList es; if (matchAttrs(e, es)) { v.type = tAttrs; @@ -481,6 +505,25 @@ static void eval(Env & env, Expr e, Value & v) return; } + if (matchConcatStrings(e, es)) { + unsigned int n = ATgetLength(es), j = 0; + Value vs[n]; + unsigned int len = 0; + for (ATermIterator i(es); i; ++i, ++j) { + eval(env, *i, vs[j]); + if (vs[j].type != tString) throw TypeError("string expected"); + len += strlen(vs[j].string.s); + } + char * s = new char[len + 1], * t = s; + for (unsigned int i = 0; i < j; ++i) { + strcpy(t, vs[i].string.s); + t += strlen(vs[i].string.s); + } + *t = 0; + mkString(v, s); + return; + } + throw Error("unsupported term"); } @@ -601,6 +644,8 @@ void run(Strings args) doTest("true == false"); doTest("__head [ 1 2 3 ]"); doTest("__add 1 2"); + doTest("\"foo\""); + doTest("let s = \"bar\"; in \"foo${s}\""); printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% environments") % nrEnvs); From 807a67bc7430f13e23f28d70780da0e9090e5cf2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Mar 2010 09:43:39 +0000 Subject: [PATCH 020/103] --- src/libexpr/eval-test.cc | 67 +++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index db5401138..6f686bd6b 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)); } From e3f32ac5af69641d12ad07a52f3ab495f61938f1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Mar 2010 09:43:55 +0000 Subject: [PATCH 021/103] --- src/libexpr/eval-test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 6f686bd6b..983cac951 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -686,6 +686,7 @@ void run(Strings args) doTest("true == false"); doTest("__head [ 1 2 3 ]"); doTest("__add 1 2"); + doTest("null"); doTest("\"foo\""); doTest("let s = \"bar\"; in \"foo${s}\""); From 52090d24185f5092bfd5f8f1fdf0d0890e19a09d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Mar 2010 10:13:51 +0000 Subject: [PATCH 022/103] --- src/libexpr/eval-test.cc | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 983cac951..543a9cf85 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -125,6 +125,9 @@ std::ostream & operator << (std::ostream & str, Value & v) case tString: str << "\"" << v.string.s << "\""; // !!! escaping break; + case tNull: + str << "true"; + break; case tAttrs: str << "{ "; foreach (Bindings::iterator, i, *v.attrs) @@ -308,6 +311,16 @@ static Env & allocEnv() char * p1 = 0, * p2 = 0; +static bool evalBool(Env & env, Expr e) +{ + Value v; + eval(env, e, v); + if (v.type != tBool) + throw TypeError(format("value is %1% while a Boolean was expected") % showType(v)); + return v.boolean; +} + + static void eval(Env & env, Expr e, Value & v) { char c; @@ -569,6 +582,17 @@ static void eval(Env & env, Expr e, Value & v) return; } + Expr e3; + if (matchIf(e, e1, e2, e3)) { + eval(env, evalBool(env, e1) ? e2 : e3, v); + return; + } + + if (matchOpOr(e, e1, e2)) { + mkBool(v, evalBool(env, e1) || evalBool(env, e2)); + return; + } + throw Error("unsupported term"); } @@ -633,6 +657,10 @@ void doTest(string s) v.type = tBool; v.boolean = false; } + { + Value & v = baseEnv.bindings[toATerm("null")]; + v.type = tNull; + } /* Add primops to the base environment. */ addPrimOp(baseEnv, "__head", 1, prim_head); @@ -689,6 +717,10 @@ void run(Strings args) doTest("null"); doTest("\"foo\""); doTest("let s = \"bar\"; in \"foo${s}\""); + doTest("if true then 1 else 2"); + doTest("if false then 1 else 2"); + doTest("if false || true then 1 else 2"); + doTest("let x = x; in if true || x then 1 else 2"); printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% environments") % nrEnvs); From 31428c3a0675f7223470af726bc697dc7a228927 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Mar 2010 14:37:56 +0000 Subject: [PATCH 023/103] * Started integrating the new evaluator. --- src/libexpr/attr-path.cc | 2 + src/libexpr/eval-test.cc | 662 +------------------------ src/libexpr/eval.cc | 564 ++++++++++++++++++++- src/libexpr/eval.hh | 152 +++++- src/libexpr/get-drvs.cc | 2 + src/libexpr/parser.y | 4 +- src/libexpr/primops.cc | 58 ++- src/nix-instantiate/nix-instantiate.cc | 10 +- 8 files changed, 745 insertions(+), 709 deletions(-) diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index e8e4c050c..5d81303fe 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -6,6 +6,7 @@ namespace nix { +#if 0 bool isAttrs(EvalState & state, Expr e, ATermMap & attrs) { e = evalExpr(state, e); @@ -77,6 +78,7 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath, return e; } +#endif } diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 543a9cf85..d37014a73 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -10,668 +10,13 @@ using namespace nix; -struct Env; -struct Value; - -typedef ATerm Sym; - -typedef std::map Bindings; - - -struct Env -{ - Env * up; - Bindings bindings; -}; - - -typedef enum { - tInt = 1, - tBool, - tString, - tPath, - tNull, - tAttrs, - tList, - tThunk, - tLambda, - tCopy, - tBlackhole, - tPrimOp, - tPrimOpApp, -} ValueType; - - -typedef void (* PrimOp_) (Value * * args, Value & v); - - -struct Value -{ - ValueType type; - union - { - int integer; - bool boolean; - struct { - const char * s; - const char * * context; - } string; - Bindings * attrs; - struct { - unsigned int length; - Value * elems; - } list; - struct { - Env * env; - Expr expr; - } thunk; - struct { - Env * env; - Pattern pat; - Expr body; - } lambda; - Value * val; - struct { - PrimOp_ fun; - unsigned int arity; - } primOp; - struct { - Value * left, * right; - unsigned int argsLeft; - } primOpApp; - }; -}; - - -static void mkThunk(Value & v, Env & env, Expr expr) -{ - v.type = tThunk; - v.thunk.env = &env; - v.thunk.expr = expr; -} - - -static void mkInt(Value & v, int n) -{ - v.type = tInt; - v.integer = n; -} - - -static void mkBool(Value & v, bool b) -{ - v.type = tBool; - v.boolean = b; -} - - -static void mkString(Value & v, const char * s) -{ - v.type = tString; - v.string.s = s; - v.string.context = 0; -} - - -std::ostream & operator << (std::ostream & str, Value & v) -{ - switch (v.type) { - case tInt: - str << v.integer; - break; - case tBool: - str << (v.boolean ? "true" : "false"); - break; - case tString: - str << "\"" << v.string.s << "\""; // !!! escaping - break; - case tNull: - str << "true"; - break; - case tAttrs: - str << "{ "; - foreach (Bindings::iterator, i, *v.attrs) - str << aterm2String(i->first) << " = " << i->second << "; "; - str << "}"; - break; - case tList: - str << "[ "; - for (unsigned int n = 0; n < v.list.length; ++n) - str << v.list.elems[n] << " "; - str << "]"; - break; - case tThunk: - str << ""; - break; - case tLambda: - str << ""; - break; - case tPrimOp: - str << ""; - break; - case tPrimOpApp: - str << ""; - break; - default: - abort(); - } - return str; -} - - -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) { - v.type = tBlackhole; - eval(*v.thunk.env, v.thunk.expr, v); - } - else if (v.type == tCopy) { - forceValue(*v.val); - v = *v.val; - } - else if (v.type == tBlackhole) - throw EvalError("infinite recursion encountered"); -} - - -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; - Value * v = lookupWith(env->up, name); - if (v) return v; - Bindings::iterator i = env->bindings.find(sWith); - if (i == env->bindings.end()) return 0; - Bindings::iterator j = i->second.attrs->find(name); - if (j != i->second.attrs->end()) return &j->second; - return 0; -} - - -static Value * lookupVar(Env * env, Sym name) -{ - /* First look for a regular variable binding for `name'. */ - for (Env * env2 = env; env2; env2 = env2->up) { - Bindings::iterator i = env2->bindings.find(name); - if (i != env2->bindings.end()) return &i->second; - } - - /* Otherwise, look for a `with' attribute set containing `name'. - Outer `withs' take precedence (i.e. `with {x=1;}; with {x=2;}; - x' evaluates to 1). */ - Value * v = lookupWith(env, name); - if (v) return v; - - /* Alternative implementation where the inner `withs' take - precedence (i.e. `with {x=1;}; with {x=2;}; x' evaluates to - 2). */ -#if 0 - for (Env * env2 = env; env2; env2 = env2->up) { - Bindings::iterator i = env2->bindings.find(sWith); - if (i == env2->bindings.end()) continue; - Bindings::iterator j = i->second.attrs->find(name); - if (j != i->second.attrs->end()) return &j->second; - } -#endif - - throw Error("undefined variable"); -} - - -static bool eqValues(Value & v1, Value & v2) -{ - forceValue(v1); - forceValue(v2); - switch (v1.type) { - - case tInt: - return v2.type == tInt && v1.integer == v2.integer; - - case tBool: - return v2.type == tBool && v1.boolean == v2.boolean; - - case tList: - if (v2.type != tList || v1.list.length != v2.list.length) return false; - for (unsigned int n = 0; n < v1.list.length; ++n) - if (!eqValues(v1.list.elems[n], v2.list.elems[n])) return false; - return true; - - case tAttrs: { - if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false; - Bindings::iterator i, j; - for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) - if (!eqValues(i->second, j->second)) return false; - return true; - } - - default: - throw Error("cannot compare given values"); - } -} - - -unsigned long nrValues = 0, nrEnvs = 0, nrEvaluated = 0; - -static Value * allocValues(unsigned int count) -{ - nrValues += count; - return new Value[count]; // !!! check destructor -} - -static Env & allocEnv() -{ - nrEnvs++; - return *(new Env); -} - - -char * p1 = 0, * p2 = 0; - - -static bool evalBool(Env & env, Expr e) -{ - Value v; - eval(env, e, v); - if (v.type != tBool) - throw TypeError(format("value is %1% while a Boolean was expected") % showType(v)); - return v.boolean; -} - - -static void eval(Env & env, Expr e, Value & v) -{ - char c; - if (!p1) p1 = &c; else if (!p2) p2 = &c; - - printMsg(lvlError, format("eval: %1%") % e); - - nrEvaluated++; - - Sym name; - if (matchVar(e, name)) { - Value * v2 = lookupVar(&env, name); - forceValue(*v2); - v = *v2; - return; - } - - int n; - if (matchInt(e, n)) { - mkInt(v, n); - return; - } - - ATerm s; ATermList context; - if (matchStr(e, s, context)) { - assert(context == ATempty); - mkString(v, ATgetName(ATgetAFun(s))); - return; - } - - ATermList es; - if (matchAttrs(e, es)) { - v.type = tAttrs; - v.attrs = new Bindings; - ATerm e2, pos; - for (ATermIterator i(es); i; ++i) { - if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value & v2 = (*v.attrs)[name]; - nrValues++; - mkThunk(v2, env, e2); - } - return; - } - - ATermList rbnds, nrbnds; - if (matchRec(e, rbnds, nrbnds)) { - Env & env2(allocEnv()); - env2.up = &env; - - v.type = tAttrs; - v.attrs = &env2.bindings; - ATerm name, e2, pos; - for (ATermIterator i(rbnds); i; ++i) { - if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value & v2 = env2.bindings[name]; - nrValues++; - mkThunk(v2, env2, e2); - } - - return; - } - - Expr e1, e2; - 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); - if (i == v.attrs->end()) throw TypeError("attribute not found"); - forceValue(i->second); - v = i->second; - return; - } - - Pattern pat; Expr body; Pos pos; - 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)) { - eval(env, fun, v); - - if (v.type == tPrimOp || v.type == tPrimOpApp) { - unsigned int argsLeft = - v.type == tPrimOp ? v.primOp.arity : v.primOpApp.argsLeft; - if (argsLeft == 1) { - /* We have all the arguments, so call the primop. - First find the primop. */ - Value * primOp = &v; - while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left; - assert(primOp->type == tPrimOp); - unsigned int arity = primOp->primOp.arity; - - Value vLastArg; - mkThunk(vLastArg, env, arg); - - /* Put all the arguments in an array. */ - Value * vArgs[arity]; - unsigned int n = arity - 1; - vArgs[n--] = &vLastArg; - for (Value * arg = &v; arg->type == tPrimOpApp; arg = arg->primOpApp.left) - vArgs[n--] = arg->primOpApp.right; - - /* And call the primop. */ - primOp->primOp.fun(vArgs, v); - } else { - Value * v2 = allocValues(2); - v2[0] = v; - mkThunk(v2[1], env, arg); - v.type = tPrimOpApp; - v.primOpApp.left = &v2[0]; - v.primOpApp.right = &v2[1]; - v.primOpApp.argsLeft = argsLeft - 1; - } - return; - } - - if (v.type != tLambda) throw TypeError("expected function"); - - Env & env2(allocEnv()); - env2.up = &env; - - ATermList formals; ATerm ellipsis; - - if (matchVarPat(v.lambda.pat, name)) { - Value & vArg = env2.bindings[name]; - nrValues++; - mkThunk(vArg, env, arg); - } - - else if (matchAttrsPat(v.lambda.pat, formals, ellipsis, name)) { - Value * vArg; - Value vArg_; - - if (name == sNoAlias) - vArg = &vArg_; - else { - vArg = &env2.bindings[name]; - nrValues++; - } - - eval(env, arg, *vArg); - forceAttrs(*vArg); - - /* For each formal argument, get the actual argument. If - there is no matching actual argument but the formal - argument has a default, use the default. */ - unsigned int attrsUsed = 0; - for (ATermIterator i(formals); i; ++i) { - Expr def; Sym name; - DefaultValue def2; - if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ - - Bindings::iterator j = vArg->attrs->find(name); - - Value & v = env2.bindings[name]; - nrValues++; - - if (j == vArg->attrs->end()) { - if (!matchDefaultValue(def2, def)) def = 0; - if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") - % aterm2String(name)); - mkThunk(v, env2, def); - } else { - attrsUsed++; - v.type = tCopy; - v.val = &j->second; - } - } - - /* Check that each actual argument is listed as a formal - argument (unless the attribute match specifies a - `...'). TODO: show the names of the - expected/unexpected arguments. */ - if (ellipsis == eFalse && attrsUsed != vArg->attrs->size()) - throw TypeError("function called with unexpected argument"); - } - - else abort(); - - eval(env2, v.lambda.body, v); - return; - } - - Expr attrs; - if (matchWith(e, attrs, body, pos)) { - Env & env2(allocEnv()); - env2.up = &env; - - Value & vAttrs = env2.bindings[sWith]; - nrValues++; - eval(env, attrs, vAttrs); - forceAttrs(vAttrs); - - eval(env2, body, v); - return; - } - - if (matchList(e, es)) { - v.type = tList; - v.list.length = ATgetLength(es); - v.list.elems = allocValues(v.list.length); - 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)) { - Value v1; eval(env, e1, v1); - Value v2; eval(env, e2, v2); - mkBool(v, eqValues(v1, v2)); - return; - } - - 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)) { - Value v1; eval(env, e1, v1); - forceList(v1); - Value v2; eval(env, e2, v2); - forceList(v2); - v.type = tList; - v.list.length = v1.list.length + v2.list.length; - v.list.elems = allocValues(v.list.length); - /* !!! This loses sharing with the original lists. We could - use a tCopy node, but that would use more memory. */ - for (unsigned int n = 0; n < v1.list.length; ++n) - 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)) { - unsigned int n = ATgetLength(es), j = 0; - Value vs[n]; - unsigned int len = 0; - for (ATermIterator i(es); i; ++i, ++j) { - eval(env, *i, vs[j]); - if (vs[j].type != tString) throw TypeError("string expected"); - len += strlen(vs[j].string.s); - } - char * s = new char[len + 1], * t = s; - for (unsigned int i = 0; i < j; ++i) { - strcpy(t, vs[i].string.s); - t += strlen(vs[i].string.s); - } - *t = 0; - mkString(v, s); - return; - } - - Expr e3; - if (matchIf(e, e1, e2, e3)) { - eval(env, evalBool(env, e1) ? e2 : e3, v); - return; - } - - if (matchOpOr(e, e1, e2)) { - mkBool(v, evalBool(env, e1) || evalBool(env, e2)); - return; - } - - throw Error("unsupported term"); -} - - -static void strictEval(Env & env, Expr e, Value & v) -{ - eval(env, e, v); - - if (v.type == tAttrs) { - foreach (Bindings::iterator, i, *v.attrs) - forceValue(i->second); - } - - else if (v.type == tList) { - for (unsigned int n = 0; n < v.list.length; ++n) - forceValue(v.list.elems[n]); - } -} - - -static void prim_head(Value * * args, Value & v) -{ - forceList(*args[0]); - if (args[0]->list.length == 0) - throw Error("`head' called on an empty list"); - forceValue(args[0]->list.elems[0]); - v = args[0]->list.elems[0]; -} - - -static void prim_add(Value * * args, Value & v) -{ - forceInt(*args[0]); - forceInt(*args[1]); - mkInt(v, args[0]->integer + args[1]->integer); -} - - -static void addPrimOp(Env & env, const string & name, unsigned int arity, PrimOp_ fun) -{ - Value & v = env.bindings[toATerm(name)]; - nrValues++; - v.type = tPrimOp; - v.primOp.arity = arity; - v.primOp.fun = fun; -} - - void doTest(string s) { - Env baseEnv; - baseEnv.up = 0; - - /* Add global constants such as `true' to the base environment. */ - { - Value & v = baseEnv.bindings[toATerm("true")]; - v.type = tBool; - v.boolean = true; - } - { - Value & v = baseEnv.bindings[toATerm("false")]; - v.type = tBool; - v.boolean = false; - } - { - Value & v = baseEnv.bindings[toATerm("null")]; - v.type = tNull; - } - - /* Add primops to the base environment. */ - addPrimOp(baseEnv, "__head", 1, prim_head); - addPrimOp(baseEnv, "__add", 2, prim_add); - - p1 = p2 = 0; EvalState state; Expr e = parseExprFromString(state, s, "/"); printMsg(lvlError, format(">>>>> %1%") % e); Value v; - strictEval(baseEnv, e, v); + state.strictEval(e, v); printMsg(lvlError, format("result: %1%") % v); } @@ -721,11 +66,6 @@ void run(Strings args) doTest("if false then 1 else 2"); doTest("if false || true then 1 else 2"); doTest("let x = x; in if true || x then 1 else 2"); - - 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)); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 1501fc048..794e39660 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -7,6 +7,8 @@ #include "nixexpr-ast.hh" #include "globals.hh" +#include + #define LocalNoInline(f) static f __attribute__((noinline)); f #define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f @@ -15,15 +17,77 @@ namespace nix { -EvalState::EvalState() - : normalForms(32768), primOps(128) +std::ostream & operator << (std::ostream & str, Value & v) { - nrEvaluated = nrCached = 0; + switch (v.type) { + case tInt: + str << v.integer; + break; + case tBool: + str << (v.boolean ? "true" : "false"); + break; + case tString: + str << "\"" << v.string.s << "\""; // !!! escaping + break; + case tNull: + str << "true"; + break; + case tAttrs: + str << "{ "; + foreach (Bindings::iterator, i, *v.attrs) + str << aterm2String(i->first) << " = " << i->second << "; "; + str << "}"; + break; + case tList: + str << "[ "; + for (unsigned int n = 0; n < v.list.length; ++n) + str << v.list.elems[n] << " "; + str << "]"; + break; + case tThunk: + str << ""; + break; + case tLambda: + str << ""; + break; + case tPrimOp: + str << ""; + break; + case tPrimOpApp: + str << ""; + break; + default: + throw Error("invalid value"); + } + return str; +} + + +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"); + } +} + + +EvalState::EvalState() : baseEnv(allocEnv()) +{ + nrValues = nrEnvs = nrEvaluated = 0; initNixExprHelpers(); - addPrimOps(); - + createBaseEnv(); + allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == ""; } @@ -31,7 +95,11 @@ EvalState::EvalState() void EvalState::addPrimOp(const string & name, unsigned int arity, PrimOp primOp) { - primOps.set(toATerm(name), makePrimOpDef(arity, ATmakeBlob(0, (void *) primOp))); + Value & v = baseEnv.bindings[toATerm(name)]; + nrValues++; + v.type = tPrimOp; + v.primOp.arity = arity; + v.primOp.fun = primOp; } @@ -76,6 +144,471 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, } +static void mkThunk(Value & v, Env & env, Expr expr) +{ + v.type = tThunk; + v.thunk.env = &env; + v.thunk.expr = expr; +} + + +static Value * lookupWith(Env * env, Sym name) +{ + if (!env) return 0; + Value * v = lookupWith(env->up, name); + if (v) return v; + Bindings::iterator i = env->bindings.find(sWith); + if (i == env->bindings.end()) return 0; + Bindings::iterator j = i->second.attrs->find(name); + if (j != i->second.attrs->end()) return &j->second; + return 0; +} + + +static Value * lookupVar(Env * env, Sym name) +{ + /* First look for a regular variable binding for `name'. */ + for (Env * env2 = env; env2; env2 = env2->up) { + Bindings::iterator i = env2->bindings.find(name); + if (i != env2->bindings.end()) return &i->second; + } + + /* Otherwise, look for a `with' attribute set containing `name'. + Outer `withs' take precedence (i.e. `with {x=1;}; with {x=2;}; + x' evaluates to 1). */ + Value * v = lookupWith(env, name); + if (v) return v; + + /* Alternative implementation where the inner `withs' take + precedence (i.e. `with {x=1;}; with {x=2;}; x' evaluates to + 2). */ +#if 0 + for (Env * env2 = env; env2; env2 = env2->up) { + Bindings::iterator i = env2->bindings.find(sWith); + if (i == env2->bindings.end()) continue; + Bindings::iterator j = i->second.attrs->find(name); + if (j != i->second.attrs->end()) return &j->second; + } +#endif + + throw Error("undefined variable"); +} + + +Value * EvalState::allocValues(unsigned int count) +{ + nrValues += count; + return new Value[count]; // !!! check destructor +} + + +Env & EvalState::allocEnv() +{ + nrEnvs++; + return *(new Env); +} + + +static char * deepestStack = (char *) -1; /* for measuring stack usage */ + + +void EvalState::eval(Env & env, Expr e, Value & v) +{ + /* When changing this function, make sure that you don't cause a + (large) increase in stack consumption! */ + + char x; + if (&x < deepestStack) deepestStack = &x; + + printMsg(lvlError, format("eval: %1%") % e); + + nrEvaluated++; + + Sym name; + if (matchVar(e, name)) { + Value * v2 = lookupVar(&env, name); + forceValue(*v2); + v = *v2; + return; + } + + int n; + if (matchInt(e, n)) { + mkInt(v, n); + return; + } + + ATerm s; ATermList context; + if (matchStr(e, s, context)) { + assert(context == ATempty); + mkString(v, ATgetName(ATgetAFun(s))); + return; + } + + ATermList es; + if (matchAttrs(e, es)) { + v.type = tAttrs; + v.attrs = new Bindings; + ATerm e2, pos; + for (ATermIterator i(es); i; ++i) { + if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ + Value & v2 = (*v.attrs)[name]; + nrValues++; + mkThunk(v2, env, e2); + } + return; + } + + ATermList rbnds, nrbnds; + if (matchRec(e, rbnds, nrbnds)) { + Env & env2(allocEnv()); + env2.up = &env; + + v.type = tAttrs; + v.attrs = &env2.bindings; + ATerm name, e2, pos; + for (ATermIterator i(rbnds); i; ++i) { + if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ + Value & v2 = env2.bindings[name]; + nrValues++; + mkThunk(v2, env2, e2); + } + + return; + } + + Expr e1, e2; + 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); + if (i == v.attrs->end()) throw TypeError("attribute not found"); + forceValue(i->second); + v = i->second; + return; + } + + Pattern pat; Expr body; Pos pos; + 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)) { + eval(env, fun, v); + + if (v.type == tPrimOp || v.type == tPrimOpApp) { + unsigned int argsLeft = + v.type == tPrimOp ? v.primOp.arity : v.primOpApp.argsLeft; + if (argsLeft == 1) { + /* We have all the arguments, so call the primop. + First find the primop. */ + Value * primOp = &v; + while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left; + assert(primOp->type == tPrimOp); + unsigned int arity = primOp->primOp.arity; + + Value vLastArg; + mkThunk(vLastArg, env, arg); + + /* Put all the arguments in an array. */ + Value * vArgs[arity]; + unsigned int n = arity - 1; + vArgs[n--] = &vLastArg; + for (Value * arg = &v; arg->type == tPrimOpApp; arg = arg->primOpApp.left) + vArgs[n--] = arg->primOpApp.right; + + /* And call the primop. */ + primOp->primOp.fun(*this, vArgs, v); + } else { + Value * v2 = allocValues(2); + v2[0] = v; + mkThunk(v2[1], env, arg); + v.type = tPrimOpApp; + v.primOpApp.left = &v2[0]; + v.primOpApp.right = &v2[1]; + v.primOpApp.argsLeft = argsLeft - 1; + } + return; + } + + if (v.type != tLambda) throw TypeError("expected function"); + + Env & env2(allocEnv()); + env2.up = &env; + + ATermList formals; ATerm ellipsis; + + if (matchVarPat(v.lambda.pat, name)) { + Value & vArg = env2.bindings[name]; + nrValues++; + mkThunk(vArg, env, arg); + } + + else if (matchAttrsPat(v.lambda.pat, formals, ellipsis, name)) { + Value * vArg; + Value vArg_; + + if (name == sNoAlias) + vArg = &vArg_; + else { + vArg = &env2.bindings[name]; + nrValues++; + } + + eval(env, arg, *vArg); + forceAttrs(*vArg); + + /* For each formal argument, get the actual argument. If + there is no matching actual argument but the formal + argument has a default, use the default. */ + unsigned int attrsUsed = 0; + for (ATermIterator i(formals); i; ++i) { + Expr def; Sym name; + DefaultValue def2; + if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ + + Bindings::iterator j = vArg->attrs->find(name); + + Value & v = env2.bindings[name]; + nrValues++; + + if (j == vArg->attrs->end()) { + if (!matchDefaultValue(def2, def)) def = 0; + if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") + % aterm2String(name)); + mkThunk(v, env2, def); + } else { + attrsUsed++; + v.type = tCopy; + v.val = &j->second; + } + } + + /* Check that each actual argument is listed as a formal + argument (unless the attribute match specifies a + `...'). TODO: show the names of the + expected/unexpected arguments. */ + if (ellipsis == eFalse && attrsUsed != vArg->attrs->size()) + throw TypeError("function called with unexpected argument"); + } + + else abort(); + + eval(env2, v.lambda.body, v); + return; + } + + Expr attrs; + if (matchWith(e, attrs, body, pos)) { + Env & env2(allocEnv()); + env2.up = &env; + + Value & vAttrs = env2.bindings[sWith]; + nrValues++; + eval(env, attrs, vAttrs); + forceAttrs(vAttrs); + + eval(env2, body, v); + return; + } + + if (matchList(e, es)) { + v.type = tList; + v.list.length = ATgetLength(es); + v.list.elems = allocValues(v.list.length); + 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)) { + Value v1; eval(env, e1, v1); + Value v2; eval(env, e2, v2); + mkBool(v, eqValues(v1, v2)); + return; + } + + 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)) { + Value v1; eval(env, e1, v1); + forceList(v1); + Value v2; eval(env, e2, v2); + forceList(v2); + v.type = tList; + v.list.length = v1.list.length + v2.list.length; + v.list.elems = allocValues(v.list.length); + /* !!! This loses sharing with the original lists. We could + use a tCopy node, but that would use more memory. */ + for (unsigned int n = 0; n < v1.list.length; ++n) + 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)) { + unsigned int n = ATgetLength(es), j = 0; + Value vs[n]; + unsigned int len = 0; + for (ATermIterator i(es); i; ++i, ++j) { + eval(env, *i, vs[j]); + if (vs[j].type != tString) throw TypeError("string expected"); + len += strlen(vs[j].string.s); + } + char * s = new char[len + 1], * t = s; + for (unsigned int i = 0; i < j; ++i) { + strcpy(t, vs[i].string.s); + t += strlen(vs[i].string.s); + } + *t = 0; + mkString(v, s); + return; + } + + Expr e3; + if (matchIf(e, e1, e2, e3)) { + eval(env, evalBool(env, e1) ? e2 : e3, v); + return; + } + + if (matchOpOr(e, e1, e2)) { + mkBool(v, evalBool(env, e1) || evalBool(env, e2)); + return; + } + + throw Error("unsupported term"); +} + + +void EvalState::eval(Expr e, Value & v) +{ + eval(baseEnv, e, v); +} + + +bool EvalState::evalBool(Env & env, Expr e) +{ + Value v; + eval(env, e, v); + if (v.type != tBool) + throw TypeError(format("value is %1% while a Boolean was expected") % showType(v)); + return v.boolean; +} + + +void EvalState::strictEval(Env & env, Expr e, Value & v) +{ + eval(env, e, v); + + if (v.type == tAttrs) { + foreach (Bindings::iterator, i, *v.attrs) + forceValue(i->second); + } + + else if (v.type == tList) { + for (unsigned int n = 0; n < v.list.length; ++n) + forceValue(v.list.elems[n]); + } +} + + +void EvalState::strictEval(Expr e, Value & v) +{ + strictEval(baseEnv, e, v); +} + + +void EvalState::forceValue(Value & v) +{ + if (v.type == tThunk) { + v.type = tBlackhole; + eval(*v.thunk.env, v.thunk.expr, v); + } + else if (v.type == tCopy) { + forceValue(*v.val); + v = *v.val; + } + else if (v.type == tBlackhole) + throw EvalError("infinite recursion encountered"); +} + + +int EvalState::forceInt(Value & v) +{ + forceValue(v); + if (v.type != tInt) + throw TypeError(format("value is %1% while an integer was expected") % showType(v)); + return v.integer; +} + + +void EvalState::forceAttrs(Value & v) +{ + forceValue(v); + if (v.type != tAttrs) + throw TypeError(format("value is %1% while an attribute set was expected") % showType(v)); +} + + +void EvalState::forceList(Value & v) +{ + forceValue(v); + if (v.type != tList) + throw TypeError(format("value is %1% while a list was expected") % showType(v)); +} + + +bool EvalState::eqValues(Value & v1, Value & v2) +{ + forceValue(v1); + forceValue(v2); + + if (v1.type != v2.type) return false; + + switch (v1.type) { + + case tInt: + return v1.integer == v2.integer; + + case tBool: + return v1.boolean == v2.boolean; + + case tString: + /* !!! contexts */ + return strcmp(v1.string.s, v2.string.s) == 0; + + case tList: + if (v2.type != tList || v1.list.length != v2.list.length) return false; + for (unsigned int n = 0; n < v1.list.length; ++n) + if (!eqValues(v1.list.elems[n], v2.list.elems[n])) return false; + return true; + + case tAttrs: { + if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false; + Bindings::iterator i, j; + for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) + if (!eqValues(i->second, j->second)) return false; + return true; + } + + default: + throw Error("cannot compare given values"); + } +} + + +#if 0 /* Pattern-match `pat' against `arg'. The result is a set of substitutions (`subs') and a set of recursive substitutions (`subsRecursive'). The latter can refer to the variables bound by @@ -683,9 +1216,6 @@ LocalNoInline(bool areEqual(EvalState & state, Expr e1, Expr e2)) } -static char * deepestStack = (char *) -1; /* for measuring stack usage */ - - Expr evalExpr2(EvalState & state, Expr e) { /* When changing this function, make sure that you don't cause a @@ -884,23 +1414,19 @@ Expr strictEvalExpr(EvalState & state, Expr e) ATermMap strictNormalForms; return strictEvalExpr(state, e, strictNormalForms); } +#endif -/* Yes, this is a really bad idea... */ -extern "C" { - unsigned long AT_calcAllocatedSize(); -} - void printEvalStats(EvalState & state) { char x; bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0"; printMsg(showStats ? lvlInfo : lvlDebug, - format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space") - % state.nrEvaluated % state.nrCached - % ((float) state.nrCached / (float) state.nrEvaluated * 100) - % AT_calcAllocatedSize() - % (&x - deepestStack)); + format("evaluated %1% expressions, used %2% bytes of stack space, allocated %3% values, allocated %4% environments") + % state.nrEvaluated + % (&x - deepestStack) + % state.nrValues + % state.nrEnvs); if (showStats) printATermMapStats(); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index fed6d3472..8ca997f14 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -11,7 +11,101 @@ namespace nix { class Hash; - +class EvalState; +struct Env; +struct Value; + +typedef ATerm Sym; + +typedef std::map Bindings; + + +struct Env +{ + Env * up; + Bindings bindings; +}; + + +typedef enum { + tInt = 1, + tBool, + tString, + tPath, + tNull, + tAttrs, + tList, + tThunk, + tLambda, + tCopy, + tBlackhole, + tPrimOp, + tPrimOpApp, +} ValueType; + + +typedef void (* PrimOp) (EvalState & state, Value * * args, Value & v); + + +struct Value +{ + ValueType type; + union + { + int integer; + bool boolean; + struct { + const char * s; + const char * * context; + } string; + Bindings * attrs; + struct { + unsigned int length; + Value * elems; + } list; + struct { + Env * env; + Expr expr; + } thunk; + struct { + Env * env; + Pattern pat; + Expr body; + } lambda; + Value * val; + struct { + PrimOp fun; + unsigned int arity; + } primOp; + struct { + Value * left, * right; + unsigned int argsLeft; + } primOpApp; + }; +}; + + +static inline void mkInt(Value & v, int n) +{ + v.type = tInt; + v.integer = n; +} + + +static inline void mkBool(Value & v, bool b) +{ + v.type = tBool; + v.boolean = b; +} + + +static inline void mkString(Value & v, const char * s) +{ + v.type = tString; + v.string.s = s; + v.string.context = 0; +} + typedef std::map DrvRoots; typedef std::map DrvHashes; @@ -22,32 +116,69 @@ typedef std::map SrcToStore; struct EvalState; -/* Note: using a ATermVector is safe here, since when we call a primop - we also have an ATermList on the stack. */ -typedef Expr (* PrimOp) (EvalState &, const ATermVector & args); + +std::ostream & operator << (std::ostream & str, Value & v); struct EvalState { - ATermMap normalForms; - ATermMap primOps; DrvRoots drvRoots; DrvHashes drvHashes; /* normalised derivation hashes */ SrcToStore srcToStore; - unsigned int nrEvaluated; - unsigned int nrCached; + unsigned long nrValues; + unsigned long nrEnvs; + unsigned long nrEvaluated; bool allowUnsafeEquality; EvalState(); - void addPrimOps(); + /* Evaluate an expression to normal form, storing the result in + value `v'. */ + void eval(Expr e, Value & v); + void eval(Env & env, Expr e, Value & v); + + /* Evaluation the expression, then verify that it has the expected + type. */ + bool evalBool(Env & env, Expr e); + + /* Evaluate an expression, and recursively evaluate list elements + and attributes. */ + void strictEval(Expr e, Value & v); + void strictEval(Env & env, Expr e, Value & v); + + /* If `v' is a thunk, enter it and overwrite `v' with the result + of the evaluation of the thunk. Otherwise, this is a no-op. */ + void forceValue(Value & v); + + /* Force `v', and then verify that it has the expected type. */ + int forceInt(Value & v); + void forceAttrs(Value & v); + void forceList(Value & v); + +private: + + /* The base environment, containing the builtin functions and + values. */ + Env & baseEnv; + + void createBaseEnv(); + void addPrimOp(const string & name, unsigned int arity, PrimOp primOp); + + /* Do a deep equality test between two values. That is, list + elements and attributes are compared recursively. */ + bool eqValues(Value & v1, Value & v2); + + /* Allocation primitives. */ + Value * allocValues(unsigned int count); + Env & allocEnv(); }; +#if 0 /* Evaluate an expression to normal form. */ Expr evalExpr(EvalState & state, Expr e); @@ -86,11 +217,12 @@ Path coerceToPath(EvalState & state, Expr e, PathSet & context); value or has a binding in the `args' map. Note: result is a call, not a normal form; it should be evaluated by calling evalExpr(). */ Expr autoCallFunction(Expr e, const ATermMap & args); +#endif /* Print statistics. */ void printEvalStats(EvalState & state); - + } diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 1442d7988..26ce6e71c 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -6,6 +6,7 @@ namespace nix { +#if 0 string DrvInfo::queryDrvPath(EvalState & state) const { if (drvPath == "") { @@ -256,6 +257,7 @@ void getDerivations(EvalState & state, Expr e, const string & pathPrefix, Exprs doneExprs; getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs); } +#endif } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index e55dfd76f..2f0c9db3f 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -375,7 +375,7 @@ expr_op | expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); } | expr_op '~' expr_op { $$ = makeSubPath($1, $3); } | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); } - | expr_op '+' expr_op { $$ = makeOpPlus($1, $3); } + | expr_op '+' expr_op { $$ = makeConcatStrings(ATmakeList2($1, $3)); } | expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); } | expr_app ; @@ -513,7 +513,7 @@ static Expr parse(EvalState & state, if (res) throw ParseError(data.error); try { - checkVarDefs(state.primOps, data.result); + // !!! checkVarDefs(state.primOps, data.result); } catch (Error & e) { throw ParseError(format("%1%, in `%2%'") % e.msg() % path); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 52292f3f8..a24f40be6 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -19,6 +19,7 @@ namespace nix { +#if 0 /************************************************************* * Constants *************************************************************/ @@ -895,18 +896,21 @@ static Expr prim_isList(EvalState & state, const ATermVector & args) ATermList list; return makeBool(matchList(evalExpr(state, args[0]), list)); } +#endif /* Return the first element of a list. */ -static Expr prim_head(EvalState & state, const ATermVector & args) +static void prim_head(EvalState & state, Value * * args, Value & v) { - ATermList list = evalList(state, args[0]); - if (ATisEmpty(list)) + state.forceList(*args[0]); + if (args[0]->list.length == 0) throw Error("`head' called on an empty list"); - return evalExpr(state, ATgetFirst(list)); + state.forceValue(args[0]->list.elems[0]); + v = args[0]->list.elems[0]; } +#if 0 /* Return a list consisting of everything but the the first element of a list. */ static Expr prim_tail(EvalState & state, const ATermVector & args) @@ -938,6 +942,7 @@ static Expr prim_length(EvalState & state, const ATermVector & args) ATermList list = evalList(state, args[0]); return makeInt(ATgetLength(list)); } +#endif /************************************************************* @@ -945,14 +950,13 @@ static Expr prim_length(EvalState & state, const ATermVector & args) *************************************************************/ -static Expr prim_add(EvalState & state, const ATermVector & args) +static void prim_add(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])); } +#if 0 static Expr prim_sub(EvalState & state, const ATermVector & args) { int i1 = evalInt(state, args[0]); @@ -1102,6 +1106,7 @@ static Expr prim_compareVersions(EvalState & state, const ATermVector & args) int d = compareVersions(version1, version2); return makeInt(d); } +#endif /************************************************************* @@ -1109,14 +1114,31 @@ static Expr prim_compareVersions(EvalState & state, const ATermVector & args) *************************************************************/ -void EvalState::addPrimOps() +void EvalState::createBaseEnv() { - addPrimOp("builtins", 0, prim_builtins); - + baseEnv.up = 0; + + { Value & v = baseEnv.bindings[toATerm("builtins")]; + v.type = tAttrs; + v.attrs = new Bindings; + } + + /* Add global constants such as `true' to the base environment. */ + { Value & v = baseEnv.bindings[toATerm("true")]; + mkBool(v, true); + } + { Value & v = baseEnv.bindings[toATerm("false")]; + mkBool(v, false); + } + { Value & v = baseEnv.bindings[toATerm("null")]; + v.type = tNull; + } + { Value & v = (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm("currentSystem")]; + mkString(v, thisSystem.c_str()); // !!! copy string + } + +#if 0 // Constants - addPrimOp("true", 0, prim_true); - addPrimOp("false", 0, prim_false); - addPrimOp("null", 0, prim_null); addPrimOp("__currentSystem", 0, prim_currentSystem); addPrimOp("__currentTime", 0, prim_currentTime); @@ -1134,7 +1156,6 @@ void EvalState::addPrimOps() addPrimOp("__tryEval", 1, prim_tryEval); addPrimOp("__getEnv", 1, prim_getEnv); addPrimOp("__trace", 2, prim_trace); - // Expr <-> String addPrimOp("__exprToString", 1, prim_exprToString); @@ -1169,13 +1190,17 @@ void EvalState::addPrimOps() // Lists addPrimOp("__isList", 1, prim_isList); +#endif addPrimOp("__head", 1, prim_head); +#if 0 addPrimOp("__tail", 1, prim_tail); addPrimOp("map", 2, prim_map); addPrimOp("__length", 1, prim_length); - +#endif + // Integer arithmetic addPrimOp("__add", 2, prim_add); +#if 0 addPrimOp("__sub", 2, prim_sub); addPrimOp("__mul", 2, prim_mul); addPrimOp("__div", 2, prim_div); @@ -1191,6 +1216,7 @@ void EvalState::addPrimOps() // Versions addPrimOp("__parseDrvName", 1, prim_parseDrvName); addPrimOp("__compareVersions", 2, prim_compareVersions); +#endif } diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 3822de5c6..5473db4ac 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -37,6 +37,7 @@ static int rootNr = 0; static bool indirectRoot = false; +#if 0 static void printResult(EvalState & state, Expr e, bool evalOnly, bool xmlOutput, const ATermMap & autoArgs) { @@ -63,21 +64,28 @@ static void printResult(EvalState & state, Expr e, } } } +#endif void processExpr(EvalState & state, const Strings & attrPaths, bool parseOnly, bool strict, const ATermMap & autoArgs, bool evalOnly, bool xmlOutput, Expr e) { + Value v; + state.strictEval(e, v); + std::cout << v << std::endl; + +#if 0 for (Strings::const_iterator i = attrPaths.begin(); i != attrPaths.end(); ++i) { Expr e2 = findAlongAttrPath(state, *i, autoArgs, e); if (!parseOnly) if (strict) - e2 = strictEvalExpr(state, e2); + e2 = state.strictEval(e2); else e2 = evalExpr(state, e2); printResult(state, e2, evalOnly, xmlOutput, autoArgs); } +#endif } From d78a05ab4014d75fd1e394961376f02cff20ed88 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Mar 2010 09:22:33 +0000 Subject: [PATCH 024/103] * Make `import' work. --- src/libexpr/eval-test.cc | 4 +- src/libexpr/eval.cc | 138 +++++++++++++++++++++++++++++---------- src/libexpr/eval.hh | 38 +++++++---- src/libexpr/primops.cc | 23 +++---- 4 files changed, 141 insertions(+), 62 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index d37014a73..8aade1298 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -13,7 +13,7 @@ using namespace nix; void doTest(string s) { EvalState state; - Expr e = parseExprFromString(state, s, "/"); + Expr e = parseExprFromString(state, s, absPath(".")); printMsg(lvlError, format(">>>>> %1%") % e); Value v; state.strictEval(e, v); @@ -66,6 +66,8 @@ void run(Strings args) doTest("if false then 1 else 2"); doTest("if false || true then 1 else 2"); doTest("let x = x; in if true || x then 1 else 2"); + doTest("/etc/passwd"); + doTest("import ./foo.nix"); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 794e39660..86484031b 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -29,6 +29,9 @@ std::ostream & operator << (std::ostream & str, Value & v) case tString: str << "\"" << v.string.s << "\""; // !!! escaping break; + case tPath: + str << v.path; // !!! escaping? + break; case tNull: str << "true"; break; @@ -209,6 +212,20 @@ Env & EvalState::allocEnv() } +void EvalState::evalFile(const Path & path, Value & v) +{ + startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path); + Expr e = parseExprFromFile(*this, path); + try { + eval(e, v); + } catch (Error & e) { + e.addPrefix(format("while evaluating the file `%1%':\n") + % path); + throw; + } +} + + static char * deepestStack = (char *) -1; /* for measuring stack usage */ @@ -241,7 +258,12 @@ void EvalState::eval(Env & env, Expr e, Value & v) ATerm s; ATermList context; if (matchStr(e, s, context)) { assert(context == ATempty); - mkString(v, ATgetName(ATgetAFun(s))); + mkString(v, strdup(ATgetName(ATgetAFun(s)))); + return; + } + + if (matchPath(e, s)) { + mkPath(v, strdup(ATgetName(ATgetAFun(s)))); return; } @@ -282,8 +304,14 @@ void EvalState::eval(Env & env, Expr e, Value & v) eval(env, e2, v); 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); + if (i == v.attrs->end()) + throwEvalError("attribute `%1%' missing", aterm2String(name)); + try { + forceValue(i->second); + } catch (Error & e) { + addErrorPrefix(e, "while evaluating the attribute `%1%':\n", aterm2String(name)); + throw; + } v = i->second; return; } @@ -569,6 +597,80 @@ void EvalState::forceList(Value & v) } +string EvalState::coerceToString(Value & v, PathSet & context, + bool coerceMore, bool copyToStore) +{ + forceValue(v); + + string s; + + if (v.type == tString) return v.string.s; + + if (v.type == tPath) { + Path path(canonPath(v.path)); + + if (!copyToStore) return path; + + if (isDerivation(path)) + throw EvalError(format("file names are not allowed to end in `%1%'") + % drvExtension); + + Path dstPath; + if (srcToStore[path] != "") + dstPath = srcToStore[path]; + else { + dstPath = readOnlyMode + ? computeStorePathForPath(path).first + : store->addToStore(path); + srcToStore[path] = dstPath; + printMsg(lvlChatty, format("copied source `%1%' -> `%2%'") + % path % dstPath); + } + + context.insert(dstPath); + return dstPath; + } + + if (v.type == tAttrs) { + Bindings::iterator i = v.attrs->find(toATerm("outPath")); + if (i == v.attrs->end()) + throwTypeError("cannot coerce an attribute set (except a derivation) to a string"); + return coerceToString(i->second, context, coerceMore, copyToStore); + } + + if (coerceMore) { + + /* Note that `false' is represented as an empty string for + shell scripting convenience, just like `null'. */ + if (v.type == tBool && v.boolean) return "1"; + if (v.type == tBool && !v.boolean) return ""; + if (v.type == tInt) return int2String(v.integer); + if (v.type == tNull) return ""; + + if (v.type == tList) { + string result; + for (unsigned int n = 0; n < v.list.length; ++n) { + if (n) result += " "; + result += coerceToString(v.list.elems[n], + context, coerceMore, copyToStore); + } + return result; + } + } + + throwTypeError("cannot coerce %1% to a string", showType(v)); +} + + +Path EvalState::coerceToPath(Value & v, PathSet & context) +{ + string path = coerceToString(v, context, false, false); + if (path == "" || path[0] != '/') + throw EvalError(format("string `%1%' doesn't represent an absolute path") % path); + return path; +} + + bool EvalState::eqValues(Value & v1, Value & v2) { forceValue(v1); @@ -1046,22 +1148,6 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg)) } -LocalNoInline(Expr evalSelect(EvalState & state, Expr e, ATerm name)) -{ - ATerm pos; - string s = aterm2String(name); - Expr a = queryAttr(evalExpr(state, e), s, pos); - if (!a) throwEvalError("attribute `%1%' missing", s); - try { - return evalExpr(state, a); - } catch (Error & e) { - addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n", - s, showPos(pos)); - throw; - } -} - - LocalNoInline(Expr evalAssert(EvalState & state, Expr cond, Expr body, ATerm pos)) { if (!evalBool(state, cond)) @@ -1352,20 +1438,6 @@ Expr evalExpr(EvalState & state, Expr e) } -Expr evalFile(EvalState & state, const Path & path) -{ - startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path); - Expr e = parseExprFromFile(state, path); - try { - return evalExpr(state, e); - } catch (Error & e) { - e.addPrefix(format("while evaluating the file `%1%':\n") - % path); - throw; - } -} - - static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 8ca997f14..4706602d5 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -58,6 +58,7 @@ struct Value const char * s; const char * * context; } string; + const char * path; Bindings * attrs; struct { unsigned int length; @@ -107,6 +108,13 @@ static inline void mkString(Value & v, const char * s) } +static inline void mkPath(Value & v, const char * s) +{ + v.type = tPath; + v.path = s; +} + + typedef std::map DrvRoots; typedef std::map DrvHashes; @@ -134,6 +142,10 @@ struct EvalState EvalState(); + /* Evaluate an expression read from the given file to normal + form. */ + void evalFile(const Path & path, Value & v); + /* Evaluate an expression to normal form, storing the result in value `v'. */ void eval(Expr e, Value & v); @@ -157,6 +169,18 @@ struct EvalState void forceAttrs(Value & v); void forceList(Value & v); + /* String coercion. Converts strings, paths and derivations to a + string. If `coerceMore' is set, also converts nulls, integers, + booleans and lists to a string. If `copyToStore' is set, + referenced paths are copied to the Nix store as a side effect.q */ + string coerceToString(Value & v, PathSet & context, + bool coerceMore = false, bool copyToStore = true); + + /* Path coercion. Converts strings, paths and derivations to a + path. The result is guaranteed to be a canonicalised, absolute + path. Nothing is copied to the store. */ + Path coerceToPath(Value & v, PathSet & context); + private: /* The base environment, containing the builtin functions and @@ -182,9 +206,6 @@ private: /* Evaluate an expression to normal form. */ Expr evalExpr(EvalState & state, Expr e); -/* Evaluate an expression read from the given file to normal form. */ -Expr evalFile(EvalState & state, const Path & path); - /* Evaluate an expression, and recursively evaluate list elements and attributes. If `canonicalise' is true, we remove things like position information and make sure that attribute sets are in @@ -202,17 +223,6 @@ ATermList evalList(EvalState & state, Expr e); a list). */ ATermList flattenList(EvalState & state, Expr e); -/* String coercion. Converts strings, paths and derivations to a - string. If `coerceMore' is set, also converts nulls, integers, - booleans and lists to a string. */ -string coerceToString(EvalState & state, Expr e, PathSet & context, - bool coerceMore = false, bool copyToStore = true); - -/* Path coercion. Converts strings, paths and derivations to a path. - The result is guaranteed to be an canonicalised, absolute path. - Nothing is copied to the store. */ -Path coerceToPath(EvalState & state, Expr e, PathSet & context); - /* Automatically call a function for which each argument has a default value or has a binding in the `args' map. Note: result is a call, not a normal form; it should be evaluated by calling evalExpr(). */ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a24f40be6..2815567e5 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -14,6 +14,7 @@ #include #include +#include namespace nix { @@ -69,20 +70,11 @@ static Expr prim_null(EvalState & state, const ATermVector & args) } -/* Return a string constant representing the current platform. Note! - that differs between platforms, so Nix expressions using - `__currentSystem' can evaluate to different values on different - platforms. */ -static Expr prim_currentSystem(EvalState & state, const ATermVector & args) -{ - return makeStr(thisSystem); -} - - static Expr prim_currentTime(EvalState & state, const ATermVector & args) { return ATmake("Int()", time(0)); } +#endif /************************************************************* @@ -92,10 +84,10 @@ static Expr prim_currentTime(EvalState & state, const ATermVector & args) /* Load and evaluate an expression from path specified by the argument. */ -static Expr prim_import(EvalState & state, const ATermVector & args) +static void prim_import(EvalState & state, Value * * args, Value & v) { PathSet context; - Path path = coerceToPath(state, args[0], context); + Path path = state.coerceToPath(*args[0], context); for (PathSet::iterator i = context.begin(); i != context.end(); ++i) { assert(isStorePath(*i)); @@ -106,10 +98,11 @@ static Expr prim_import(EvalState & state, const ATermVector & args) store->buildDerivations(singleton(*i)); } - return evalFile(state, path); + state.evalFile(path, v); } +#if 0 /* Determine whether the argument is the null value. */ static Expr prim_isNull(EvalState & state, const ATermVector & args) { @@ -1134,7 +1127,7 @@ void EvalState::createBaseEnv() v.type = tNull; } { Value & v = (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm("currentSystem")]; - mkString(v, thisSystem.c_str()); // !!! copy string + mkString(v, strdup(thisSystem.c_str())); } #if 0 @@ -1143,7 +1136,9 @@ void EvalState::createBaseEnv() addPrimOp("__currentTime", 0, prim_currentTime); // Miscellaneous +#endif addPrimOp("import", 1, prim_import); +#if 0 addPrimOp("isNull", 1, prim_isNull); addPrimOp("__isFunction", 1, prim_isFunction); addPrimOp("__isString", 1, prim_isString); From 5b72d8a749e6718986f6e2bfef2ae725981a26ff Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Mar 2010 13:47:59 +0000 Subject: [PATCH 025/103] * Implemented `map'. --- src/libexpr/eval-test.cc | 2 + src/libexpr/eval.cc | 210 ++++++++++++++++++++------------------- src/libexpr/eval.hh | 13 ++- src/libexpr/primops.cc | 22 ++-- 4 files changed, 139 insertions(+), 108 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 8aade1298..0b20883a3 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -68,6 +68,8 @@ void run(Strings args) doTest("let x = x; in if true || x then 1 else 2"); doTest("/etc/passwd"); doTest("import ./foo.nix"); + doTest("map (x: __add 1 x) [ 1 2 3 ]"); + doTest("map (__add 1) [ 1 2 3 ]"); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 86484031b..f1437e465 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -328,106 +328,9 @@ void EvalState::eval(Env & env, Expr e, Value & v) Expr fun, arg; if (matchCall(e, fun, arg)) { eval(env, fun, v); - - if (v.type == tPrimOp || v.type == tPrimOpApp) { - unsigned int argsLeft = - v.type == tPrimOp ? v.primOp.arity : v.primOpApp.argsLeft; - if (argsLeft == 1) { - /* We have all the arguments, so call the primop. - First find the primop. */ - Value * primOp = &v; - while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left; - assert(primOp->type == tPrimOp); - unsigned int arity = primOp->primOp.arity; - - Value vLastArg; - mkThunk(vLastArg, env, arg); - - /* Put all the arguments in an array. */ - Value * vArgs[arity]; - unsigned int n = arity - 1; - vArgs[n--] = &vLastArg; - for (Value * arg = &v; arg->type == tPrimOpApp; arg = arg->primOpApp.left) - vArgs[n--] = arg->primOpApp.right; - - /* And call the primop. */ - primOp->primOp.fun(*this, vArgs, v); - } else { - Value * v2 = allocValues(2); - v2[0] = v; - mkThunk(v2[1], env, arg); - v.type = tPrimOpApp; - v.primOpApp.left = &v2[0]; - v.primOpApp.right = &v2[1]; - v.primOpApp.argsLeft = argsLeft - 1; - } - return; - } - - if (v.type != tLambda) throw TypeError("expected function"); - - Env & env2(allocEnv()); - env2.up = &env; - - ATermList formals; ATerm ellipsis; - - if (matchVarPat(v.lambda.pat, name)) { - Value & vArg = env2.bindings[name]; - nrValues++; - mkThunk(vArg, env, arg); - } - - else if (matchAttrsPat(v.lambda.pat, formals, ellipsis, name)) { - Value * vArg; - Value vArg_; - - if (name == sNoAlias) - vArg = &vArg_; - else { - vArg = &env2.bindings[name]; - nrValues++; - } - - eval(env, arg, *vArg); - forceAttrs(*vArg); - - /* For each formal argument, get the actual argument. If - there is no matching actual argument but the formal - argument has a default, use the default. */ - unsigned int attrsUsed = 0; - for (ATermIterator i(formals); i; ++i) { - Expr def; Sym name; - DefaultValue def2; - if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ - - Bindings::iterator j = vArg->attrs->find(name); - - Value & v = env2.bindings[name]; - nrValues++; - - if (j == vArg->attrs->end()) { - if (!matchDefaultValue(def2, def)) def = 0; - if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") - % aterm2String(name)); - mkThunk(v, env2, def); - } else { - attrsUsed++; - v.type = tCopy; - v.val = &j->second; - } - } - - /* Check that each actual argument is listed as a formal - argument (unless the attribute match specifies a - `...'). TODO: show the names of the - expected/unexpected arguments. */ - if (ellipsis == eFalse && attrsUsed != vArg->attrs->size()) - throw TypeError("function called with unexpected argument"); - } - - else abort(); - - eval(env2, v.lambda.body, v); + Value vArg; + mkThunk(vArg, env, arg); // !!! should this be on the heap? + callFunction(v, vArg, v); return; } @@ -519,6 +422,103 @@ void EvalState::eval(Env & env, Expr e, Value & v) } +void EvalState::callFunction(Value & fun, Value & arg, Value & v) +{ + if (fun.type == tPrimOp || fun.type == tPrimOpApp) { + unsigned int argsLeft = + fun.type == tPrimOp ? fun.primOp.arity : fun.primOpApp.argsLeft; + if (argsLeft == 1) { + /* We have all the arguments, so call the primop. First + find the primop. */ + Value * primOp = &fun; + while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left; + assert(primOp->type == tPrimOp); + unsigned int arity = primOp->primOp.arity; + + /* Put all the arguments in an array. */ + Value * vArgs[arity]; + unsigned int n = arity - 1; + vArgs[n--] = &arg; + for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left) + vArgs[n--] = arg->primOpApp.right; + + /* And call the primop. */ + primOp->primOp.fun(*this, vArgs, v); + } else { + Value * v2 = allocValues(2); + v2[0] = fun; + v2[1] = arg; + v.type = tPrimOpApp; + v.primOpApp.left = &v2[0]; + v.primOpApp.right = &v2[1]; + v.primOpApp.argsLeft = argsLeft - 1; + } + return; + } + + if (fun.type != tLambda) + throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%", + showType(fun)); + + Env & env2(allocEnv()); + env2.up = fun.lambda.env; + + ATermList formals; ATerm ellipsis, name; + + if (matchVarPat(fun.lambda.pat, name)) { + Value & vArg = env2.bindings[name]; + nrValues++; + vArg = arg; + } + + else if (matchAttrsPat(fun.lambda.pat, formals, ellipsis, name)) { + forceAttrs(arg); + + if (name != sNoAlias) { + env2.bindings[name] = arg; + nrValues++; + } + + /* For each formal argument, get the actual argument. If + there is no matching actual argument but the formal + argument has a default, use the default. */ + unsigned int attrsUsed = 0; + for (ATermIterator i(formals); i; ++i) { + Expr def; Sym name; + DefaultValue def2; + if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ + + Bindings::iterator j = arg.attrs->find(name); + + Value & v = env2.bindings[name]; + nrValues++; + + if (j == arg.attrs->end()) { + if (!matchDefaultValue(def2, def)) def = 0; + if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") + % aterm2String(name)); + mkThunk(v, env2, def); + } else { + attrsUsed++; + v.type = tCopy; + v.val = &j->second; + } + } + + /* Check that each actual argument is listed as a formal + argument (unless the attribute match specifies a `...'). + TODO: show the names of the expected/unexpected + arguments. */ + if (ellipsis == eFalse && attrsUsed != arg.attrs->size()) + throw TypeError("function called with unexpected argument"); + } + + else abort(); + + eval(env2, fun.lambda.body, v); +} + + void EvalState::eval(Expr e, Value & v) { eval(baseEnv, e, v); @@ -567,6 +567,8 @@ void EvalState::forceValue(Value & v) forceValue(*v.val); v = *v.val; } + else if (v.type == tApp) + callFunction(*v.app.left, *v.app.right, v); else if (v.type == tBlackhole) throw EvalError("infinite recursion encountered"); } @@ -597,6 +599,14 @@ void EvalState::forceList(Value & v) } +void EvalState::forceFunction(Value & v) +{ + forceValue(v); + if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp) + throw TypeError(format("value is %1% while a function was expected") % showType(v)); +} + + string EvalState::coerceToString(Value & v, PathSet & context, bool coerceMore, bool copyToStore) { diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 4706602d5..385c2d78d 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -36,6 +36,7 @@ typedef enum { tAttrs, tList, tThunk, + tApp, tLambda, tCopy, tBlackhole, @@ -68,6 +69,9 @@ struct Value Env * env; Expr expr; } thunk; + struct { + Value * left, * right; + } app; struct { Env * env; Pattern pat; @@ -161,13 +165,16 @@ struct EvalState void strictEval(Env & env, Expr e, Value & v); /* If `v' is a thunk, enter it and overwrite `v' with the result - of the evaluation of the thunk. Otherwise, this is a no-op. */ + of the evaluation of the thunk. If `v' is a delayed function + application, call the function and overwrite `v' with the + result. Otherwise, this is a no-op. */ void forceValue(Value & v); /* Force `v', and then verify that it has the expected type. */ int forceInt(Value & v); void forceAttrs(Value & v); void forceList(Value & v); + void forceFunction(Value & v); // either lambda or primop /* String coercion. Converts strings, paths and derivations to a string. If `coerceMore' is set, also converts nulls, integers, @@ -196,6 +203,10 @@ private: elements and attributes are compared recursively. */ bool eqValues(Value & v1, Value & v2); + void callFunction(Value & fun, Value & arg, Value & v); + +public: + /* Allocation primitives. */ Value * allocValues(unsigned int count); Env & allocEnv(); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 2815567e5..bf8271b13 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -913,22 +913,28 @@ static Expr prim_tail(EvalState & state, const ATermVector & args) throw Error("`tail' called on an empty list"); return makeList(ATgetNext(list)); } +#endif /* Apply a function to every element of a list. */ -static Expr prim_map(EvalState & state, const ATermVector & args) +static void prim_map(EvalState & state, Value * * args, Value & v) { - Expr fun = evalExpr(state, args[0]); - ATermList list = evalList(state, args[1]); + state.forceFunction(*args[0]); + state.forceList(*args[1]); - ATermList res = ATempty; - for (ATermIterator i(list); i; ++i) - res = ATinsert(res, makeCall(fun, *i)); + v.type = tList; + v.list.length = args[1]->list.length; + v.list.elems = state.allocValues(v.list.length); - return makeList(ATreverse(res)); + for (unsigned int n = 0; n < v.list.length; ++n) { + v.list.elems[n].type = tApp; + v.list.elems[n].app.left = args[0]; + v.list.elems[n].app.right = &args[1]->list.elems[n]; + } } +#if 0 /* Return the length of a list. This is an O(1) time operation. */ static Expr prim_length(EvalState & state, const ATermVector & args) { @@ -1189,7 +1195,9 @@ void EvalState::createBaseEnv() addPrimOp("__head", 1, prim_head); #if 0 addPrimOp("__tail", 1, prim_tail); +#endif addPrimOp("map", 2, prim_map); +#if 0 addPrimOp("__length", 1, prim_length); #endif From c3aa615a5f4a8bdde5d836ce91d5daa743ded065 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Mar 2010 14:39:27 +0000 Subject: [PATCH 026/103] * More primops. --- src/libexpr/eval-test.cc | 6 ++- src/libexpr/eval.cc | 32 ++++++++--- src/libexpr/eval.hh | 4 ++ src/libexpr/primops.cc | 111 +++++++++------------------------------ 4 files changed, 56 insertions(+), 97 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 0b20883a3..e4e70c38c 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -56,10 +56,12 @@ void run(Strings args) doTest("{ x = [ 1 2 ]; } == { x = [ 1 ] ++ [ 2 ]; }"); doTest("1 != 1"); doTest("true"); + doTest("builtins.true"); doTest("true == false"); doTest("__head [ 1 2 3 ]"); doTest("__add 1 2"); doTest("null"); + doTest("null"); doTest("\"foo\""); doTest("let s = \"bar\"; in \"foo${s}\""); doTest("if true then 1 else 2"); @@ -67,9 +69,9 @@ void run(Strings args) doTest("if false || true then 1 else 2"); doTest("let x = x; in if true || x then 1 else 2"); doTest("/etc/passwd"); - doTest("import ./foo.nix"); + //doTest("import ./foo.nix"); doTest("map (x: __add 1 x) [ 1 2 3 ]"); - doTest("map (__add 1) [ 1 2 3 ]"); + doTest("map (builtins.add 1) [ 1 2 3 ]"); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f1437e465..06ca01ff4 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -95,14 +95,26 @@ EvalState::EvalState() : baseEnv(allocEnv()) } +void EvalState::addConstant(const string & name, Value & v) +{ + baseEnv.bindings[toATerm(name)] = v; + string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; + (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm(name2)] = v; + nrValues += 2; +} + + void EvalState::addPrimOp(const string & name, unsigned int arity, PrimOp primOp) { - Value & v = baseEnv.bindings[toATerm(name)]; - nrValues++; + Value v; v.type = tPrimOp; v.primOp.arity = arity; v.primOp.fun = primOp; + baseEnv.bindings[toATerm(name)] = v; + string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; + (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm(name2)] = v; + nrValues += 2; } @@ -212,6 +224,14 @@ Env & EvalState::allocEnv() } +void EvalState::mkList(Value & v, unsigned int length) +{ + v.type = tList; + v.list.length = length; + v.list.elems = allocValues(length); +} + + void EvalState::evalFile(const Path & path, Value & v) { startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path); @@ -349,9 +369,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) } if (matchList(e, es)) { - v.type = tList; - v.list.length = ATgetLength(es); - v.list.elems = allocValues(v.list.length); + 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; @@ -376,9 +394,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) forceList(v1); Value v2; eval(env, e2, v2); forceList(v2); - v.type = tList; - v.list.length = v1.list.length + v2.list.length; - v.list.elems = allocValues(v.list.length); + mkList(v, v1.list.length + v2.list.length); /* !!! This loses sharing with the original lists. We could use a tCopy node, but that would use more memory. */ for (unsigned int n = 0; n < v1.list.length; ++n) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 385c2d78d..5e7be5fcd 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -196,6 +196,8 @@ private: void createBaseEnv(); + void addConstant(const string & name, Value & v); + void addPrimOp(const string & name, unsigned int arity, PrimOp primOp); @@ -210,6 +212,8 @@ public: /* Allocation primitives. */ Value * allocValues(unsigned int count); Env & allocEnv(); + + void mkList(Value & v, unsigned int length); }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index bf8271b13..17ea8ee61 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -20,63 +20,6 @@ namespace nix { -#if 0 -/************************************************************* - * Constants - *************************************************************/ - - -static Expr prim_builtins(EvalState & state, const ATermVector & args) -{ - /* Return an attribute set containing all primops. This allows - Nix expressions to test for new primops and take appropriate - action if they're not available. For instance, rather than - calling a primop `foo' directly, they could say `if builtins ? - foo then builtins.foo ... else ...'. */ - - ATermMap builtins(state.primOps.size()); - - for (ATermMap::const_iterator i = state.primOps.begin(); - i != state.primOps.end(); ++i) - { - string name = aterm2String(i->key); - if (string(name, 0, 2) == "__") - name = string(name, 2); - /* !!! should use makePrimOp here, I guess. */ - builtins.set(toATerm(name), makeAttrRHS(makeVar(i->key), makeNoPos())); - } - - return makeAttrs(builtins); -} - - -/* Boolean constructors. */ -static Expr prim_true(EvalState & state, const ATermVector & args) -{ - return eTrue; -} - - -static Expr prim_false(EvalState & state, const ATermVector & args) -{ - return eFalse; -} - - -/* Return the null value. */ -static Expr prim_null(EvalState & state, const ATermVector & args) -{ - return makeNull(); -} - - -static Expr prim_currentTime(EvalState & state, const ATermVector & args) -{ - return ATmake("Int()", time(0)); -} -#endif - - /************************************************************* * Miscellaneous *************************************************************/ @@ -903,17 +846,17 @@ static void prim_head(EvalState & state, Value * * args, Value & v) } -#if 0 /* Return a list consisting of everything but the the first element of a list. */ -static Expr prim_tail(EvalState & state, const ATermVector & args) +static void prim_tail(EvalState & state, Value * * args, Value & v) { - ATermList list = evalList(state, args[0]); - if (ATisEmpty(list)) + state.forceList(*args[0]); + if (args[0]->list.length == 0) throw Error("`tail' called on an empty list"); - return makeList(ATgetNext(list)); + state.mkList(v, args[0]->list.length - 1); + for (unsigned int n = 0; n < v.list.length; ++n) + v.list.elems[n] = args[0]->list.elems[n + 1]; } -#endif /* Apply a function to every element of a list. */ @@ -922,9 +865,7 @@ static void prim_map(EvalState & state, Value * * args, Value & v) state.forceFunction(*args[0]); state.forceList(*args[1]); - v.type = tList; - v.list.length = args[1]->list.length; - v.list.elems = state.allocValues(v.list.length); + state.mkList(v, args[1]->list.length); for (unsigned int n = 0; n < v.list.length; ++n) { v.list.elems[n].type = tApp; @@ -1121,28 +1062,26 @@ void EvalState::createBaseEnv() v.type = tAttrs; v.attrs = new Bindings; } - - /* Add global constants such as `true' to the base environment. */ - { Value & v = baseEnv.bindings[toATerm("true")]; - mkBool(v, true); - } - { Value & v = baseEnv.bindings[toATerm("false")]; - mkBool(v, false); - } - { Value & v = baseEnv.bindings[toATerm("null")]; - v.type = tNull; - } - { Value & v = (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm("currentSystem")]; - mkString(v, strdup(thisSystem.c_str())); - } -#if 0 - // Constants - addPrimOp("__currentSystem", 0, prim_currentSystem); - addPrimOp("__currentTime", 0, prim_currentTime); + /* Add global constants such as `true' to the base environment. */ + Value v; + + mkBool(v, true); + addConstant("true", v); + + mkBool(v, false); + addConstant("false", v); + + v.type = tNull; + addConstant("null", v); + + mkInt(v, time(0)); + addConstant("__currentTime", v); + + mkString(v, strdup(thisSystem.c_str())); + addConstant("__currentSystem", v); // Miscellaneous -#endif addPrimOp("import", 1, prim_import); #if 0 addPrimOp("isNull", 1, prim_isNull); @@ -1193,9 +1132,7 @@ void EvalState::createBaseEnv() addPrimOp("__isList", 1, prim_isList); #endif addPrimOp("__head", 1, prim_head); -#if 0 addPrimOp("__tail", 1, prim_tail); -#endif addPrimOp("map", 2, prim_map); #if 0 addPrimOp("__length", 1, prim_length); From c9170be2bd2e735b37db7e7b4ccaca86835cb5c5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Mar 2010 15:18:20 +0000 Subject: [PATCH 027/103] * More primops. --- src/libexpr/eval.cc | 160 +++++-------------------- src/libexpr/eval.hh | 10 +- src/libexpr/primops.cc | 104 ++++++++-------- src/nix-instantiate/nix-instantiate.cc | 2 +- 4 files changed, 92 insertions(+), 184 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 06ca01ff4..34c420339 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -405,21 +405,35 @@ void EvalState::eval(Env & env, Expr e, Value & v) } if (matchConcatStrings(e, es)) { - unsigned int n = ATgetLength(es), j = 0; - Value vs[n]; - unsigned int len = 0; - for (ATermIterator i(es); i; ++i, ++j) { - eval(env, *i, vs[j]); - if (vs[j].type != tString) throw TypeError("string expected"); - len += strlen(vs[j].string.s); + PathSet context; + std::ostringstream s; + + bool first = true, isPath; + + for (ATermIterator i(es); i; ++i) { + eval(env, *i, v); + + /* If the first element is a path, then the result will + also be a path, we don't copy anything (yet - that's + done later, since paths are copied when they are used + in a derivation), and none of the strings are allowed + to have contexts. */ + if (first) { + isPath = v.type == tPath; + first = false; + } + + s << coerceToString(v, context, false, !isPath); } - char * s = new char[len + 1], * t = s; - for (unsigned int i = 0; i < j; ++i) { - strcpy(t, vs[i].string.s); - t += strlen(vs[i].string.s); - } - *t = 0; - mkString(v, s); + + if (isPath && !context.empty()) + throw EvalError(format("a string that refers to a store path cannot be appended to a path, in `%1%'") + % s.str()); + + if (isPath) + mkPath(v, strdup(s.str().c_str())); + else + mkString(v, strdup(s.str().c_str())); // !!! context return; } @@ -969,116 +983,6 @@ ATermList flattenList(EvalState & state, Expr e) } -string coerceToString(EvalState & state, Expr e, PathSet & context, - bool coerceMore, bool copyToStore) -{ - e = evalExpr(state, e); - - string s; - - if (matchStr(e, s, context)) return s; - - ATerm s2; - if (matchPath(e, s2)) { - Path path(canonPath(aterm2String(s2))); - - if (!copyToStore) return path; - - if (isDerivation(path)) - throw EvalError(format("file names are not allowed to end in `%1%'") - % drvExtension); - - Path dstPath; - if (state.srcToStore[path] != "") - dstPath = state.srcToStore[path]; - else { - dstPath = readOnlyMode - ? computeStorePathForPath(path).first - : store->addToStore(path); - state.srcToStore[path] = dstPath; - printMsg(lvlChatty, format("copied source `%1%' -> `%2%'") - % path % dstPath); - } - - context.insert(dstPath); - return dstPath; - } - - ATermList es; - if (matchAttrs(e, es)) { - Expr e2 = queryAttr(e, "outPath"); - if (!e2) throwTypeError("cannot coerce an attribute set (except a derivation) to a string"); - return coerceToString(state, e2, context, coerceMore, copyToStore); - } - - if (coerceMore) { - - /* Note that `false' is represented as an empty string for - shell scripting convenience, just like `null'. */ - if (e == eTrue) return "1"; - if (e == eFalse) return ""; - int n; - if (matchInt(e, n)) return int2String(n); - if (matchNull(e)) return ""; - - if (matchList(e, es)) { - string result; - es = flattenList(state, e); - bool first = true; - for (ATermIterator i(es); i; ++i) { - if (!first) result += " "; else first = false; - result += coerceToString(state, *i, - context, coerceMore, copyToStore); - } - return result; - } - } - - throwTypeError("cannot coerce %1% to a string", showType(e)); -} - - -/* Common implementation of `+', ConcatStrings and `~'. */ -static ATerm concatStrings(EvalState & state, ATermVector & args, - string separator = "") -{ - if (args.empty()) return makeStr("", PathSet()); - - PathSet context; - std::ostringstream s; - - /* If the first element is a path, then the result will also be a - path, we don't copy anything (yet - that's done later, since - paths are copied when they are used in a derivation), and none - of the strings are allowed to have contexts. */ - ATerm dummy; - args.front() = evalExpr(state, args.front()); - bool isPath = matchPath(args.front(), dummy); - - for (ATermVector::const_iterator i = args.begin(); i != args.end(); ++i) { - if (i != args.begin()) s << separator; - s << coerceToString(state, *i, context, false, !isPath); - } - - if (isPath && !context.empty()) - throw EvalError(format("a string that refers to a store path cannot be appended to a path, in `%1%'") - % s.str()); - - return isPath - ? makePath(toATerm(s.str())) - : makeStr(s.str(), context); -} - - -Path coerceToPath(EvalState & state, Expr e, PathSet & context) -{ - string path = coerceToString(state, e, context, false, false); - if (path == "" || path[0] != '/') - throw EvalError(format("string `%1%' doesn't represent an absolute path") % path); - return path; -} - - Expr autoCallFunction(Expr e, const ATermMap & args) { Pattern pat; @@ -1515,16 +1419,16 @@ Expr strictEvalExpr(EvalState & state, Expr e) #endif -void printEvalStats(EvalState & state) +void EvalState::printStats() { char x; bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0"; printMsg(showStats ? lvlInfo : lvlDebug, format("evaluated %1% expressions, used %2% bytes of stack space, allocated %3% values, allocated %4% environments") - % state.nrEvaluated + % nrEvaluated % (&x - deepestStack) - % state.nrValues - % state.nrEnvs); + % nrValues + % nrEnvs); if (showStats) printATermMapStats(); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 5e7be5fcd..28ec2e398 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -132,7 +132,7 @@ struct EvalState; std::ostream & operator << (std::ostream & str, Value & v); -struct EvalState +class EvalState { DrvRoots drvRoots; DrvHashes drvHashes; /* normalised derivation hashes */ @@ -144,6 +144,8 @@ struct EvalState bool allowUnsafeEquality; +public: + EvalState(); /* Evaluate an expression read from the given file to normal @@ -214,6 +216,9 @@ public: Env & allocEnv(); void mkList(Value & v, unsigned int length); + + /* Print statistics. */ + void printStats(); }; @@ -244,9 +249,6 @@ ATermList flattenList(EvalState & state, Expr e); Expr autoCallFunction(Expr e, const ATermMap & args); #endif -/* Print statistics. */ -void printEvalStats(EvalState & state); - } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 17ea8ee61..bb1a85498 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -47,14 +47,14 @@ static void prim_import(EvalState & state, Value * * args, Value & v) #if 0 /* Determine whether the argument is the null value. */ -static Expr prim_isNull(EvalState & state, const ATermVector & args) +static void prim_isNull(EvalState & state, Value * * args, Value & v) { return makeBool(matchNull(evalExpr(state, args[0]))); } /* Determine whether the argument is a function. */ -static Expr prim_isFunction(EvalState & state, const ATermVector & args) +static void prim_isFunction(EvalState & state, Value * * args, Value & v) { Expr e = evalExpr(state, args[0]); Pattern pat; @@ -63,14 +63,14 @@ static Expr prim_isFunction(EvalState & state, const ATermVector & args) } /* Determine whether the argument is an Int. */ -static Expr prim_isInt(EvalState & state, const ATermVector & args) +static void prim_isInt(EvalState & state, Value * * args, Value & v) { int i; return makeBool(matchInt(evalExpr(state, args[0]), i)); } /* Determine whether the argument is an String. */ -static Expr prim_isString(EvalState & state, const ATermVector & args) +static void prim_isString(EvalState & state, Value * * args, Value & v) { string s; PathSet l; @@ -78,13 +78,13 @@ static Expr prim_isString(EvalState & state, const ATermVector & args) } /* Determine whether the argument is an Bool. */ -static Expr prim_isBool(EvalState & state, const ATermVector & args) +static void prim_isBool(EvalState & state, Value * * args, Value & v) { ATermBool b; return makeBool(matchBool(evalExpr(state, args[0]), b)); } -static Expr prim_genericClosure(EvalState & state, const ATermVector & args) +static void prim_genericClosure(EvalState & state, Value * * args, Value & v) { startNest(nest, lvlDebug, "finding dependencies"); @@ -132,7 +132,7 @@ static Expr prim_genericClosure(EvalState & state, const ATermVector & args) } -static Expr prim_abort(EvalState & state, const ATermVector & args) +static void prim_abort(EvalState & state, Value * * args, Value & v) { PathSet context; throw Abort(format("evaluation aborted with the following error message: `%1%'") % @@ -140,7 +140,7 @@ static Expr prim_abort(EvalState & state, const ATermVector & args) } -static Expr prim_throw(EvalState & state, const ATermVector & args) +static void prim_throw(EvalState & state, Value * * args, Value & v) { PathSet context; throw ThrownError(format("user-thrown exception: %1%") % @@ -148,7 +148,7 @@ static Expr prim_throw(EvalState & state, const ATermVector & args) } -static Expr prim_addErrorContext(EvalState & state, const ATermVector & args) +static void prim_addErrorContext(EvalState & state, Value * * args, Value & v) { PathSet context; try { @@ -162,7 +162,7 @@ static Expr prim_addErrorContext(EvalState & state, const ATermVector & args) /* Try evaluating the argument. Success => {success=true; value=something;}, * else => {success=false; value=false;} */ -static Expr prim_tryEval(EvalState & state, const ATermVector & args) +static void prim_tryEval(EvalState & state, Value * * args, Value & v) { ATermMap res = ATermMap(); try { @@ -179,7 +179,7 @@ static Expr prim_tryEval(EvalState & state, const ATermVector & args) /* Return an environment variable. Use with care. */ -static Expr prim_getEnv(EvalState & state, const ATermVector & args) +static void prim_getEnv(EvalState & state, Value * * args, Value & v) { string name = evalStringNoCtx(state, args[0]); return makeStr(getEnv(name)); @@ -190,7 +190,7 @@ static Expr prim_getEnv(EvalState & state, const ATermVector & args) on standard error. Then return the second expression. Useful for debugging. */ -static Expr prim_trace(EvalState & state, const ATermVector & args) +static void prim_trace(EvalState & state, Value * * args, Value & v) { Expr e = evalExpr(state, args[0]); string s; @@ -272,7 +272,7 @@ static Hash hashDerivationModulo(EvalState & state, Derivation drv) derivation; `drvPath' containing the path of the Nix expression; and `type' set to `derivation' to indicate that this is a derivation. */ -static Expr prim_derivationStrict(EvalState & state, const ATermVector & args) +static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) { startNest(nest, lvlVomit, "evaluating derivation"); @@ -470,7 +470,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args) } -static Expr prim_derivationLazy(EvalState & state, const ATermVector & args) +static void prim_derivationLazy(EvalState & state, Value * * args, Value & v) { Expr eAttrs = evalExpr(state, args[0]); ATermMap attrs; @@ -496,7 +496,7 @@ static Expr prim_derivationLazy(EvalState & state, const ATermVector & args) /* Convert the argument to a path. !!! obsolete? */ -static Expr prim_toPath(EvalState & state, const ATermVector & args) +static void prim_toPath(EvalState & state, Value * * args, Value & v) { PathSet context; string path = coerceToPath(state, args[0], context); @@ -512,7 +512,7 @@ static Expr prim_toPath(EvalState & state, const ATermVector & args) /nix/store/newhash-oldhash-oldname. In the past, `toPath' had special case behaviour for store paths, but that created weird corner cases. */ -static Expr prim_storePath(EvalState & state, const ATermVector & args) +static void prim_storePath(EvalState & state, Value * * args, Value & v) { PathSet context; Path path = canonPath(coerceToPath(state, args[0], context)); @@ -526,7 +526,7 @@ static Expr prim_storePath(EvalState & state, const ATermVector & args) } -static Expr prim_pathExists(EvalState & state, const ATermVector & args) +static void prim_pathExists(EvalState & state, Value * * args, Value & v) { PathSet context; Path path = coerceToPath(state, args[0], context); @@ -538,7 +538,7 @@ static Expr prim_pathExists(EvalState & state, const ATermVector & args) /* Return the base name of the given string, i.e., everything following the last slash. */ -static Expr prim_baseNameOf(EvalState & state, const ATermVector & args) +static void prim_baseNameOf(EvalState & state, Value * * args, Value & v) { PathSet context; return makeStr(baseNameOf(coerceToString(state, args[0], context)), context); @@ -548,7 +548,7 @@ static Expr prim_baseNameOf(EvalState & state, const ATermVector & args) /* Return the directory of the given path, i.e., everything before the last slash. Return either a path or a string depending on the type of the argument. */ -static Expr prim_dirOf(EvalState & state, const ATermVector & args) +static void prim_dirOf(EvalState & state, Value * * args, Value & v) { PathSet context; Expr e = evalExpr(state, args[0]); ATerm dummy; @@ -559,7 +559,7 @@ static Expr prim_dirOf(EvalState & state, const ATermVector & args) /* Return the contents of a file as a string. */ -static Expr prim_readFile(EvalState & state, const ATermVector & args) +static void prim_readFile(EvalState & state, Value * * args, Value & v) { PathSet context; Path path = coerceToPath(state, args[0], context); @@ -577,7 +577,7 @@ static Expr prim_readFile(EvalState & state, const ATermVector & args) /* Convert the argument (which can be any Nix expression) to an XML representation returned in a string. Not all Nix expressions can be sensibly or completely represented (e.g., functions). */ -static Expr prim_toXML(EvalState & state, const ATermVector & args) +static void prim_toXML(EvalState & state, Value * * args, Value & v) { std::ostringstream out; PathSet context; @@ -588,7 +588,7 @@ static Expr prim_toXML(EvalState & state, const ATermVector & args) /* Store a string in the Nix store as a source file that can be used as an input by derivations. */ -static Expr prim_toFile(EvalState & state, const ATermVector & args) +static void prim_toFile(EvalState & state, Value * * args, Value & v) { PathSet context; string name = evalStringNoCtx(state, args[0]); @@ -647,7 +647,7 @@ struct FilterFromExpr : PathFilter }; -static Expr prim_filterSource(EvalState & state, const ATermVector & args) +static void prim_filterSource(EvalState & state, Value * * args, Value & v) { PathSet context; Path path = coerceToPath(state, args[1], context); @@ -671,7 +671,7 @@ static Expr prim_filterSource(EvalState & state, const ATermVector & args) /* Return the names of the attributes in an attribute set as a sorted list of strings. */ -static Expr prim_attrNames(EvalState & state, const ATermVector & args) +static void prim_attrNames(EvalState & state, Value * * args, Value & v) { ATermMap attrs; queryAllAttrs(evalExpr(state, args[0]), attrs); @@ -690,7 +690,7 @@ static Expr prim_attrNames(EvalState & state, const ATermVector & args) /* Dynamic version of the `.' operator. */ -static Expr prim_getAttr(EvalState & state, const ATermVector & args) +static void prim_getAttr(EvalState & state, Value * * args, Value & v) { string attr = evalStringNoCtx(state, args[0]); return evalExpr(state, makeSelect(args[1], toATerm(attr))); @@ -698,7 +698,7 @@ static Expr prim_getAttr(EvalState & state, const ATermVector & args) /* Dynamic version of the `?' operator. */ -static Expr prim_hasAttr(EvalState & state, const ATermVector & args) +static void prim_hasAttr(EvalState & state, Value * * args, Value & v) { string attr = evalStringNoCtx(state, args[0]); return evalExpr(state, makeOpHasAttr(args[1], toATerm(attr))); @@ -709,7 +709,7 @@ static Expr prim_hasAttr(EvalState & state, const ATermVector & args) pairs. To be precise, a list [{name = "name1"; value = value1;} ... {name = "nameN"; value = valueN;}] is transformed to {name1 = value1; ... nameN = valueN;}. */ -static Expr prim_listToAttrs(EvalState & state, const ATermVector & args) +static void prim_listToAttrs(EvalState & state, Value * * args, Value & v) { try { ATermMap res = ATermMap(); @@ -739,7 +739,7 @@ static Expr prim_listToAttrs(EvalState & state, const ATermVector & args) } -static Expr prim_removeAttrs(EvalState & state, const ATermVector & args) +static void prim_removeAttrs(EvalState & state, Value * * args, Value & v) { ATermMap attrs; queryAllAttrs(evalExpr(state, args[0]), attrs, true); @@ -755,7 +755,7 @@ static Expr prim_removeAttrs(EvalState & state, const ATermVector & args) /* Determine whether the argument is an attribute set. */ -static Expr prim_isAttrs(EvalState & state, const ATermVector & args) +static void prim_isAttrs(EvalState & state, Value * * args, Value & v) { ATermList list; return makeBool(matchAttrs(evalExpr(state, args[0]), list)); @@ -765,7 +765,7 @@ static Expr prim_isAttrs(EvalState & state, const ATermVector & args) /* Return the right-biased intersection of two attribute sets as1 and as2, i.e. a set that contains every attribute from as2 that is also a member of as1. */ -static Expr prim_intersectAttrs(EvalState & state, const ATermVector & args) +static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v) { ATermMap as1, as2; queryAllAttrs(evalExpr(state, args[0]), as1, true); @@ -807,7 +807,7 @@ static void attrsInPattern(ATermMap & map, Pattern pat) functionArgs (x: ...) => { } */ -static Expr prim_functionArgs(EvalState & state, const ATermVector & args) +static void prim_functionArgs(EvalState & state, Value * * args, Value & v) { Expr f = evalExpr(state, args[0]); ATerm pat, body, pos; @@ -827,7 +827,7 @@ static Expr prim_functionArgs(EvalState & state, const ATermVector & args) /* Determine whether the argument is a list. */ -static Expr prim_isList(EvalState & state, const ATermVector & args) +static void prim_isList(EvalState & state, Value * * args, Value & v) { ATermList list; return makeBool(matchList(evalExpr(state, args[0]), list)); @@ -877,7 +877,7 @@ static void prim_map(EvalState & state, Value * * args, Value & v) #if 0 /* Return the length of a list. This is an O(1) time operation. */ -static Expr prim_length(EvalState & state, const ATermVector & args) +static void prim_length(EvalState & state, Value * * args, Value & v) { ATermList list = evalList(state, args[0]); return makeInt(ATgetLength(list)); @@ -897,7 +897,7 @@ static void prim_add(EvalState & state, Value * * args, Value & v) #if 0 -static Expr prim_sub(EvalState & state, const ATermVector & args) +static void prim_sub(EvalState & state, Value * * args, Value & v) { int i1 = evalInt(state, args[0]); int i2 = evalInt(state, args[1]); @@ -905,7 +905,7 @@ static Expr prim_sub(EvalState & state, const ATermVector & args) } -static Expr prim_mul(EvalState & state, const ATermVector & args) +static void prim_mul(EvalState & state, Value * * args, Value & v) { int i1 = evalInt(state, args[0]); int i2 = evalInt(state, args[1]); @@ -913,20 +913,19 @@ static Expr prim_mul(EvalState & state, const ATermVector & args) } -static Expr prim_div(EvalState & state, const ATermVector & args) +static void prim_div(EvalState & state, Value * * args, Value & v) { int i1 = evalInt(state, args[0]); int i2 = evalInt(state, args[1]); if (i2 == 0) throw EvalError("division by zero"); return makeInt(i1 / i2); } +#endif -static Expr prim_lessThan(EvalState & state, const ATermVector & args) +static void prim_lessThan(EvalState & state, Value * * args, Value & v) { - int i1 = evalInt(state, args[0]); - int i2 = evalInt(state, args[1]); - return makeBool(i1 < i2); + mkBool(v, state.forceInt(*args[0]) < state.forceInt(*args[1])); } @@ -938,19 +937,20 @@ static Expr prim_lessThan(EvalState & state, const ATermVector & args) /* Convert the argument to a string. Paths are *not* copied to the store, so `toString /foo/bar' yields `"/foo/bar"', not `"/nix/store/whatever..."'. */ -static Expr prim_toString(EvalState & state, const ATermVector & args) +static void prim_toString(EvalState & state, Value * * args, Value & v) { PathSet context; - string s = coerceToString(state, args[0], context, true, false); - return makeStr(s, context); + string s = state.coerceToString(*args[0], context, true, false); + mkString(v, strdup(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 Expr prim_substring(EvalState & state, const ATermVector & args) +static void prim_substring(EvalState & state, Value * * args, Value & v) { int start = evalInt(state, args[0]); int len = evalInt(state, args[1]); @@ -963,7 +963,7 @@ static Expr prim_substring(EvalState & state, const ATermVector & args) } -static Expr prim_stringLength(EvalState & state, const ATermVector & args) +static void prim_stringLength(EvalState & state, Value * * args, Value & v) { PathSet context; string s = coerceToString(state, args[0], context); @@ -971,7 +971,7 @@ static Expr prim_stringLength(EvalState & state, const ATermVector & args) } -static Expr prim_unsafeDiscardStringContext(EvalState & state, const ATermVector & args) +static void prim_unsafeDiscardStringContext(EvalState & state, Value * * args, Value & v) { PathSet context; string s = coerceToString(state, args[0], context); @@ -985,7 +985,7 @@ static Expr prim_unsafeDiscardStringContext(EvalState & state, const ATermVector source-only deployment). This primop marks the string context so that builtins.derivation adds the path to drv.inputSrcs rather than drv.inputDrvs. */ -static Expr prim_unsafeDiscardOutputDependency(EvalState & state, const ATermVector & args) +static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args, Value & v) { PathSet context; string s = coerceToString(state, args[0], context); @@ -1004,14 +1004,14 @@ static Expr prim_unsafeDiscardOutputDependency(EvalState & state, const ATermVec /* Expression serialization/deserialization */ -static Expr prim_exprToString(EvalState & state, const ATermVector & args) +static void prim_exprToString(EvalState & state, Value * * args, Value & v) { /* !!! this disregards context */ return makeStr(atPrint(evalExpr(state, args[0]))); } -static Expr prim_stringToExpr(EvalState & state, const ATermVector & args) +static void prim_stringToExpr(EvalState & state, Value * * args, Value & v) { /* !!! this can introduce arbitrary garbage terms in the evaluator! */; @@ -1028,7 +1028,7 @@ static Expr prim_stringToExpr(EvalState & state, const ATermVector & args) *************************************************************/ -static Expr prim_parseDrvName(EvalState & state, const ATermVector & args) +static void prim_parseDrvName(EvalState & state, Value * * args, Value & v) { string name = evalStringNoCtx(state, args[0]); DrvName parsed(name); @@ -1039,7 +1039,7 @@ static Expr prim_parseDrvName(EvalState & state, const ATermVector & args) } -static Expr prim_compareVersions(EvalState & state, const ATermVector & args) +static void prim_compareVersions(EvalState & state, Value * * args, Value & v) { string version1 = evalStringNoCtx(state, args[0]); string version2 = evalStringNoCtx(state, args[1]); @@ -1144,10 +1144,12 @@ void EvalState::createBaseEnv() 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); addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 5473db4ac..0c4dc06e8 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -159,7 +159,7 @@ void run(Strings args) evalOnly, xmlOutput, e); } - printEvalStats(state); + state.printStats(); } From 47df476daa568af9f645b6a039c028e602a7e44b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Mar 2010 18:05:54 +0000 Subject: [PATCH 028/103] * More operators / primops. --- src/libexpr/eval-test.cc | 1 + src/libexpr/eval.cc | 248 +++++++++++-------------- src/libexpr/eval.hh | 18 +- src/libexpr/primops.cc | 55 +++--- src/nix-instantiate/nix-instantiate.cc | 10 +- 5 files changed, 145 insertions(+), 187 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index e4e70c38c..ff94e3ce1 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 34c420339..47a2b93b3 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 28ec2e398..87ab7733a 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 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 bb1a85498..e097a9284 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); diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 0c4dc06e8..86bb1841b 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -71,9 +71,13 @@ void processExpr(EvalState & state, const Strings & attrPaths, bool parseOnly, bool strict, const ATermMap & autoArgs, bool evalOnly, bool xmlOutput, Expr e) { - Value v; - state.strictEval(e, v); - std::cout << v << std::endl; + if (parseOnly) + std::cout << format("%1%\n") % canonicaliseExpr(e); + else { + Value v; + state.strictEval(e, v); + std::cout << v << std::endl; + } #if 0 for (Strings::const_iterator i = attrPaths.begin(); i != attrPaths.end(); ++i) { From 7f19e03c65693ae6a5eefc7e681b3003676d38eb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Mar 2010 22:39:48 +0000 Subject: [PATCH 029/103] * More primops. --- src/libexpr/eval.cc | 13 ++- src/libexpr/eval.hh | 3 +- src/libexpr/primops.cc | 184 ++++++++++++++++++++++------------------- 3 files changed, 109 insertions(+), 91 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 47a2b93b3..1365faf8c 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -254,6 +254,13 @@ void EvalState::mkList(Value & v, unsigned int length) } +void EvalState::mkAttrs(Value & v) +{ + v.type = tAttrs; + v.attrs = new Bindings; +} + + void EvalState::evalFile(const Path & path, Value & v) { startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path); @@ -308,8 +315,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) mkPath(v, ATgetName(ATgetAFun(s))); else if (matchAttrs(e, es)) { - v.type = tAttrs; - v.attrs = new Bindings; + mkAttrs(v); ATerm e2, pos; for (ATermIterator i(es); i; ++i) { if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ @@ -468,8 +474,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) /* Attribute set update (//). */ else if (matchOpUpdate(e, e1, e2)) { - v.type = tAttrs; - v.attrs = new Bindings; + mkAttrs(v); Value v2; eval(env, e2, v2); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 87ab7733a..198d936b9 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -105,7 +105,7 @@ static inline void mkBool(Value & v, bool b) void mkString(Value & v, const char * s); -void mkString(Value & v, const string & s, const PathSet & context); +void mkString(Value & v, const string & s, const PathSet & context = PathSet()); void mkPath(Value & v, const char * s); @@ -207,6 +207,7 @@ public: Env & allocEnv(); void mkList(Value & v, unsigned int length); + void mkAttrs(Value & v); /* Print statistics. */ void printStats(); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index e097a9284..4b7e37e61 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -45,45 +45,47 @@ static void prim_import(EvalState & state, Value * * args, Value & v) } -#if 0 /* Determine whether the argument is the null value. */ static void prim_isNull(EvalState & state, Value * * args, Value & v) { - return makeBool(matchNull(evalExpr(state, args[0]))); + state.forceValue(*args[0]); + mkBool(v, args[0]->type == tNull); } /* Determine whether the argument is a function. */ static void prim_isFunction(EvalState & state, Value * * args, Value & v) { - Expr e = evalExpr(state, args[0]); - Pattern pat; - ATerm body, pos; - return makeBool(matchFunction(e, pat, body, pos)); + state.forceValue(*args[0]); + mkBool(v, args[0]->type == tLambda); } + /* Determine whether the argument is an Int. */ static void prim_isInt(EvalState & state, Value * * args, Value & v) { - int i; - return makeBool(matchInt(evalExpr(state, args[0]), i)); + state.forceValue(*args[0]); + mkBool(v, args[0]->type == tInt); } + /* Determine whether the argument is an String. */ static void prim_isString(EvalState & state, Value * * args, Value & v) { - string s; - PathSet l; - return makeBool(matchStr(evalExpr(state, args[0]), s, l)); + state.forceValue(*args[0]); + mkBool(v, args[0]->type == tString); } + /* Determine whether the argument is an Bool. */ static void prim_isBool(EvalState & state, Value * * args, Value & v) { - ATermBool b; - return makeBool(matchBool(evalExpr(state, args[0]), b)); + state.forceValue(*args[0]); + mkBool(v, args[0]->type == tBool); } + +#if 0 static void prim_genericClosure(EvalState & state, Value * * args, Value & v) { startNest(nest, lvlDebug, "finding dependencies"); @@ -130,13 +132,14 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v) return makeList(res); } +#endif static void prim_abort(EvalState & state, Value * * args, Value & v) { PathSet context; throw Abort(format("evaluation aborted with the following error message: `%1%'") % - evalString(state, args[0], context)); + state.coerceToString(*args[0], context)); } @@ -144,10 +147,11 @@ static void prim_throw(EvalState & state, Value * * args, Value & v) { PathSet context; throw ThrownError(format("user-thrown exception: %1%") % - evalString(state, args[0], context)); + state.coerceToString(*args[0], context)); } +#if 0 static void prim_addErrorContext(EvalState & state, Value * * args, Value & v) { PathSet context; @@ -176,16 +180,18 @@ static void prim_tryEval(EvalState & state, Value * * args, Value & v) } return makeAttrs(res); } +#endif /* Return an environment variable. Use with care. */ static void prim_getEnv(EvalState & state, Value * * args, Value & v) { - string name = evalStringNoCtx(state, args[0]); - return makeStr(getEnv(name)); + string name = state.forceStringNoCtx(*args[0]); + mkString(v, getEnv(name)); } +#if 0 /* Evaluate the first expression, and print its abstract syntax tree on standard error. Then return the second expression. Useful for debugging. @@ -488,6 +494,7 @@ static void prim_derivationLazy(EvalState & state, Value * * args, Value & v) return makeAttrs(attrs); } +#endif /************************************************************* @@ -499,11 +506,12 @@ static void prim_derivationLazy(EvalState & state, Value * * args, Value & v) static void prim_toPath(EvalState & state, Value * * args, Value & v) { PathSet context; - string path = coerceToPath(state, args[0], context); - return makeStr(canonPath(path), context); + string path = state.coerceToPath(*args[0], context); + mkString(v, canonPath(path), context); } +#if 0 /* Allow a valid store path to be used in an expression. This is useful in some generated expressions such as in nix-push, which generates a call to a function with an already existing store path @@ -524,15 +532,16 @@ static void prim_storePath(EvalState & state, Value * * args, Value & v) context.insert(path2); return makeStr(path, context); } +#endif static void prim_pathExists(EvalState & state, Value * * args, Value & v) { PathSet context; - Path path = coerceToPath(state, args[0], context); + Path path = state.coerceToPath(*args[0], context); if (!context.empty()) throw EvalError(format("string `%1%' cannot refer to other paths") % path); - return makeBool(pathExists(path)); + mkBool(v, pathExists(path)); } @@ -541,7 +550,7 @@ static void prim_pathExists(EvalState & state, Value * * args, Value & v) static void prim_baseNameOf(EvalState & state, Value * * args, Value & v) { PathSet context; - return makeStr(baseNameOf(coerceToString(state, args[0], context)), context); + mkString(v, baseNameOf(state.coerceToString(*args[0], context)), context); } @@ -551,10 +560,8 @@ static void prim_baseNameOf(EvalState & state, Value * * args, Value & v) static void prim_dirOf(EvalState & state, Value * * args, Value & v) { PathSet context; - Expr e = evalExpr(state, args[0]); ATerm dummy; - bool isPath = matchPath(e, dummy); - Path dir = dirOf(coerceToPath(state, e, context)); - return isPath ? makePath(toATerm(dir)) : makeStr(dir, context); + Path dir = dirOf(state.coerceToPath(*args[0], context)); + if (args[0]->type == tPath) mkPath(v, dir.c_str()); else mkString(v, dir, context); } @@ -562,10 +569,10 @@ static void prim_dirOf(EvalState & state, Value * * args, Value & v) static void prim_readFile(EvalState & state, Value * * args, Value & v) { PathSet context; - Path path = coerceToPath(state, args[0], context); + Path path = state.coerceToPath(*args[0], context); if (!context.empty()) throw EvalError(format("string `%1%' cannot refer to other paths") % path); - return makeStr(readFile(path)); + mkString(v, readFile(path).c_str()); } @@ -574,6 +581,7 @@ static void prim_readFile(EvalState & state, Value * * args, Value & v) *************************************************************/ +#if 0 /* Convert the argument (which can be any Nix expression) to an XML representation returned in a string. Not all Nix expressions can be sensibly or completely represented (e.g., functions). */ @@ -662,6 +670,7 @@ static void prim_filterSource(EvalState & state, Value * * args, Value & v) return makeStr(dstPath, singleton(dstPath)); } +#endif /************************************************************* @@ -673,21 +682,18 @@ static void prim_filterSource(EvalState & state, Value * * args, Value & v) list of strings. */ static void prim_attrNames(EvalState & state, Value * * args, Value & v) { - ATermMap attrs; - queryAllAttrs(evalExpr(state, args[0]), attrs); + state.forceAttrs(*args[0]); + + state.mkList(v, args[0]->attrs->size()); StringSet names; - for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) - names.insert(aterm2String(i->key)); + foreach (Bindings::iterator, i, *args[0]->attrs) + names.insert(aterm2String(i->first)); - ATermList list = ATempty; - for (StringSet::const_reverse_iterator i = names.rbegin(); - i != names.rend(); ++i) - list = ATinsert(list, makeStr(*i, PathSet())); - - return makeList(list); + unsigned int n = 0; + foreach (StringSet::iterator, i, names) + mkString(v.list.elems[n++], *i); } -#endif /* Dynamic version of the `.' operator. */ @@ -712,6 +718,31 @@ static void prim_hasAttr(EvalState & state, Value * * args, Value & v) } +/* Determine whether the argument is an attribute set. */ +static void prim_isAttrs(EvalState & state, Value * * args, Value & v) +{ + state.forceValue(*args[0]); + mkBool(v, args[0]->type == tAttrs); +} + + +static void prim_removeAttrs(EvalState & state, Value * * args, Value & v) +{ + state.forceAttrs(*args[0]); + state.forceList(*args[1]); + + state.mkAttrs(v); + + foreach (Bindings::iterator, i, *args[0]->attrs) + (*v.attrs)[i->first] = i->second; + + for (unsigned int i = 0; i < args[1]->list.length; ++i) { + state.forceStringNoCtx(args[1]->list.elems[i]); + v.attrs->erase(toATerm(args[1]->list.elems[i].string.s)); + } +} + + #if 0 /* Builds an attribute set from a list specifying (name, value) pairs. To be precise, a list [{name = "name1"; value = value1;} @@ -745,31 +776,10 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v) throw; } } +#endif -static void prim_removeAttrs(EvalState & state, Value * * args, Value & v) -{ - ATermMap attrs; - queryAllAttrs(evalExpr(state, args[0]), attrs, true); - - ATermList list = evalList(state, args[1]); - - for (ATermIterator i(list); i; ++i) - /* It's not an error for *i not to exist. */ - attrs.remove(toATerm(evalStringNoCtx(state, *i))); - - return makeAttrs(attrs); -} - - -/* Determine whether the argument is an attribute set. */ -static void prim_isAttrs(EvalState & state, Value * * args, Value & v) -{ - ATermList list; - return makeBool(matchAttrs(evalExpr(state, args[0]), list)); -} - - +#if 0 /* Return the right-biased intersection of two attribute sets as1 and as2, i.e. a set that contains every attribute from as2 that is also a member of as1. */ @@ -827,6 +837,7 @@ static void prim_functionArgs(EvalState & state, Value * * args, Value & v) return makeAttrs(as); } +#endif /************************************************************* @@ -837,10 +848,9 @@ static void prim_functionArgs(EvalState & state, Value * * args, Value & v) /* Determine whether the argument is a list. */ static void prim_isList(EvalState & state, Value * * args, Value & v) { - ATermList list; - return makeBool(matchList(evalExpr(state, args[0]), list)); + state.forceValue(*args[0]); + mkBool(v, args[0]->type == tList); } -#endif /* Return the first element of a list. */ @@ -883,14 +893,12 @@ static void prim_map(EvalState & state, Value * * args, Value & v) } -#if 0 /* Return the length of a list. This is an O(1) time operation. */ static void prim_length(EvalState & state, Value * * args, Value & v) { - ATermList list = evalList(state, args[0]); - return makeInt(ATgetLength(list)); + state.forceList(*args[0]); + mkInt(v, v.list.length); } -#endif /************************************************************* @@ -1022,6 +1030,7 @@ static void prim_stringToExpr(EvalState & state, Value * * args, Value & v) throw EvalError("stringToExpr needs string argument!"); return ATreadFromString(s.c_str()); } +#endif /************************************************************* @@ -1031,23 +1040,20 @@ static void prim_stringToExpr(EvalState & state, Value * * args, Value & v) static void prim_parseDrvName(EvalState & state, Value * * args, Value & v) { - string name = evalStringNoCtx(state, args[0]); + string name = state.forceStringNoCtx(*args[0]); DrvName parsed(name); - ATermMap attrs(2); - attrs.set(toATerm("name"), makeAttrRHS(makeStr(parsed.name), makeNoPos())); - attrs.set(toATerm("version"), makeAttrRHS(makeStr(parsed.version), makeNoPos())); - return makeAttrs(attrs); + state.mkAttrs(v); + mkString((*v.attrs)[toATerm("name")], parsed.name); + mkString((*v.attrs)[toATerm("version")], parsed.version); } static void prim_compareVersions(EvalState & state, Value * * args, Value & v) { - string version1 = evalStringNoCtx(state, args[0]); - string version2 = evalStringNoCtx(state, args[1]); - int d = compareVersions(version1, version2); - return makeInt(d); + string version1 = state.forceStringNoCtx(*args[0]); + string version2 = state.forceStringNoCtx(*args[1]); + mkInt(v, compareVersions(version1, version2)); } -#endif /************************************************************* @@ -1084,18 +1090,22 @@ void EvalState::createBaseEnv() // Miscellaneous addPrimOp("import", 1, prim_import); -#if 0 addPrimOp("isNull", 1, prim_isNull); addPrimOp("__isFunction", 1, prim_isFunction); addPrimOp("__isString", 1, prim_isString); addPrimOp("__isInt", 1, prim_isInt); addPrimOp("__isBool", 1, prim_isBool); +#if 0 addPrimOp("__genericClosure", 1, prim_genericClosure); +#endif addPrimOp("abort", 1, prim_abort); addPrimOp("throw", 1, prim_throw); +#if 0 addPrimOp("__addErrorContext", 2, prim_addErrorContext); addPrimOp("__tryEval", 1, prim_tryEval); +#endif addPrimOp("__getEnv", 1, prim_getEnv); +#if 0 addPrimOp("__trace", 2, prim_trace); // Expr <-> String @@ -1105,41 +1115,43 @@ void EvalState::createBaseEnv() // Derivations addPrimOp("derivation!", 1, prim_derivationStrict); addPrimOp("derivation", 1, prim_derivationLazy); +#endif // Paths addPrimOp("__toPath", 1, prim_toPath); +#if 0 addPrimOp("__storePath", 1, prim_storePath); +#endif addPrimOp("__pathExists", 1, prim_pathExists); addPrimOp("baseNameOf", 1, prim_baseNameOf); addPrimOp("dirOf", 1, prim_dirOf); addPrimOp("__readFile", 1, prim_readFile); // Creating files +#if 0 addPrimOp("__toXML", 1, prim_toXML); addPrimOp("__toFile", 2, prim_toFile); addPrimOp("__filterSource", 2, prim_filterSource); +#endif // 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); +#if 0 addPrimOp("__listToAttrs", 1, prim_listToAttrs); addPrimOp("__intersectAttrs", 2, prim_intersectAttrs); addPrimOp("__functionArgs", 1, prim_functionArgs); +#endif // Lists addPrimOp("__isList", 1, prim_isList); -#endif addPrimOp("__head", 1, prim_head); addPrimOp("__tail", 1, prim_tail); addPrimOp("map", 2, prim_map); -#if 0 addPrimOp("__length", 1, prim_length); -#endif // Integer arithmetic addPrimOp("__add", 2, prim_add); @@ -1155,11 +1167,11 @@ void EvalState::createBaseEnv() #if 0 addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); +#endif // Versions addPrimOp("__parseDrvName", 1, prim_parseDrvName); addPrimOp("__compareVersions", 2, prim_compareVersions); -#endif } From 4c53ca2692f0a1325ec2d085ac3a6ee313b3cdb2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Mar 2010 09:54:12 +0000 Subject: [PATCH 030/103] * Compare nulls. --- src/libexpr/eval.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 1365faf8c..4624cbeaf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -417,7 +417,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) PathSet context; std::ostringstream s; - bool first = true, isPath; + bool first = true, isPath = false; for (ATermIterator i(es); i; ++i) { eval(env, *i, v); @@ -788,6 +788,9 @@ bool EvalState::eqValues(Value & v1, Value & v2) /* !!! contexts */ return strcmp(v1.string.s, v2.string.s) == 0; + case tNull: + return true; + case tList: if (v2.type != tList || v1.list.length != v2.list.length) return false; for (unsigned int n = 0; n < v1.list.length; ++n) @@ -803,7 +806,7 @@ bool EvalState::eqValues(Value & v1, Value & v2) } default: - throw Error("cannot compare given values"); + throw Error(format("cannot compare %1% with %2%") % showType(v1) % showType(v2)); } } From 13c2adc897b7f18e9a7784efbd46fabd995d2798 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Mar 2010 11:05:39 +0000 Subject: [PATCH 031/103] * Implemented `rec { inherit ...; }'. --- src/libexpr/eval-test.cc | 3 +++ src/libexpr/eval.cc | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index ff94e3ce1..3399aedc4 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -73,6 +73,9 @@ void run(Strings args) doTest("map (x: __add 1 x) [ 1 2 3 ]"); doTest("map (builtins.add 1) [ 1 2 3 ]"); doTest("builtins.hasAttr \"x\" { x = 1; }"); + doTest("let x = 1; as = rec { inherit x; y = as.x; }; in as.y"); + doTest("let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); + doTest("let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 4624cbeaf..7f0adb2c4 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -326,11 +326,16 @@ void EvalState::eval(Env & env, Expr e, Value & v) } else if (matchRec(e, rbnds, nrbnds)) { + /* Create a new environment that contains the attributes in + this `rec'. */ Env & env2(allocEnv()); env2.up = &env; v.type = tAttrs; v.attrs = &env2.bindings; + + /* The recursive attributes are evaluated in the new + environment. */ ATerm name, e2, pos; for (ATermIterator i(rbnds); i; ++i) { if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ @@ -338,6 +343,15 @@ void EvalState::eval(Env & env, Expr e, Value & v) nrValues++; mkThunk(v2, env2, e2); } + + /* The non-recursive attributes, on the other hand, are + evaluated in the original environment. */ + for (ATermIterator i(nrbnds); i; ++i) { + if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ + Value & v2 = env2.bindings[name]; + nrValues++; + mkThunk(v2, env, e2); + } } else if (matchSelect(e, e2, name)) { From f061086a93400c34f19eb54f0b34637af9f0d9cb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Mar 2010 13:35:29 +0000 Subject: [PATCH 032/103] * Fix the broken test for listToAttrs. --- tests/lang/eval-okay-listtoattrs.exp | 1 + ...{eval-okay-listToAttrs.nix => eval-okay-listtoattrs.nix} | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 tests/lang/eval-okay-listtoattrs.exp rename tests/lang/{eval-okay-listToAttrs.nix => eval-okay-listtoattrs.nix} (75%) diff --git a/tests/lang/eval-okay-listtoattrs.exp b/tests/lang/eval-okay-listtoattrs.exp new file mode 100644 index 000000000..49adcc16f --- /dev/null +++ b/tests/lang/eval-okay-listtoattrs.exp @@ -0,0 +1 @@ +Str("AA",[]) diff --git a/tests/lang/eval-okay-listToAttrs.nix b/tests/lang/eval-okay-listtoattrs.nix similarity index 75% rename from tests/lang/eval-okay-listToAttrs.nix rename to tests/lang/eval-okay-listtoattrs.nix index f5eae92cc..d5cd726b0 100644 --- a/tests/lang/eval-okay-listToAttrs.nix +++ b/tests/lang/eval-okay-listtoattrs.nix @@ -1,8 +1,10 @@ # this test shows how to use listToAttrs and that evaluation is still lazy (throw isn't called) +with import ./lib.nix; + let - asi = attr: value : { inherit attr value; }; + asi = name: value : { inherit name value; }; list = [ ( asi "a" "A" ) ( asi "b" "B" ) ]; a = builtins.listToAttrs list; b = builtins.listToAttrs ( list ++ list ); r = builtins.listToAttrs [ (asi "result" [ a b ]) ( asi "throw" (throw "this should not be thrown")) ]; -in r.result +in concat (map (x: x.a) r.result) From 51876789131e81dca9807c00773158160c3824c2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Mar 2010 15:14:23 +0000 Subject: [PATCH 033/103] --- tests/lang/eval-okay-listToAttrs.out | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tests/lang/eval-okay-listToAttrs.out diff --git a/tests/lang/eval-okay-listToAttrs.out b/tests/lang/eval-okay-listToAttrs.out deleted file mode 100644 index 4e779ff06..000000000 --- a/tests/lang/eval-okay-listToAttrs.out +++ /dev/null @@ -1 +0,0 @@ -List([Attrs([Bind("a",Str("A",[]),NoPos),Bind("b",Str("B",[]),NoPos)]),Attrs([Bind("a",Str("A",[]),NoPos),Bind("b",Str("B",[]),NoPos)])]) From 3d94be61ea562dea2098b6570f711386179913ef Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Mar 2010 15:38:03 +0000 Subject: [PATCH 034/103] * Implemented derivations. --- src/libexpr/eval.cc | 41 +++++-- src/libexpr/eval.hh | 9 +- src/libexpr/get-drvs.cc | 141 ++++++++++------------- src/libexpr/get-drvs.hh | 15 +-- src/libexpr/primops.cc | 150 +++++++++++-------------- src/nix-instantiate/nix-instantiate.cc | 19 +++- 6 files changed, 183 insertions(+), 192 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 7f0adb2c4..0296afe60 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -261,6 +261,14 @@ void EvalState::mkAttrs(Value & v) } +void EvalState::cloneAttrs(Value & src, Value & dst) +{ + mkAttrs(dst); + foreach (Bindings::iterator, i, *src.attrs) + (*dst.attrs)[i->first] = i->second; // !!! sharing? +} + + void EvalState::evalFile(const Path & path, Value & v) { startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path); @@ -488,17 +496,14 @@ void EvalState::eval(Env & env, Expr e, Value & v) /* Attribute set update (//). */ else if (matchOpUpdate(e, e1, e2)) { - mkAttrs(v); - Value v2; + eval(env, e1, v2); + + cloneAttrs(v2, v); + 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; + (*v.attrs)[i->first] = i->second; // !!! sharing } /* Attribute existence test (?). */ @@ -673,6 +678,15 @@ int EvalState::forceInt(Value & v) } +bool EvalState::forceBool(Value & v) +{ + forceValue(v); + if (v.type != tBool) + throw TypeError(format("value is %1% while a Boolean was expected") % showType(v)); + return v.boolean; +} + + void EvalState::forceAttrs(Value & v) { forceValue(v); @@ -697,15 +711,22 @@ void EvalState::forceFunction(Value & v) } -string EvalState::forceStringNoCtx(Value & v) +string EvalState::forceString(Value & v) { forceValue(v); if (v.type != tString) throw TypeError(format("value is %1% while a string was expected") % showType(v)); + return string(v.string.s); +} + + +string EvalState::forceStringNoCtx(Value & v) +{ + string s = forceString(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); + return s; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 198d936b9..eba97dd73 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -109,7 +109,6 @@ void mkString(Value & v, const string & s, const PathSet & context = PathSet()); void mkPath(Value & v, const char * s); -typedef std::map DrvRoots; typedef std::map DrvHashes; /* Cache for calls to addToStore(); maps source paths to the store @@ -124,8 +123,10 @@ std::ostream & operator << (std::ostream & str, Value & v); class EvalState { - DrvRoots drvRoots; +public: DrvHashes drvHashes; /* normalised derivation hashes */ + +private: SrcToStore srcToStore; unsigned long nrValues; @@ -164,9 +165,11 @@ public: /* Force `v', and then verify that it has the expected type. */ int forceInt(Value & v); + bool forceBool(Value & v); void forceAttrs(Value & v); void forceList(Value & v); void forceFunction(Value & v); // either lambda or primop + string forceString(Value & v); string forceStringNoCtx(Value & v); /* String coercion. Converts strings, paths and derivations to a @@ -209,6 +212,8 @@ public: void mkList(Value & v, unsigned int length); void mkAttrs(Value & v); + void cloneAttrs(Value & src, Value & dst); + /* Print statistics. */ void printStats(); }; diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 26ce6e71c..5ff77ff65 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -6,25 +6,18 @@ namespace nix { -#if 0 string DrvInfo::queryDrvPath(EvalState & state) const { if (drvPath == "") { - Expr a = attrs->get(toATerm("drvPath")); - - /* Backwards compatibility hack with user environments made by - Nix <= 0.10: these contain illegal Path("") expressions. */ - ATerm t; - if (a && matchPath(evalExpr(state, a), t)) - return aterm2String(t); - + Bindings::iterator i = attrs->find(toATerm("drvPath")); PathSet context; - (string &) drvPath = a ? coerceToPath(state, a, context) : ""; + (string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; } return drvPath; } +#if 0 string DrvInfo::queryOutPath(EvalState & state) const { if (outPath == "") { @@ -102,54 +95,47 @@ void DrvInfo::setMetaInfo(const MetaInfo & meta) } attrs->set(toATerm("meta"), makeAttrs(metaAttrs)); } +#endif -/* Cache for already evaluated derivations. Usually putting ATerms in - a STL container is unsafe (they're not scanning for GC roots), but - here it doesn't matter; everything in this set is reachable from - the stack as well. */ -typedef set Exprs; +/* Cache for already considered values. */ +typedef set Values; -/* Evaluate expression `e'. If it evaluates to an attribute set of - type `derivation', then put information about it in `drvs' (unless - it's already in `doneExprs'). The result boolean indicates whether - it makes sense for the caller to recursively search for derivations - in `e'. */ -static bool getDerivation(EvalState & state, Expr e, - const string & attrPath, DrvInfos & drvs, Exprs & doneExprs) +/* Evaluate value `v'. If it evaluates to an attribute set of type + `derivation', then put information about it in `drvs' (unless it's + already in `doneExprs'). The result boolean indicates whether it + makes sense for the caller to recursively search for derivations in + `v'. */ +static bool getDerivation(EvalState & state, Value & v, + const string & attrPath, DrvInfos & drvs, Values & doneValues) { try { - - ATermList es; - e = evalExpr(state, e); - if (!matchAttrs(e, es)) return true; + state.forceValue(v); + if (v.type != tAttrs) return true; - boost::shared_ptr attrs(new ATermMap()); - queryAllAttrs(e, *attrs, false); - - Expr a = attrs->get(toATerm("type")); - if (!a || evalStringNoCtx(state, a) != "derivation") return true; + Bindings::iterator i = v.attrs->find(toATerm("type")); + if (i == v.attrs->end() || state.forceStringNoCtx(i->second) != "derivation") return true; /* Remove spurious duplicates (e.g., an attribute set like `rec { x = derivation {...}; y = x;}'. */ - if (doneExprs.find(e) != doneExprs.end()) return false; - doneExprs.insert(e); + if (doneValues.find(&v) != doneValues.end()) return false; + doneValues.insert(&v); DrvInfo drv; - a = attrs->get(toATerm("name")); + i = v.attrs->find(toATerm("name")); /* !!! We really would like to have a decent back trace here. */ - if (!a) throw TypeError("derivation name missing"); - drv.name = evalStringNoCtx(state, a); + if (i == v.attrs->end()) throw TypeError("derivation name missing"); + drv.name = state.forceStringNoCtx(i->second); - a = attrs->get(toATerm("system")); - if (!a) + i = v.attrs->find(toATerm("system")); + if (i == v.attrs->end()) drv.system = "unknown"; else - drv.system = evalStringNoCtx(state, a); + drv.system = state.forceStringNoCtx(i->second); - drv.attrs = attrs; + drv.attrs = v.attrs; drv.attrPath = attrPath; @@ -162,11 +148,11 @@ static bool getDerivation(EvalState & state, Expr e, } -bool getDerivation(EvalState & state, Expr e, DrvInfo & drv) +bool getDerivation(EvalState & state, Value & v, DrvInfo & drv) { - Exprs doneExprs; + Values doneValues; DrvInfos drvs; - getDerivation(state, e, "", drvs, doneExprs); + getDerivation(state, v, "", drvs, doneValues); if (drvs.size() != 1) return false; drv = drvs.front(); return true; @@ -179,85 +165,72 @@ static string addToPath(const string & s1, const string & s2) } -static void getDerivations(EvalState & state, Expr e, +static void getDerivations(EvalState & state, Value & v, const string & pathPrefix, const ATermMap & autoArgs, - DrvInfos & drvs, Exprs & doneExprs) + DrvInfos & drvs, Values & doneValues) { - e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs)); - + // !!! autoCallFunction(evalExpr(state, e), autoArgs) + /* Process the expression. */ - ATermList es; DrvInfo drv; - if (!getDerivation(state, e, pathPrefix, drvs, doneExprs)) - return; + if (!getDerivation(state, v, pathPrefix, drvs, doneValues)) ; - if (matchAttrs(e, es)) { - ATermMap drvMap(ATgetLength(es)); - queryAllAttrs(e, drvMap); + else if (v.type == tAttrs) { /* !!! undocumented hackery to support combining channels in nix-env.cc. */ - bool combineChannels = drvMap.get(toATerm("_combineChannels")); + bool combineChannels = v.attrs->find(toATerm("_combineChannels")) != v.attrs->end(); /* Consider the attributes in sorted order to get more deterministic behaviour in nix-env operations (e.g. when there are names clashes between derivations, the derivation bound to the attribute with the "lower" name should take precedence). */ - typedef std::map AttrsSorted; - AttrsSorted attrsSorted; - foreach (ATermMap::const_iterator, i, drvMap) - attrsSorted[aterm2String(i->key)] = i->value; + StringSet attrs; + foreach (Bindings::iterator, i, *v.attrs) + attrs.insert(aterm2String(i->first)); - foreach (AttrsSorted::iterator, i, attrsSorted) { - startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first); - string pathPrefix2 = addToPath(pathPrefix, i->first); + foreach (StringSet::iterator, i, attrs) { + startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % *i); + string pathPrefix2 = addToPath(pathPrefix, *i); + Value & v2((*v.attrs)[toATerm(*i)]); if (combineChannels) - getDerivations(state, i->second, pathPrefix2, autoArgs, drvs, doneExprs); - else if (getDerivation(state, i->second, pathPrefix2, drvs, doneExprs)) { + getDerivations(state, v2, pathPrefix2, autoArgs, drvs, doneValues); + else if (getDerivation(state, v2, pathPrefix2, drvs, doneValues)) { /* If the value of this attribute is itself an attribute set, should we recurse into it? => Only if it has a `recurseForDerivations = true' attribute. */ - ATermList es; - Expr e = evalExpr(state, i->second), e2; - if (matchAttrs(e, es)) { - ATermMap attrs(ATgetLength(es)); - queryAllAttrs(e, attrs, false); - if (((e2 = attrs.get(toATerm("recurseForDerivations"))) - && evalBool(state, e2))) - getDerivations(state, e, pathPrefix2, autoArgs, drvs, doneExprs); + if (v2.type == tAttrs) { + Bindings::iterator j = v2.attrs->find(toATerm("recurseForDerivations")); + if (j != v2.attrs->end() && state.forceBool(j->second)) + getDerivations(state, v2, pathPrefix2, autoArgs, drvs, doneValues); } } } - - return; } - if (matchList(e, es)) { - int n = 0; - for (ATermIterator i(es); i; ++i, ++n) { + else if (v.type == tList) { + for (unsigned int n = 0; n < v.list.length; ++n) { startNest(nest, lvlDebug, format("evaluating list element")); string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); - if (getDerivation(state, *i, pathPrefix2, drvs, doneExprs)) - getDerivations(state, *i, pathPrefix2, autoArgs, drvs, doneExprs); + if (getDerivation(state, v.list.elems[n], pathPrefix2, drvs, doneValues)) + getDerivations(state, v.list.elems[n], pathPrefix2, autoArgs, drvs, doneValues); } - return; } - throw TypeError("expression does not evaluate to a derivation (or a set or list of those)"); + else throw TypeError("expression does not evaluate to a derivation (or a set or list of those)"); } -void getDerivations(EvalState & state, Expr e, const string & pathPrefix, +void getDerivations(EvalState & state, Value & v, const string & pathPrefix, const ATermMap & autoArgs, DrvInfos & drvs) { - Exprs doneExprs; - getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs); + Values doneValues; + getDerivations(state, v, pathPrefix, autoArgs, drvs, doneValues); } -#endif } diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index b56f54711..733f20201 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -35,10 +35,8 @@ public: string attrPath; /* path towards the derivation */ string system; - /* !!! these should really be hidden, and setMetaInfo() should - make a copy since the ATermMap can be shared between multiple - DrvInfos. */ - boost::shared_ptr attrs; + /* !!! make this private */ + Bindings * attrs; string queryDrvPath(EvalState & state) const; string queryOutPath(EvalState & state) const; @@ -62,12 +60,11 @@ public: typedef list DrvInfos; -/* Evaluate expression `e'. If it evaluates to a derivation, store - information about the derivation in `drv' and return true. - Otherwise, return false. */ -bool getDerivation(EvalState & state, Expr e, DrvInfo & drv); +/* If value `v' denotes a derivation, store information about the + derivation in `drv' and return true. Otherwise, return false. */ +bool getDerivation(EvalState & state, Value & v, DrvInfo & drv); -void getDerivations(EvalState & state, Expr e, const string & pathPrefix, +void getDerivations(EvalState & state, Value & v, const string & pathPrefix, const ATermMap & autoArgs, DrvInfos & drvs); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4b7e37e61..31914a65d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -207,6 +207,7 @@ static void prim_trace(EvalState & state, Value * * args, Value & v) printMsg(lvlError, format("trace: %1%") % e); return evalExpr(state, args[1]); } +#endif /************************************************************* @@ -282,24 +283,21 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) { startNest(nest, lvlVomit, "evaluating derivation"); - ATermMap attrs; - queryAllAttrs(evalExpr(state, args[0]), attrs, true); + state.forceAttrs(*args[0]); - /* Figure out the name already (for stack backtraces). */ - ATerm posDrvName; - Expr eDrvName = attrs.get(toATerm("name")); - if (!eDrvName) + /* Figure out the name first (for stack backtraces). */ + Bindings::iterator attr = args[0]->attrs->find(toATerm("name")); + if (attr == args[0]->attrs->end()) throw EvalError("required attribute `name' missing"); - if (!matchAttrRHS(eDrvName, eDrvName, posDrvName)) abort(); string drvName; try { - drvName = evalStringNoCtx(state, eDrvName); + drvName = state.forceStringNoCtx(attr->second); } catch (Error & e) { - e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n") - % showPos(posDrvName)); + e.addPrefix(format("while evaluating the derivation attribute `name' at :\n")); + // !!! % showPos(posDrvName)); throw; } - + /* Build the derivation expression by processing the attributes. */ Derivation drv; @@ -308,12 +306,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) string outputHash, outputHashAlgo; bool outputHashRecursive = false; - for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) { - string key = aterm2String(i->key); - ATerm value; - Expr pos; - ATerm rhs = i->value; - if (!matchAttrRHS(rhs, value, pos)) abort(); + foreach (Bindings::iterator, i, *args[0]->attrs) { + string key = aterm2String(i->first); startNest(nest, lvlVomit, format("processing attribute `%1%'") % key); try { @@ -321,15 +315,9 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) /* The `args' attribute is special: it supplies the command-line arguments to the builder. */ if (key == "args") { - ATermList es; - value = evalExpr(state, value); - if (!matchList(value, es)) { - static bool haveWarned = false; - warnOnce(haveWarned, "the `args' attribute should evaluate to a list"); - es = flattenList(state, value); - } - for (ATermIterator i(es); i; ++i) { - string s = coerceToString(state, *i, context, true); + state.forceList(i->second); + for (unsigned int n = 0; n < i->second.list.length; ++n) { + string s = state.coerceToString(i->second.list.elems[n], context, true); drv.args.push_back(s); } } @@ -337,7 +325,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) /* All other attributes are passed to the builder through the environment. */ else { - string s = coerceToString(state, value, context, true); + string s = state.coerceToString(i->second, context, true); drv.env[key] = s; if (key == "builder") drv.builder = s; else if (key == "system") drv.platform = s; @@ -352,13 +340,12 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) } } catch (Error & e) { - e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n") - % key % showPos(pos)); - e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n") - % drvName % showPos(posDrvName)); + e.addPrefix(format("while evaluating the derivation attribute `%1%' at :\n") + % key /* !!! % showPos(pos) */); + e.addPrefix(format("while instantiating the derivation named `%1%' at :\n") + % drvName /* !!! % showPos(posDrvName) */); throw; } - } /* Everything in the context of the strings in the derivation @@ -466,25 +453,25 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) state.drvHashes[drvPath] = hashDerivationModulo(state, drv); /* !!! assumes a single output */ - ATermMap outAttrs(2); - outAttrs.set(toATerm("outPath"), - makeAttrRHS(makeStr(outPath, singleton(drvPath)), makeNoPos())); - outAttrs.set(toATerm("drvPath"), - makeAttrRHS(makeStr(drvPath, singleton("=" + drvPath)), makeNoPos())); - - return makeAttrs(outAttrs); + //state.mkAttrs(v); + state.cloneAttrs(*args[0], v); + mkString((*v.attrs)[toATerm("outPath")], outPath, singleton(drvPath)); + mkString((*v.attrs)[toATerm("drvPath")], drvPath, singleton("=" + drvPath)); + mkString((*v.attrs)[toATerm("type")], "derivation"); // !!! remove } static void prim_derivationLazy(EvalState & state, Value * * args, Value & v) { - Expr eAttrs = evalExpr(state, args[0]); - ATermMap attrs; - queryAllAttrs(eAttrs, attrs, true); + state.forceAttrs(*args[0]); - attrs.set(toATerm("type"), - makeAttrRHS(makeStr("derivation"), makeNoPos())); + state.cloneAttrs(*args[0], v); + mkString((*v.attrs)[toATerm("type")], "derivation"); + + /* !!! */ + +#if 0 Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs); attrs.set(toATerm("outPath"), @@ -493,8 +480,8 @@ static void prim_derivationLazy(EvalState & state, Value * * args, Value & v) makeAttrRHS(makeSelect(drvStrict, toATerm("drvPath")), makeNoPos())); return makeAttrs(attrs); -} #endif +} /************************************************************* @@ -592,6 +579,7 @@ static void prim_toXML(EvalState & state, Value * * args, Value & v) printTermAsXML(strictEvalExpr(state, args[0]), out, context); return makeStr(out.str(), context); } +#endif /* Store a string in the Nix store as a source file that can be used @@ -599,12 +587,12 @@ static void prim_toXML(EvalState & state, Value * * args, Value & v) static void prim_toFile(EvalState & state, Value * * args, Value & v) { PathSet context; - string name = evalStringNoCtx(state, args[0]); - string contents = evalString(state, args[1], context); + string name = state.forceStringNoCtx(*args[0]); + string contents = state.forceString(*args[1]); // !!! context PathSet refs; - for (PathSet::iterator i = context.begin(); i != context.end(); ++i) { + foreach (PathSet::iterator, i, context) { Path path = *i; if (path.at(0) == '=') path = string(path, 1); if (isDerivation(path)) @@ -619,11 +607,12 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v) /* Note: we don't need to add `context' to the context of the result, since `storePath' itself has references to the paths used in args[1]. */ - - return makeStr(storePath, singleton(storePath)); + + mkString(v, storePath, singleton(storePath)); } +#if 0 struct FilterFromExpr : PathFilter { EvalState & state; @@ -731,10 +720,7 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v) state.forceAttrs(*args[0]); state.forceList(*args[1]); - state.mkAttrs(v); - - foreach (Bindings::iterator, i, *args[0]->attrs) - (*v.attrs)[i->first] = i->second; + state.cloneAttrs(*args[0], v); for (unsigned int i = 0; i < args[1]->list.length; ++i) { state.forceStringNoCtx(args[1]->list.elems[i]); @@ -743,40 +729,32 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v) } -#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 = value1; ... nameN = valueN;}. */ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v) { - try { - ATermMap res = ATermMap(); - ATermList list; - list = evalList(state, args[0]); - for (ATermIterator i(list); i; ++i){ - // *i should now contain a pointer to the list item expression - ATermList attrs; - Expr evaledExpr = evalExpr(state, *i); - if (matchAttrs(evaledExpr, attrs)){ - Expr e = evalExpr(state, makeSelect(evaledExpr, toATerm("name"))); - string attr = evalStringNoCtx(state,e); - Expr r = makeSelect(evaledExpr, toATerm("value")); - res.set(toATerm(attr), makeAttrRHS(r, makeNoPos())); - } - else - throw TypeError(format("list element in `listToAttrs' is %s, expected a set { name = \"\"; value = ; }") - % showType(evaledExpr)); - } - - return makeAttrs(res); - - } catch (Error & e) { - e.addPrefix(format("in `listToAttrs':\n")); - throw; + state.forceList(*args[0]); + + state.mkAttrs(v); + + for (unsigned int i = 0; i < args[0]->list.length; ++i) { + Value & v2(args[0]->list.elems[i]); + state.forceAttrs(v2); + + Bindings::iterator j = v2.attrs->find(toATerm("name")); + if (j == v2.attrs->end()) + throw TypeError("`name' attribute missing in a call to `listToAttrs'"); + string name = state.forceStringNoCtx(j->second); + + j = v2.attrs->find(toATerm("value")); + if (j == v2.attrs->end()) + throw TypeError("`value' attribute missing in a call to `listToAttrs'"); + + (*v.attrs)[toATerm(name)] = j->second; // !!! sharing? } } -#endif #if 0 @@ -897,7 +875,7 @@ static void prim_map(EvalState & state, Value * * args, Value & v) static void prim_length(EvalState & state, Value * * args, Value & v) { state.forceList(*args[0]); - mkInt(v, v.list.length); + mkInt(v, args[0]->list.length); } @@ -1111,11 +1089,11 @@ void EvalState::createBaseEnv() // Expr <-> String addPrimOp("__exprToString", 1, prim_exprToString); addPrimOp("__stringToExpr", 1, prim_stringToExpr); +#endif // Derivations - addPrimOp("derivation!", 1, prim_derivationStrict); - addPrimOp("derivation", 1, prim_derivationLazy); -#endif + addPrimOp("derivation", 1, prim_derivationStrict); + //addPrimOp("derivation", 1, prim_derivationLazy); // Paths addPrimOp("__toPath", 1, prim_toPath); @@ -1130,7 +1108,9 @@ void EvalState::createBaseEnv() // Creating files #if 0 addPrimOp("__toXML", 1, prim_toXML); +#endif addPrimOp("__toFile", 2, prim_toFile); +#if 0 addPrimOp("__filterSource", 2, prim_filterSource); #endif @@ -1140,8 +1120,8 @@ void EvalState::createBaseEnv() addPrimOp("__hasAttr", 2, prim_hasAttr); addPrimOp("__isAttrs", 1, prim_isAttrs); addPrimOp("removeAttrs", 2, prim_removeAttrs); -#if 0 addPrimOp("__listToAttrs", 1, prim_listToAttrs); +#if 0 addPrimOp("__intersectAttrs", 2, prim_intersectAttrs); addPrimOp("__functionArgs", 1, prim_functionArgs); #endif diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 86bb1841b..a71998de2 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -75,8 +75,23 @@ void processExpr(EvalState & state, const Strings & attrPaths, std::cout << format("%1%\n") % canonicaliseExpr(e); else { Value v; - state.strictEval(e, v); - std::cout << v << std::endl; + if (strict) state.strictEval(e, v); else state.eval(e, v); + if (evalOnly) + std::cout << v << std::endl; + else { + DrvInfos drvs; + getDerivations(state, v, "", autoArgs, drvs); + foreach (DrvInfos::iterator, i, drvs) { + Path drvPath = i->queryDrvPath(state); + if (gcRoot == "") + printGCWarning(); + else + drvPath = addPermRoot(drvPath, + makeRootName(gcRoot, rootNr), + indirectRoot); + std::cout << format("%1%\n") % drvPath; + } + } } #if 0 From 55e207b2dc43e426bd0dfbc2065b8853a1fc59b0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Mar 2010 16:14:32 +0000 Subject: [PATCH 035/103] * Cache parse trees to prevent repeated parsing of imported Nix expressions. --- src/libexpr/eval.cc | 9 ++++++++- src/libexpr/eval.hh | 2 ++ src/libexpr/parser.y | 6 ------ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0296afe60..b4156ffec 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -272,7 +272,14 @@ void EvalState::cloneAttrs(Value & src, Value & dst) void EvalState::evalFile(const Path & path, Value & v) { startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path); - Expr e = parseExprFromFile(*this, path); + + Expr e = parseTrees.get(toATerm(path)); + + if (!e) { + e = parseExprFromFile(*this, path); + parseTrees.set(toATerm(path), e); + } + try { eval(e, v); } catch (Error & e) { diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index eba97dd73..7369892fb 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -135,6 +135,8 @@ private: bool allowUnsafeEquality; + ATermMap parseTrees; + public: EvalState(); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 2f0c9db3f..3a56c2627 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -526,12 +526,6 @@ Expr parseExprFromFile(EvalState & state, Path path) { assert(path[0] == '/'); -#if 0 - /* Perhaps this is already an imploded parse tree? */ - Expr e = ATreadFromNamedFile(path.c_str()); - if (e) return e; -#endif - /* If `path' is a symlink, follow it. This is so that relative path references work. */ struct stat st; From d8cd3115d8e1acc9e866c67265668d5268f2c1ec Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Mar 2010 19:12:08 +0000 Subject: [PATCH 036/103] * Get nix-env to compile. --- src/libexpr/get-drvs.cc | 45 +++++++++++++++++------------------------ src/nix-env/nix-env.cc | 31 +++++++++++++++++----------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 5ff77ff65..93f559cac 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -17,14 +17,12 @@ string DrvInfo::queryDrvPath(EvalState & state) const } -#if 0 string DrvInfo::queryOutPath(EvalState & state) const { if (outPath == "") { - Expr a = attrs->get(toATerm("outPath")); - if (!a) throw TypeError("output path missing"); + Bindings::iterator i = attrs->find(toATerm("outPath")); PathSet context; - (string &) outPath = coerceToPath(state, a, context); + (string &) outPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; } return outPath; } @@ -34,33 +32,26 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const { MetaInfo meta; - Expr a = attrs->get(toATerm("meta")); - if (!a) return meta; /* fine, empty meta information */ + Bindings::iterator a = attrs->find(toATerm("meta")); + if (a == attrs->end()) return meta; /* fine, empty meta information */ - ATermMap attrs2; - queryAllAttrs(evalExpr(state, a), attrs2); + state.forceAttrs(a->second); - for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) { - Expr e = evalExpr(state, i->value); - string s; - PathSet context; + foreach (Bindings::iterator, i, *a->second.attrs) { MetaValue value; - int n; - ATermList es; - if (matchStr(e, s, context)) { + state.forceValue(i->second); + if (i->second.type == tString) { value.type = MetaValue::tpString; - value.stringValue = s; - meta[aterm2String(i->key)] = value; - } else if (matchInt(e, n)) { + value.stringValue = i->second.string.s; + } else if (i->second.type == tInt) { value.type = MetaValue::tpInt; - value.intValue = n; - meta[aterm2String(i->key)] = value; - } else if (matchList(e, es)) { + value.intValue = i->second.integer; + } else if (i->second.type == tList) { value.type = MetaValue::tpStrings; - for (ATermIterator j(es); j; ++j) - value.stringValues.push_back(evalStringNoCtx(state, *j)); - meta[aterm2String(i->key)] = value; - } + for (unsigned int j = 0; j < i->second.list.length; ++j) + value.stringValues.push_back(state.forceStringNoCtx(i->second.list.elems[j])); + } else continue; + meta[aterm2String(i->first)] = value; } return meta; @@ -76,6 +67,8 @@ MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const void DrvInfo::setMetaInfo(const MetaInfo & meta) { + throw Error("not implemented"); +#if 0 ATermMap metaAttrs; foreach (MetaInfo::const_iterator, i, meta) { Expr e; @@ -94,8 +87,8 @@ void DrvInfo::setMetaInfo(const MetaInfo & meta) metaAttrs.set(toATerm(i->first), makeAttrRHS(e, makeNoPos())); } attrs->set(toATerm("meta"), makeAttrs(metaAttrs)); -} #endif +} /* Cache for already considered values. */ diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 35caf687b..ea85656a2 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -164,9 +164,12 @@ static void loadDerivations(EvalState & state, Path nixExprPath, string systemFilter, const ATermMap & autoArgs, const string & pathPrefix, DrvInfos & elems) { - getDerivations(state, - findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath)), - pathPrefix, autoArgs, elems); + Value v; + state.eval(loadSourceExpr(state, nixExprPath), v); + + // !!! findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath)) + + getDerivations(state, v, pathPrefix, autoArgs, elems); /* Filter out all derivations not applicable to the current system. */ @@ -221,7 +224,7 @@ static DrvInfos queryInstalled(EvalState & state, const Path & userEnv) e = bottomupRewrite(addPos, e); DrvInfos elems; - getDerivations(state, e, "", ATermMap(1), elems); + // !!! getDerivations(state, e, "", ATermMap(1), elems); return elems; } @@ -255,6 +258,8 @@ static bool createUserEnv(EvalState & state, DrvInfos & elems, const Path & profile, bool keepDerivations, const string & lockToken) { + throw Error("not implemented"); +#if 0 /* Build the components in the user environment, if they don't exist already. */ PathSet drvsToBuild; @@ -355,6 +360,7 @@ static bool createUserEnv(EvalState & state, DrvInfos & elems, switchLink(profile, generation); return true; +#endif } @@ -518,12 +524,11 @@ static void queryInstSources(EvalState & state, Expr e1 = loadSourceExpr(state, instSource.nixExprPath); - for (Strings::const_iterator i = args.begin(); - i != args.end(); ++i) - { + foreach (Strings::const_iterator, i, args) { Expr e2 = parseExprFromString(state, *i, absPath(".")); Expr call = makeCall(e2, e1); - getDerivations(state, call, "", instSource.autoArgs, elems); + Value v; state.eval(call, v); + getDerivations(state, v, "", instSource.autoArgs, elems); } break; @@ -540,7 +545,7 @@ static void queryInstSources(EvalState & state, Path path = followLinksToStorePath(*i); DrvInfo elem; - elem.attrs = boost::shared_ptr(new ATermMap(0)); /* ugh... */ + elem.attrs = new Bindings; string name = baseNameOf(path); string::size_type dash = name.find('-'); if (dash != string::npos) @@ -574,12 +579,14 @@ static void queryInstSources(EvalState & state, } case srcAttrPath: { - for (Strings::const_iterator i = args.begin(); - i != args.end(); ++i) + throw Error("not implemented"); +#if 0 + foreach (Strings::const_iterator, i, args) getDerivations(state, findAlongAttrPath(state, *i, instSource.autoArgs, loadSourceExpr(state, instSource.nixExprPath)), "", instSource.autoArgs, elems); +#endif break; } } @@ -1472,7 +1479,7 @@ void run(Strings args) op(globals, remaining, opFlags, opArgs); - printEvalStats(globals.state); + globals.state.printStats(); } From 979f163615745db74f3a94a71818e66c75baf9ac Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Mar 2010 19:52:29 +0000 Subject: [PATCH 037/103] * Handle string contexts. `nix-instantiate' can now correctly compute the `firefoxWrapper' attribute in Nixpkgs, and it's about 3 times faster than the trunk :-) --- src/libexpr/eval.cc | 88 ++++++++++++------------------------------ src/libexpr/eval.hh | 1 + src/libexpr/primops.cc | 4 +- 3 files changed, 28 insertions(+), 65 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b4156ffec..6e504f879 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -178,7 +178,13 @@ void mkString(Value & v, const char * s) void mkString(Value & v, const string & s, const PathSet & context) { mkString(v, s.c_str()); - // !!! context + if (!context.empty()) { + unsigned int len = 0, n = 0; + v.string.context = new const char *[context.size() + 1]; + foreach (PathSet::const_iterator, i, context) + v.string.context[n++] = strdup(i->c_str()); + v.string.context[n] = 0; + } } @@ -471,7 +477,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) if (isPath) mkPath(v, s.str().c_str()); else - mkString(v, s.str().c_str()); // !!! context + mkString(v, s.str(), context); } /* Conditionals. */ @@ -727,6 +733,17 @@ string EvalState::forceString(Value & v) } +string EvalState::forceString(Value & v, PathSet & context) +{ + string s = forceString(v); + if (v.string.context) { + for (const char * * p = v.string.context; *p; ++p) + context.insert(*p); + } + return s; +} + + string EvalState::forceStringNoCtx(Value & v) { string s = forceString(v); @@ -744,7 +761,12 @@ string EvalState::coerceToString(Value & v, PathSet & context, string s; - if (v.type == tString) return v.string.s; + if (v.type == tString) { + if (v.string.context) + for (const char * * p = v.string.context; *p; ++p) + context.insert(*p); + return v.string.s; + } if (v.type == tPath) { Path path(canonPath(v.path)); @@ -1142,66 +1164,6 @@ LocalNoInline(Expr evalWith(EvalState & state, Expr defs, Expr body, ATerm pos)) } -LocalNoInline(Expr evalPlusConcat(EvalState & state, Expr e)) -{ - Expr e1, e2; - ATermList es; - - ATermVector args; - - if (matchOpPlus(e, e1, e2)) { - - /* !!! Awful compatibility hack for `drv + /path'. - According to regular concatenation, /path should be - copied to the store and its store path should be - appended to the string. However, in Nix <= 0.10, /path - was concatenated. So handle that case separately, but - do print out a warning. This code can go in Nix 0.12, - maybe. */ - e1 = evalExpr(state, e1); - e2 = evalExpr(state, e2); - - ATermList as; - ATerm p; - if (matchAttrs(e1, as) && matchPath(e2, p)) { - static bool haveWarned = false; - warnOnce(haveWarned, format( - "concatenation of a derivation and a path is deprecated; " - "you should write `drv + \"%1%\"' instead of `drv + %1%'") - % aterm2String(p)); - PathSet context; - return makeStr( - coerceToString(state, makeSelect(e1, toATerm("outPath")), context) - + aterm2String(p), context); - } - - args.push_back(e1); - args.push_back(e2); - } - - else if (matchConcatStrings(e, es)) - for (ATermIterator i(es); i; ++i) args.push_back(*i); - - try { - return concatStrings(state, args); - } catch (Error & e) { - addErrorPrefix(e, "in a string concatenation:\n"); - throw; - } -} - - -LocalNoInline(Expr evalSubPath(EvalState & state, Expr e1, Expr e2)) -{ - static bool haveWarned = false; - warnOnce(haveWarned, "the subpath operator (~) is deprecated, use string concatenation (+) instead"); - ATermVector args; - args.push_back(e1); - args.push_back(e2); - return concatStrings(state, args, "/"); -} - - /* 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 7369892fb..13ea269fc 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -172,6 +172,7 @@ public: void forceList(Value & v); void forceFunction(Value & v); // either lambda or primop string forceString(Value & v); + string forceString(Value & v, PathSet & context); string forceStringNoCtx(Value & v); /* String coercion. Converts strings, paths and derivations to a diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 31914a65d..e16cd2419 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -588,7 +588,7 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v) { PathSet context; string name = state.forceStringNoCtx(*args[0]); - string contents = state.forceString(*args[1]); // !!! context + string contents = state.forceString(*args[1], context); PathSet refs; @@ -928,7 +928,7 @@ static void prim_toString(EvalState & state, Value * * args, Value & v) { PathSet context; string s = state.coerceToString(*args[0], context, true, false); - mkString(v, s.c_str()); // !!! context + mkString(v, s, context); } From dc31305b381f69de5ac5fd4776df1a802045ff00 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Mar 2010 20:09:20 +0000 Subject: [PATCH 038/103] * Fixed the trace primop and path comparison. * Removed exprToString and stringToExpr because there is no ATerm representation to work on anymore (and exposing the internals of the evaluator like this is not a good idea anyway). --- src/libexpr/eval.cc | 3 +++ src/libexpr/primops.cc | 49 +++++++----------------------------------- 2 files changed, 11 insertions(+), 41 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 6e504f879..2bfcdac07 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -852,6 +852,9 @@ bool EvalState::eqValues(Value & v1, Value & v2) /* !!! contexts */ return strcmp(v1.string.s, v2.string.s) == 0; + case tPath: + return strcmp(v1.path, v2.path) == 0; + case tNull: return true; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index e16cd2419..65b736787 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -191,23 +191,18 @@ static void prim_getEnv(EvalState & state, Value * * args, Value & v) } -#if 0 -/* Evaluate the first expression, and print its abstract syntax tree - on standard error. Then return the second expression. Useful for - debugging. - */ +/* Evaluate the first expression and print it on standard error. Then + return the second expression. Useful for debugging. */ static void prim_trace(EvalState & state, Value * * args, Value & v) { - Expr e = evalExpr(state, args[0]); - string s; - PathSet context; - if (matchStr(e, s, context)) - printMsg(lvlError, format("trace: %1%") % s); + state.forceValue(*args[0]); + if (args[0]->type == tString) + printMsg(lvlError, format("trace: %1%") % args[0]->string.s); else - printMsg(lvlError, format("trace: %1%") % e); - return evalExpr(state, args[1]); + printMsg(lvlError, format("trace: %1%") % *args[0]); + state.forceValue(*args[1]); + v = *args[1]; } -#endif /************************************************************* @@ -986,28 +981,6 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args return makeStr(s, context2); } - - -/* Expression serialization/deserialization */ - - -static void prim_exprToString(EvalState & state, Value * * args, Value & v) -{ - /* !!! this disregards context */ - return makeStr(atPrint(evalExpr(state, args[0]))); -} - - -static void prim_stringToExpr(EvalState & state, Value * * args, Value & v) -{ - /* !!! this can introduce arbitrary garbage terms in the - evaluator! */; - string s; - PathSet l; - if (!matchStr(evalExpr(state, args[0]), s, l)) - throw EvalError("stringToExpr needs string argument!"); - return ATreadFromString(s.c_str()); -} #endif @@ -1083,13 +1056,7 @@ void EvalState::createBaseEnv() addPrimOp("__tryEval", 1, prim_tryEval); #endif addPrimOp("__getEnv", 1, prim_getEnv); -#if 0 addPrimOp("__trace", 2, prim_trace); - - // Expr <-> String - addPrimOp("__exprToString", 1, prim_exprToString); - addPrimOp("__stringToExpr", 1, prim_stringToExpr); -#endif // Derivations addPrimOp("derivation", 1, prim_derivationStrict); From 71f026292ba1b401237a16ab6e0fb57c36c93df5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Apr 2010 09:55:57 +0000 Subject: [PATCH 039/103] * Make `derivation' lazy again for performance. It also turns out that there are some places in Nixpkgs (php_configurable / composableDerivation, it seems) that call `derivation' with incorrect arguments (namely, the `name' attribute missing) but get away with it because of laziness. --- src/libexpr/eval.cc | 10 +--------- src/libexpr/eval.hh | 8 ++++++++ src/libexpr/primops.cc | 36 ++++++++---------------------------- 3 files changed, 17 insertions(+), 37 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2bfcdac07..4277c65ea 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -159,14 +159,6 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, } -static void mkThunk(Value & v, Env & env, Expr expr) -{ - v.type = tThunk; - v.thunk.env = &env; - v.thunk.expr = expr; -} - - void mkString(Value & v, const char * s) { v.type = tString; @@ -179,7 +171,7 @@ void mkString(Value & v, const string & s, const PathSet & context) { mkString(v, s.c_str()); if (!context.empty()) { - unsigned int len = 0, n = 0; + unsigned int n = 0; v.string.context = new const char *[context.size() + 1]; foreach (PathSet::const_iterator, i, context) v.string.context[n++] = strdup(i->c_str()); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 13ea269fc..34b658ce1 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -104,6 +104,14 @@ static inline void mkBool(Value & v, bool b) } +static inline void mkThunk(Value & v, Env & env, Expr expr) +{ + v.type = tThunk; + v.thunk.env = &env; + v.thunk.expr = expr; +} + + void mkString(Value & v, const char * s); void mkString(Value & v, const string & s, const PathSet & context = PathSet()); void mkPath(Value & v, const char * s); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 65b736787..98a31dc3a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -448,34 +448,9 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) state.drvHashes[drvPath] = hashDerivationModulo(state, drv); /* !!! assumes a single output */ - //state.mkAttrs(v); - state.cloneAttrs(*args[0], v); + state.mkAttrs(v); mkString((*v.attrs)[toATerm("outPath")], outPath, singleton(drvPath)); mkString((*v.attrs)[toATerm("drvPath")], drvPath, singleton("=" + drvPath)); - mkString((*v.attrs)[toATerm("type")], "derivation"); // !!! remove -} - - -static void prim_derivationLazy(EvalState & state, Value * * args, Value & v) -{ - state.forceAttrs(*args[0]); - - state.cloneAttrs(*args[0], v); - - mkString((*v.attrs)[toATerm("type")], "derivation"); - - /* !!! */ - -#if 0 - Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs); - - attrs.set(toATerm("outPath"), - makeAttrRHS(makeSelect(drvStrict, toATerm("outPath")), makeNoPos())); - attrs.set(toATerm("drvPath"), - makeAttrRHS(makeSelect(drvStrict, toATerm("drvPath")), makeNoPos())); - - return makeAttrs(attrs); -#endif } @@ -1039,6 +1014,12 @@ void EvalState::createBaseEnv() mkString(v, thisSystem.c_str()); addConstant("__currentSystem", v); + /* Add a wrapper around the derivation primop that computes the + `drvPath' and `outPath' attributes lazily. */ + string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }"; + mkThunk(v, baseEnv, parseExprFromString(*this, s, "/")); + addConstant("derivation", v); + // Miscellaneous addPrimOp("import", 1, prim_import); addPrimOp("isNull", 1, prim_isNull); @@ -1059,8 +1040,7 @@ void EvalState::createBaseEnv() addPrimOp("__trace", 2, prim_trace); // Derivations - addPrimOp("derivation", 1, prim_derivationStrict); - //addPrimOp("derivation", 1, prim_derivationLazy); + addPrimOp("derivationStrict", 1, prim_derivationStrict); // Paths addPrimOp("__toPath", 1, prim_toPath); From 95cc417d76f7d374ef63e0b49a2f83e7b9202b0c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Apr 2010 10:55:36 +0000 Subject: [PATCH 040/103] * Functions are incomparable. --- src/libexpr/eval.cc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 4277c65ea..53e57ff08 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -69,16 +69,17 @@ std::ostream & operator << (std::ostream & str, 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 tString: return "a string"; + case tPath: return "a path"; case tAttrs: return "an attribute set"; case tList: return "a list"; + case tNull: return "null"; + case tLambda: return "a function"; + case tPrimOp: return "a built-in function"; case tPrimOpApp: return "a partially applied built-in function"; - default: throw Error("unknown type"); + default: throw Error(format("unknown type: %1%") % v.type); } } @@ -299,7 +300,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) char x; if (&x < deepestStack) deepestStack = &x; - debug(format("eval: %1%") % e); + //debug(format("eval: %1%") % e); nrEvaluated++; @@ -864,6 +865,12 @@ bool EvalState::eqValues(Value & v1, Value & v2) return true; } + /* Functions are incomparable. */ + case tLambda: + case tPrimOp: + case tPrimOpApp: + return false; + default: throw Error(format("cannot compare %1% with %2%") % showType(v1) % showType(v2)); } From 7b851915bfbad1c561191a037a2924d2b3d2d398 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Apr 2010 12:04:57 +0000 Subject: [PATCH 041/103] * Improve sharing. --- src/libexpr/eval.cc | 5 ++--- src/libexpr/eval.hh | 7 +++++++ src/libexpr/get-drvs.cc | 32 ++++++++++++++++---------------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 53e57ff08..49aeb0036 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -264,7 +264,7 @@ void EvalState::cloneAttrs(Value & src, Value & dst) { mkAttrs(dst); foreach (Bindings::iterator, i, *src.attrs) - (*dst.attrs)[i->first] = i->second; // !!! sharing? + mkCopy((*dst.attrs)[i->first], i->second); } @@ -601,8 +601,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) mkThunk(v, env2, def); } else { attrsUsed++; - v.type = tCopy; - v.val = &j->second; + mkCopy(v, j->second); } } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 34b658ce1..ae6b2106f 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -112,6 +112,13 @@ static inline void mkThunk(Value & v, Env & env, Expr expr) } +static inline void mkCopy(Value & v, Value & src) +{ + v.type = tCopy; + v.val = &src; +} + + void mkString(Value & v, const char * s); void mkString(Value & v, const string & s, const PathSet & context = PathSet()); void mkPath(Value & v, const char * s); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 93f559cac..f02392bed 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -91,8 +91,8 @@ void DrvInfo::setMetaInfo(const MetaInfo & meta) } -/* Cache for already considered values. */ -typedef set Values; +/* Cache for already considered attrsets. */ +typedef set Done; /* Evaluate value `v'. If it evaluates to an attribute set of type @@ -101,7 +101,7 @@ typedef set Values; makes sense for the caller to recursively search for derivations in `v'. */ static bool getDerivation(EvalState & state, Value & v, - const string & attrPath, DrvInfos & drvs, Values & doneValues) + const string & attrPath, DrvInfos & drvs, Done & done) { try { state.forceValue(v); @@ -112,8 +112,8 @@ static bool getDerivation(EvalState & state, Value & v, /* Remove spurious duplicates (e.g., an attribute set like `rec { x = derivation {...}; y = x;}'. */ - if (doneValues.find(&v) != doneValues.end()) return false; - doneValues.insert(&v); + if (done.find(v.attrs) != done.end()) return false; + done.insert(v.attrs); DrvInfo drv; @@ -143,9 +143,9 @@ static bool getDerivation(EvalState & state, Value & v, bool getDerivation(EvalState & state, Value & v, DrvInfo & drv) { - Values doneValues; + Done done; DrvInfos drvs; - getDerivation(state, v, "", drvs, doneValues); + getDerivation(state, v, "", drvs, done); if (drvs.size() != 1) return false; drv = drvs.front(); return true; @@ -160,14 +160,14 @@ static string addToPath(const string & s1, const string & s2) static void getDerivations(EvalState & state, Value & v, const string & pathPrefix, const ATermMap & autoArgs, - DrvInfos & drvs, Values & doneValues) + DrvInfos & drvs, Done & done) { // !!! autoCallFunction(evalExpr(state, e), autoArgs) /* Process the expression. */ DrvInfo drv; - if (!getDerivation(state, v, pathPrefix, drvs, doneValues)) ; + if (!getDerivation(state, v, pathPrefix, drvs, done)) ; else if (v.type == tAttrs) { @@ -189,8 +189,8 @@ static void getDerivations(EvalState & state, Value & v, string pathPrefix2 = addToPath(pathPrefix, *i); Value & v2((*v.attrs)[toATerm(*i)]); if (combineChannels) - getDerivations(state, v2, pathPrefix2, autoArgs, drvs, doneValues); - else if (getDerivation(state, v2, pathPrefix2, drvs, doneValues)) { + getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); + else if (getDerivation(state, v2, pathPrefix2, drvs, done)) { /* If the value of this attribute is itself an attribute set, should we recurse into it? => Only if it has a `recurseForDerivations = true' @@ -198,7 +198,7 @@ static void getDerivations(EvalState & state, Value & v, if (v2.type == tAttrs) { Bindings::iterator j = v2.attrs->find(toATerm("recurseForDerivations")); if (j != v2.attrs->end() && state.forceBool(j->second)) - getDerivations(state, v2, pathPrefix2, autoArgs, drvs, doneValues); + getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); } } } @@ -209,8 +209,8 @@ static void getDerivations(EvalState & state, Value & v, startNest(nest, lvlDebug, format("evaluating list element")); string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); - if (getDerivation(state, v.list.elems[n], pathPrefix2, drvs, doneValues)) - getDerivations(state, v.list.elems[n], pathPrefix2, autoArgs, drvs, doneValues); + if (getDerivation(state, v.list.elems[n], pathPrefix2, drvs, done)) + getDerivations(state, v.list.elems[n], pathPrefix2, autoArgs, drvs, done); } } @@ -221,8 +221,8 @@ static void getDerivations(EvalState & state, Value & v, void getDerivations(EvalState & state, Value & v, const string & pathPrefix, const ATermMap & autoArgs, DrvInfos & drvs) { - Values doneValues; - getDerivations(state, v, pathPrefix, autoArgs, drvs, doneValues); + Done done; + getDerivations(state, v, pathPrefix, autoArgs, drvs, done); } From c172274e170a87a30420842ee07ed1f7226d7f2e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Apr 2010 14:35:03 +0000 Subject: [PATCH 042/103] * Quick hack to make coerceToString work more or less correctly on nested lists. `nix-instantiate' can now evaluate the NixOS system derivation attribute correctly (in 2.1s on my laptop vs. 6.2s for the trunk). --- src/libexpr/eval.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 49aeb0036..8c8bb219b 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -804,9 +804,12 @@ string EvalState::coerceToString(Value & v, PathSet & context, if (v.type == tList) { string result; for (unsigned int n = 0; n < v.list.length; ++n) { - if (n) result += " "; result += coerceToString(v.list.elems[n], context, coerceMore, copyToStore); + if (n < v.list.length - 1 + /* !!! not quite correct */ + && (v.list.elems[n].type != tList || v.list.elems[n].list.length != 0)) + result += " "; } return result; } From a5ece7d016e72a61ca69a401e833314f538518f9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Apr 2010 16:59:07 +0000 Subject: [PATCH 043/103] * Removed the `~' operator. --- src/libexpr/nixexpr-ast.def | 1 - src/libexpr/parser.y | 1 - tests/dependencies.nix | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def index b34466543..36c25dcfe 100644 --- a/src/libexpr/nixexpr-ast.def +++ b/src/libexpr/nixexpr-ast.def @@ -14,7 +14,6 @@ OpAnd | Expr Expr | Expr | OpOr | Expr Expr | Expr | OpImpl | Expr Expr | Expr | OpUpdate | Expr Expr | Expr | -SubPath | Expr Expr | Expr | OpHasAttr | Expr string | Expr | OpPlus | Expr Expr | Expr | OpConcat | Expr Expr | Expr | diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 3a56c2627..3463a5c5d 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -373,7 +373,6 @@ expr_op | expr_op OR expr_op { $$ = makeOpOr($1, $3); } | expr_op IMPL expr_op { $$ = makeOpImpl($1, $3); } | expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); } - | expr_op '~' expr_op { $$ = makeSubPath($1, $3); } | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); } | expr_op '+' expr_op { $$ = makeConcatStrings(ATmakeList2($1, $3)); } | expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); } diff --git a/tests/dependencies.nix b/tests/dependencies.nix index a397e1ce4..00717ae98 100644 --- a/tests/dependencies.nix +++ b/tests/dependencies.nix @@ -9,7 +9,7 @@ let { input2 = mkDerivation { name = "dependencies-input-2"; - builder = ./. ~ "dependencies.builder2.sh"; + builder = ./dependencies.builder2.sh; }; body = mkDerivation { From a353aef0b157e7c628fd18640bd6c45215f3e606 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 6 Apr 2010 14:15:29 +0000 Subject: [PATCH 044/103] * In eval(), don't use the target value `v' as a temporary. Overwriting `v' breaks when the expression evaluation to an assertion failure or throw. --- src/libexpr/eval.cc | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 8c8bb219b..5f6ab2655 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -369,10 +369,11 @@ void EvalState::eval(Env & env, Expr e, Value & v) } 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); - if (i == v.attrs->end()) + Value v2; + eval(env, e2, v2); + forceAttrs(v2); // !!! eval followed by force is slightly inefficient + Bindings::iterator i = v2.attrs->find(name); + if (i == v2.attrs->end()) throwEvalError("attribute `%1%' missing", aterm2String(name)); try { forceValue(i->second); @@ -391,10 +392,11 @@ void EvalState::eval(Env & env, Expr e, Value & v) } else if (matchCall(e, fun, arg)) { - eval(env, fun, v); + Value vFun; + eval(env, fun, vFun); Value vArg; mkThunk(vArg, env, arg); // !!! should this be on the heap? - callFunction(v, vArg, v); + callFunction(vFun, vArg, v); } else if (matchWith(e, attrs, body, pos)) { @@ -446,9 +448,10 @@ void EvalState::eval(Env & env, Expr e, Value & v) std::ostringstream s; bool first = true, isPath = false; + Value vStr; for (ATermIterator i(es); i; ++i) { - eval(env, *i, v); + eval(env, *i, vStr); /* If the first element is a path, then the result will also be a path, we don't copy anything (yet - that's @@ -456,11 +459,11 @@ void EvalState::eval(Env & env, Expr e, Value & v) in a derivation), and none of the strings are allowed to have contexts. */ if (first) { - isPath = v.type == tPath; + isPath = vStr.type == tPath; first = false; } - s << coerceToString(v, context, false, !isPath); + s << coerceToString(vStr, context, false, !isPath); } if (isPath && !context.empty()) @@ -514,9 +517,10 @@ void EvalState::eval(Env & env, Expr e, Value & v) /* Attribute existence test (?). */ else if (matchOpHasAttr(e, e1, name)) { - eval(env, e1, v); - forceAttrs(v); - mkBool(v, v.attrs->find(name) != v.attrs->end()); + Value vAttrs; + eval(env, e1, vAttrs); + forceAttrs(vAttrs); + mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end()); } else throw Error("unsupported term"); @@ -660,7 +664,7 @@ void EvalState::strictEval(Expr e, Value & v) void EvalState::forceValue(Value & v) { if (v.type == tThunk) { - v.type = tBlackhole; + //v.type = tBlackhole; eval(*v.thunk.env, v.thunk.expr, v); } else if (v.type == tCopy) { From fc92244ba81d884e099d467a3b82fbdcbff7fc40 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 7 Apr 2010 13:55:46 +0000 Subject: [PATCH 045/103] * Implemented the primops necessary for generating the NixOS manual. --- src/libexpr/eval-test.cc | 5 +- src/libexpr/eval.cc | 52 +++++---- src/libexpr/eval.hh | 21 ++-- src/libexpr/expr-to-xml.cc | 149 ++++++++++++------------- src/libexpr/expr-to-xml.hh | 5 +- src/libexpr/get-drvs.cc | 7 +- src/libexpr/primops.cc | 64 ++++++----- src/nix-instantiate/nix-instantiate.cc | 3 +- 8 files changed, 160 insertions(+), 146 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 3399aedc4..eac95094f 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -16,7 +16,8 @@ void doTest(string s) Expr e = parseExprFromString(state, s, absPath(".")); printMsg(lvlError, format(">>>>> %1%") % e); Value v; - state.strictEval(e, v); + state.eval(e, v); + state.strictForceValue(v); printMsg(lvlError, format("result: %1%") % v); } @@ -76,6 +77,8 @@ void run(Strings args) doTest("let x = 1; as = rec { inherit x; y = as.x; }; in as.y"); doTest("let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); doTest("let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); + doTest("builtins.toXML 123"); + doTest("builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 5f6ab2655..0d18c5522 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -302,6 +302,8 @@ void EvalState::eval(Env & env, Expr e, Value & v) //debug(format("eval: %1%") % e); + checkInterrupt(); + nrEvaluated++; Sym name; @@ -639,28 +641,6 @@ bool EvalState::evalBool(Env & env, Expr e) } -void EvalState::strictEval(Env & env, Expr e, Value & v) -{ - eval(env, e, v); - - if (v.type == tAttrs) { - foreach (Bindings::iterator, i, *v.attrs) - forceValue(i->second); - } - - else if (v.type == tList) { - for (unsigned int n = 0; n < v.list.length; ++n) - forceValue(v.list.elems[n]); - } -} - - -void EvalState::strictEval(Expr e, Value & v) -{ - strictEval(baseEnv, e, v); -} - - void EvalState::forceValue(Value & v) { if (v.type == tThunk) { @@ -678,6 +658,22 @@ void EvalState::forceValue(Value & v) } +void EvalState::strictForceValue(Value & v) +{ + forceValue(v); + + if (v.type == tAttrs) { + foreach (Bindings::iterator, i, *v.attrs) + strictForceValue(i->second); + } + + else if (v.type == tList) { + for (unsigned int n = 0; n < v.list.length; ++n) + strictForceValue(v.list.elems[n]); + } +} + + int EvalState::forceInt(Value & v) { forceValue(v); @@ -750,6 +746,14 @@ string EvalState::forceStringNoCtx(Value & v) } +bool EvalState::isDerivation(Value & v) +{ + if (v.type != tAttrs) return false; + Bindings::iterator i = v.attrs->find(toATerm("type")); + return i != v.attrs->end() && forceStringNoCtx(i->second) == "derivation"; +} + + string EvalState::coerceToString(Value & v, PathSet & context, bool coerceMore, bool copyToStore) { @@ -769,7 +773,7 @@ string EvalState::coerceToString(Value & v, PathSet & context, if (!copyToStore) return path; - if (isDerivation(path)) + if (nix::isDerivation(path)) throw EvalError(format("file names are not allowed to end in `%1%'") % drvExtension); @@ -1415,5 +1419,5 @@ void EvalState::printStats() printATermMapStats(); } - + } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index ae6b2106f..a42b9ebeb 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -169,17 +169,16 @@ public: type. */ bool evalBool(Env & env, Expr e); - /* Evaluate an expression, and recursively evaluate list elements - and attributes. */ - void strictEval(Expr e, Value & v); - void strictEval(Env & env, Expr e, Value & v); - /* If `v' is a thunk, enter it and overwrite `v' with the result of the evaluation of the thunk. If `v' is a delayed function application, call the function and overwrite `v' with the result. Otherwise, this is a no-op. */ void forceValue(Value & v); + /* Force a value, then recursively force list elements and + attributes. */ + void strictForceValue(Value & v); + /* Force `v', and then verify that it has the expected type. */ int forceInt(Value & v); bool forceBool(Value & v); @@ -190,6 +189,10 @@ public: string forceString(Value & v, PathSet & context); string forceStringNoCtx(Value & v); + /* Return true iff the value `v' denotes a derivation (i.e. a + set with attribute `type = "derivation"'). */ + bool isDerivation(Value & v); + /* String coercion. Converts strings, paths and derivations to a string. If `coerceMore' is set, also converts nulls, integers, booleans and lists to a string. If `copyToStore' is set, @@ -219,10 +222,10 @@ private: elements and attributes are compared recursively. */ bool eqValues(Value & v1, Value & v2); - void callFunction(Value & fun, Value & arg, Value & v); - public: + void callFunction(Value & fun, Value & arg, Value & v); + /* Allocation primitives. */ Value * allocValues(unsigned int count); Env & allocEnv(); @@ -237,6 +240,10 @@ public: }; +/* Return a string representing the type of the value `v'. */ +string showType(Value & v); + + #if 0 /* Evaluate an expression to normal form. */ Expr evalExpr(EvalState & state, Expr e); diff --git a/src/libexpr/expr-to-xml.cc b/src/libexpr/expr-to-xml.cc index 9b3062804..ad68739c3 100644 --- a/src/libexpr/expr-to-xml.cc +++ b/src/libexpr/expr-to-xml.cc @@ -18,24 +18,19 @@ static XMLAttrs singletonAttrs(const string & name, const string & value) } -/* set is safe because all the expressions are also reachable - from the stack, therefore can't be garbage-collected. */ -typedef set ExprSet; +static void printValueAsXML(EvalState & state, bool strict, Value & v, + XMLWriter & doc, PathSet & context, PathSet & drvsSeen); -static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context, - ExprSet & drvsSeen); - - -static void showAttrs(const ATermMap & attrs, XMLWriter & doc, - PathSet & context, ExprSet & drvsSeen) +static void showAttrs(EvalState & state, bool strict, Bindings & attrs, + XMLWriter & doc, PathSet & context, PathSet & drvsSeen) { StringSet names; - for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) - names.insert(aterm2String(i->key)); - for (StringSet::iterator i = names.begin(); i != names.end(); ++i) { + foreach (Bindings::iterator, i, attrs) + names.insert(aterm2String(i->first)); + foreach (StringSet::iterator, i, names) { XMLOpenElement _(doc, "attr", singletonAttrs("name", *i)); - printTermAsXML(attrs.get(toATerm(*i)), doc, context, drvsSeen); + printValueAsXML(state, strict, attrs[toATerm(*i)], doc, context, drvsSeen); } } @@ -61,91 +56,93 @@ static void printPatternAsXML(Pattern pat, XMLWriter & doc) } -static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context, - ExprSet & drvsSeen) +static void printValueAsXML(EvalState & state, bool strict, Value & v, + XMLWriter & doc, PathSet & context, PathSet & drvsSeen) { - XMLAttrs attrs; - string s; - ATerm s2; - int i; - ATermList as, es; - ATerm pat, body, pos; - checkInterrupt(); - if (matchStr(e, s, context)) /* !!! show the context? */ - doc.writeEmptyElement("string", singletonAttrs("value", s)); + if (strict) state.forceValue(v); + + switch (v.type) { - else if (matchPath(e, s2)) - doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s2))); + case tInt: + doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str())); + break; - else if (matchNull(e)) - doc.writeEmptyElement("null"); + case tBool: + doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false")); + break; - else if (matchInt(e, i)) - doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % i).str())); + case tString: + /* !!! show the context? */ + doc.writeEmptyElement("string", singletonAttrs("value", v.string.s)); + break; - else if (e == eTrue) - doc.writeEmptyElement("bool", singletonAttrs("value", "true")); + case tPath: + doc.writeEmptyElement("path", singletonAttrs("value", v.path)); + break; - else if (e == eFalse) - doc.writeEmptyElement("bool", singletonAttrs("value", "false")); + case tNull: + doc.writeEmptyElement("null"); + break; - else if (matchAttrs(e, as)) { - ATermMap attrs; - queryAllAttrs(e, attrs); - - Expr a = attrs.get(toATerm("type")); - if (a && matchStr(a, s, context) && s == "derivation") { - - XMLAttrs xmlAttrs; - Path outPath, drvPath; + case tAttrs: + if (state.isDerivation(v)) { + XMLAttrs xmlAttrs; - a = attrs.get(toATerm("drvPath")); - if (matchStr(a, drvPath, context)) - xmlAttrs["drvPath"] = drvPath; - - a = attrs.get(toATerm("outPath")); - if (matchStr(a, outPath, context)) - xmlAttrs["outPath"] = outPath; - - XMLOpenElement _(doc, "derivation", xmlAttrs); + Bindings::iterator a = v.attrs->find(toATerm("derivation")); - if (drvsSeen.find(e) == drvsSeen.end()) { - drvsSeen.insert(e); - showAttrs(attrs, doc, context, drvsSeen); - } else - doc.writeEmptyElement("repeated"); + Path drvPath; + a = v.attrs->find(toATerm("drvPath")); + if (a != v.attrs->end() && a->second.type == tString) + xmlAttrs["drvPath"] = drvPath = a->second.string.s; + + a = v.attrs->find(toATerm("outPath")); + if (a != v.attrs->end() && a->second.type == tString) + xmlAttrs["outPath"] = a->second.string.s; + + XMLOpenElement _(doc, "derivation", xmlAttrs); + + if (drvPath != "" && drvsSeen.find(drvPath) == drvsSeen.end()) { + drvsSeen.insert(drvPath); + showAttrs(state, strict, *v.attrs, doc, context, drvsSeen); + } else + doc.writeEmptyElement("repeated"); + } + + else { + XMLOpenElement _(doc, "attrs"); + showAttrs(state, strict, *v.attrs, doc, context, drvsSeen); + } + + break; + + case tList: { + XMLOpenElement _(doc, "list"); + for (unsigned int n = 0; n < v.list.length; ++n) + printValueAsXML(state, strict, v.list.elems[n], doc, context, drvsSeen); + break; } - else { - XMLOpenElement _(doc, "attrs"); - showAttrs(attrs, doc, context, drvsSeen); + case tLambda: { + XMLOpenElement _(doc, "function"); + printPatternAsXML(v.lambda.pat, doc); + break; } - } - else if (matchList(e, es)) { - XMLOpenElement _(doc, "list"); - for (ATermIterator i(es); i; ++i) - printTermAsXML(*i, doc, context, drvsSeen); + default: + doc.writeEmptyElement("unevaluated"); } - - else if (matchFunction(e, pat, body, pos)) { - XMLOpenElement _(doc, "function"); - printPatternAsXML(pat, doc); - } - - else - doc.writeEmptyElement("unevaluated"); } -void printTermAsXML(Expr e, std::ostream & out, PathSet & context) +void printValueAsXML(EvalState & state, bool strict, + Value & v, std::ostream & out, PathSet & context) { XMLWriter doc(true, out); XMLOpenElement root(doc, "expr"); - ExprSet drvsSeen; - printTermAsXML(e, doc, context, drvsSeen); + PathSet drvsSeen; + printValueAsXML(state, strict, v, doc, context, drvsSeen); } diff --git a/src/libexpr/expr-to-xml.hh b/src/libexpr/expr-to-xml.hh index 36b8e4042..8f094185c 100644 --- a/src/libexpr/expr-to-xml.hh +++ b/src/libexpr/expr-to-xml.hh @@ -5,11 +5,12 @@ #include #include "nixexpr.hh" -#include "aterm.hh" +#include "eval.hh" namespace nix { -void printTermAsXML(Expr e, std::ostream & out, PathSet & context); +void printValueAsXML(EvalState & state, bool strict, + Value & v, std::ostream & out, PathSet & context); } diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index f02392bed..6e651d77f 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -105,10 +105,7 @@ static bool getDerivation(EvalState & state, Value & v, { try { state.forceValue(v); - if (v.type != tAttrs) return true; - - Bindings::iterator i = v.attrs->find(toATerm("type")); - if (i == v.attrs->end() || state.forceStringNoCtx(i->second) != "derivation") return true; + if (!state.isDerivation(v)) return true; /* Remove spurious duplicates (e.g., an attribute set like `rec { x = derivation {...}; y = x;}'. */ @@ -117,7 +114,7 @@ static bool getDerivation(EvalState & state, Value & v, DrvInfo drv; - i = v.attrs->find(toATerm("name")); + Bindings::iterator i = v.attrs->find(toATerm("name")); /* !!! We really would like to have a decent back trace here. */ if (i == v.attrs->end()) throw TypeError("derivation name missing"); drv.name = state.forceStringNoCtx(i->second); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 98a31dc3a..5fa2418b5 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -538,7 +538,6 @@ static void prim_readFile(EvalState & state, Value * * args, Value & v) *************************************************************/ -#if 0 /* Convert the argument (which can be any Nix expression) to an XML representation returned in a string. Not all Nix expressions can be sensibly or completely represented (e.g., functions). */ @@ -546,10 +545,9 @@ static void prim_toXML(EvalState & state, Value * * args, Value & v) { std::ostringstream out; PathSet context; - printTermAsXML(strictEvalExpr(state, args[0]), out, context); - return makeStr(out.str(), context); + printValueAsXML(state, true, *args[0], out, context); + mkString(v, out.str(), context); } -#endif /* Store a string in the Nix store as a source file that can be used @@ -582,13 +580,12 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v) } -#if 0 struct FilterFromExpr : PathFilter { EvalState & state; - Expr filter; + Value & filter; - FilterFromExpr(EvalState & state, Expr filter) + FilterFromExpr(EvalState & state, Value & filter) : state(state), filter(filter) { } @@ -599,17 +596,25 @@ struct FilterFromExpr : PathFilter if (lstat(path.c_str(), &st)) throw SysError(format("getting attributes of path `%1%'") % path); - Expr call = - makeCall( - makeCall(filter, makeStr(path)), - makeStr( - S_ISREG(st.st_mode) ? "regular" : - S_ISDIR(st.st_mode) ? "directory" : - S_ISLNK(st.st_mode) ? "symlink" : - "unknown" /* not supported, will fail! */ - )); - - return evalBool(state, call); + /* Call the filter function. The first argument is the path, + the second is a string indicating the type of the file. */ + Value arg1; + mkString(arg1, path); + + Value fun2; + state.callFunction(filter, arg1, fun2); + + Value arg2; + mkString(arg2, + S_ISREG(st.st_mode) ? "regular" : + S_ISDIR(st.st_mode) ? "directory" : + S_ISLNK(st.st_mode) ? "symlink" : + "unknown" /* not supported, will fail! */); + + Value res; + state.callFunction(fun2, arg2, res); + + return state.forceBool(res); } }; @@ -617,19 +622,22 @@ struct FilterFromExpr : PathFilter static void prim_filterSource(EvalState & state, Value * * args, Value & v) { PathSet context; - Path path = coerceToPath(state, args[1], context); + Path path = state.coerceToPath(*args[1], context); if (!context.empty()) throw EvalError(format("string `%1%' cannot refer to other paths") % path); - FilterFromExpr filter(state, args[0]); + state.forceValue(*args[0]); + if (args[0]->type != tLambda) + throw TypeError(format("first argument in call to `filterSource' is not a function but %1%") % showType(*args[0])); + + FilterFromExpr filter(state, *args[0]); Path dstPath = readOnlyMode ? computeStorePathForPath(path, true, htSHA256, filter).first : store->addToStore(path, true, htSHA256, filter); - return makeStr(dstPath, singleton(dstPath)); + mkString(v, dstPath, singleton(dstPath)); } -#endif /************************************************************* @@ -927,15 +935,15 @@ static void prim_stringLength(EvalState & state, Value * * args, Value & v) } -#if 0 static void prim_unsafeDiscardStringContext(EvalState & state, Value * * args, Value & v) { PathSet context; - string s = coerceToString(state, args[0], context); - return makeStr(s, PathSet()); + string s = state.coerceToString(*args[0], context); + mkString(v, s, PathSet()); } +#if 0 /* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a builder without causing the derivation to be built (for instance, in the derivation that builds NARs in nix-push, when doing @@ -1053,13 +1061,9 @@ void EvalState::createBaseEnv() addPrimOp("__readFile", 1, prim_readFile); // Creating files -#if 0 addPrimOp("__toXML", 1, prim_toXML); -#endif addPrimOp("__toFile", 2, prim_toFile); -#if 0 addPrimOp("__filterSource", 2, prim_filterSource); -#endif // Attribute sets addPrimOp("__attrNames", 1, prim_attrNames); @@ -1091,8 +1095,8 @@ void EvalState::createBaseEnv() addPrimOp("toString", 1, prim_toString); addPrimOp("__substring", 3, prim_substring); addPrimOp("__stringLength", 1, prim_stringLength); -#if 0 addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); +#if 0 addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); #endif diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index a71998de2..2f41fa81b 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -75,7 +75,8 @@ void processExpr(EvalState & state, const Strings & attrPaths, std::cout << format("%1%\n") % canonicaliseExpr(e); else { Value v; - if (strict) state.strictEval(e, v); else state.eval(e, v); + state.eval(e, v); + if (strict) state.strictForceValue(v); if (evalOnly) std::cout << v << std::endl; else { From 9a64454faae2ab4ccedeeaad85a0e094726b4765 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 7 Apr 2010 13:59:45 +0000 Subject: [PATCH 046/103] * expr-to-xml -> value-to-xml. --- src/libexpr/Makefile.am | 4 ++-- src/libexpr/primops.cc | 2 +- src/libexpr/{expr-to-xml.cc => value-to-xml.cc} | 2 +- src/libexpr/{expr-to-xml.hh => value-to-xml.hh} | 6 +++--- src/nix-instantiate/nix-instantiate.cc | 11 ++++++++--- 5 files changed, 15 insertions(+), 10 deletions(-) rename src/libexpr/{expr-to-xml.cc => value-to-xml.cc} (99%) rename src/libexpr/{expr-to-xml.hh => value-to-xml.hh} (72%) diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am index c5f487769..99f742ffe 100644 --- a/src/libexpr/Makefile.am +++ b/src/libexpr/Makefile.am @@ -2,12 +2,12 @@ pkglib_LTLIBRARIES = libexpr.la libexpr_la_SOURCES = \ nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \ - get-drvs.cc attr-path.cc expr-to-xml.cc common-opts.cc \ + get-drvs.cc attr-path.cc value-to-xml.cc common-opts.cc \ names.cc pkginclude_HEADERS = \ nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \ - get-drvs.hh attr-path.hh expr-to-xml.hh common-opts.hh \ + get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \ names.hh nixexpr-ast.hh libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5fa2418b5..74eb2b26d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4,7 +4,7 @@ #include "store-api.hh" #include "util.hh" #include "archive.hh" -#include "expr-to-xml.hh" +#include "value-to-xml.hh" #include "nixexpr-ast.hh" #include "parser.hh" #include "names.hh" diff --git a/src/libexpr/expr-to-xml.cc b/src/libexpr/value-to-xml.cc similarity index 99% rename from src/libexpr/expr-to-xml.cc rename to src/libexpr/value-to-xml.cc index ad68739c3..eff414aba 100644 --- a/src/libexpr/expr-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -1,4 +1,4 @@ -#include "expr-to-xml.hh" +#include "value-to-xml.hh" #include "xml-writer.hh" #include "nixexpr-ast.hh" #include "aterm.hh" diff --git a/src/libexpr/expr-to-xml.hh b/src/libexpr/value-to-xml.hh similarity index 72% rename from src/libexpr/expr-to-xml.hh rename to src/libexpr/value-to-xml.hh index 8f094185c..7dfbe06d1 100644 --- a/src/libexpr/expr-to-xml.hh +++ b/src/libexpr/value-to-xml.hh @@ -1,5 +1,5 @@ -#ifndef __EXPR_TO_XML_H -#define __EXPR_TO_XML_H +#ifndef __VALUE_TO_XML_H +#define __VALUE_TO_XML_H #include #include @@ -14,4 +14,4 @@ void printValueAsXML(EvalState & state, bool strict, } -#endif /* !__EXPR_TO_XML_H */ +#endif /* !__VALUE_TO_XML_H */ diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 2f41fa81b..ebf1be5b9 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -7,7 +7,7 @@ #include "parser.hh" #include "get-drvs.hh" #include "attr-path.hh" -#include "expr-to-xml.hh" +#include "value-to-xml.hh" #include "util.hh" #include "store-api.hh" #include "common-opts.hh" @@ -75,10 +75,15 @@ void processExpr(EvalState & state, const Strings & attrPaths, std::cout << format("%1%\n") % canonicaliseExpr(e); else { Value v; + PathSet context; state.eval(e, v); - if (strict) state.strictForceValue(v); if (evalOnly) - std::cout << v << std::endl; + if (xmlOutput) + printValueAsXML(state, strict, v, std::cout, context); + else { + if (strict) state.strictForceValue(v); + std::cout << v << std::endl; + } else { DrvInfos drvs; getDerivations(state, v, "", autoArgs, drvs); From af2a372bb000d4d5aeec37e43ee0f6245c1bba54 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 7 Apr 2010 15:47:06 +0000 Subject: [PATCH 047/103] * Update autoCallFunction() and findAlongAttrPath(). --- src/libexpr/attr-path.cc | 54 +++++-------- src/libexpr/attr-path.hh | 4 +- src/libexpr/common-opts.cc | 14 ++-- src/libexpr/common-opts.hh | 2 +- src/libexpr/eval.cc | 64 ++++++++------- src/libexpr/eval.hh | 34 ++------ src/libexpr/get-drvs.cc | 9 ++- src/libexpr/get-drvs.hh | 2 +- src/nix-env/nix-env.cc | 22 +++--- src/nix-instantiate/nix-instantiate.cc | 103 +++++++------------------ 10 files changed, 120 insertions(+), 188 deletions(-) diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 5d81303fe..5f1c0ad78 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -6,19 +6,8 @@ namespace nix { -#if 0 -bool isAttrs(EvalState & state, Expr e, ATermMap & attrs) -{ - e = evalExpr(state, e); - ATermList dummy; - if (!matchAttrs(e, dummy)) return false; - queryAllAttrs(e, attrs, false); - return true; -} - - -Expr findAlongAttrPath(EvalState & state, const string & attrPath, - const ATermMap & autoArgs, Expr e) +void findAlongAttrPath(EvalState & state, const string & attrPath, + const Bindings & autoArgs, Expr e, Value & v) { Strings tokens = tokenizeString(attrPath, "."); @@ -26,8 +15,10 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath, Error(format("attribute selection path `%1%' does not match expression") % attrPath); string curPath; + + state.mkThunk_(v, e); - for (Strings::iterator i = tokens.begin(); i != tokens.end(); ++i) { + foreach (Strings::iterator, i, tokens) { if (!curPath.empty()) curPath += "."; curPath += *i; @@ -39,7 +30,10 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath, if (string2Int(attr, attrIndex)) apType = apIndex; /* Evaluate the expression. */ - e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs)); + Value vTmp; + state.autoCallFunction(autoArgs, v, vTmp); + v = vTmp; + state.forceValue(v); /* It should evaluate to either an attribute set or an expression, according to what is specified in the @@ -47,38 +41,32 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath, if (apType == apAttr) { - ATermMap attrs; - - if (!isAttrs(state, e, attrs)) + if (v.type != tAttrs) throw TypeError( format("the expression selected by the selection path `%1%' should be an attribute set but is %2%") - % curPath % showType(e)); - - e = attrs.get(toATerm(attr)); - if (!e) - throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath); + % curPath % showType(v)); + Bindings::iterator a = v.attrs->find(toATerm(attr)); + if (a == v.attrs->end()) + throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath); + v = a->second; } else if (apType == apIndex) { - ATermList es; - if (!matchList(e, es)) + if (v.type != tList) throw TypeError( format("the expression selected by the selection path `%1%' should be a list but is %2%") - % curPath % showType(e)); + % curPath % showType(v)); - e = ATelementAt(es, attrIndex); - if (!e) - throw Error(format("list index %1% in selection path `%2%' not found") % attrIndex % curPath); - + if (attrIndex >= v.list.length) + throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath); + + v = v.list.elems[attrIndex]; } } - - return e; } -#endif } diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh index 7abaa83a0..518a7a95d 100644 --- a/src/libexpr/attr-path.hh +++ b/src/libexpr/attr-path.hh @@ -10,8 +10,8 @@ namespace nix { -Expr findAlongAttrPath(EvalState & state, const string & attrPath, - const ATermMap & autoArgs, Expr e); +void findAlongAttrPath(EvalState & state, const string & attrPath, + const Bindings & autoArgs, Expr e, Value & v); } diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc index 9e3f8f961..d5d3df40a 100644 --- a/src/libexpr/common-opts.cc +++ b/src/libexpr/common-opts.cc @@ -9,7 +9,7 @@ namespace nix { bool parseOptionArg(const string & arg, Strings::iterator & i, const Strings::iterator & argsEnd, EvalState & state, - ATermMap & autoArgs) + Bindings & autoArgs) { if (arg != "--arg" && arg != "--argstr") return false; @@ -19,11 +19,13 @@ bool parseOptionArg(const string & arg, Strings::iterator & i, string name = *i++; if (i == argsEnd) throw error; string value = *i++; - - Expr e = arg == "--arg" - ? parseExprFromString(state, value, absPath(".")) - : makeStr(value); - autoArgs.set(toATerm(name), e); + + Value & v(autoArgs[toATerm(name)]); + + if (arg == "--arg") + state.mkThunk_(v, parseExprFromString(state, value, absPath("."))); + else + mkString(v, value); return true; } diff --git a/src/libexpr/common-opts.hh b/src/libexpr/common-opts.hh index fb9659cdc..80298ce55 100644 --- a/src/libexpr/common-opts.hh +++ b/src/libexpr/common-opts.hh @@ -9,7 +9,7 @@ namespace nix { /* Some common option parsing between nix-env and nix-instantiate. */ bool parseOptionArg(const string & arg, Strings::iterator & i, const Strings::iterator & argsEnd, EvalState & state, - ATermMap & autoArgs); + Bindings & autoArgs); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0d18c5522..98b6b7bdd 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -260,6 +260,12 @@ void EvalState::mkAttrs(Value & v) } +void EvalState::mkThunk_(Value & v, Expr expr) +{ + mkThunk(v, baseEnv, expr); +} + + void EvalState::cloneAttrs(Value & src, Value & dst) { mkAttrs(dst); @@ -625,6 +631,37 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) } +void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res) +{ + forceValue(fun); + + ATerm name; + ATermList formals; + ATermBool ellipsis; + + if (fun.type != tLambda || !matchAttrsPat(fun.lambda.pat, formals, ellipsis, name)) { + res = fun; + return; + } + + Value actualArgs; + mkAttrs(actualArgs); + + for (ATermIterator i(formals); i; ++i) { + Expr name, def; ATerm def2; + if (!matchFormal(*i, name, def2)) abort(); + Bindings::const_iterator j = args.find(name); + if (j != args.end()) + (*actualArgs.attrs)[name] = j->second; + else if (!matchDefaultValue(def2, def)) + throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1% ')") + % aterm2String(name)); + } + + callFunction(fun, actualArgs, res); +} + + void EvalState::eval(Expr e, Value & v) { eval(baseEnv, e, v); @@ -1058,33 +1095,6 @@ ATermList flattenList(EvalState & state, Expr e) } -Expr autoCallFunction(Expr e, const ATermMap & args) -{ - Pattern pat; - ATerm body, pos, name; - ATermList formals; - ATermBool ellipsis; - - if (matchFunction(e, pat, body, pos) && matchAttrsPat(pat, formals, ellipsis, name)) { - ATermMap actualArgs(ATgetLength(formals)); - - for (ATermIterator i(formals); i; ++i) { - Expr name, def, value; ATerm def2; - if (!matchFormal(*i, name, def2)) abort(); - if ((value = args.get(name))) - actualArgs.set(name, makeAttrRHS(value, makeNoPos())); - else if (!matchDefaultValue(def2, def)) - throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')") - % aterm2String(name)); - } - - e = makeCall(e, makeAttrs(actualArgs)); - } - - return e; -} - - /* Evaluation of various language constructs. These have been taken out of evalExpr2 to reduce stack space usage. (GCC is really dumb about stack space: it just adds up all the local variables and diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index a42b9ebeb..0ea474447 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -226,13 +226,18 @@ public: void callFunction(Value & fun, Value & arg, Value & v); + /* Automatically call a function for which each argument has a + default value or has a binding in the `args' map. */ + void autoCallFunction(const Bindings & args, Value & fun, Value & res); + /* Allocation primitives. */ Value * allocValues(unsigned int count); Env & allocEnv(); void mkList(Value & v, unsigned int length); void mkAttrs(Value & v); - + void mkThunk_(Value & v, Expr expr); + void cloneAttrs(Value & src, Value & dst); /* Print statistics. */ @@ -244,33 +249,6 @@ public: string showType(Value & v); -#if 0 -/* Evaluate an expression to normal form. */ -Expr evalExpr(EvalState & state, Expr e); - -/* Evaluate an expression, and recursively evaluate list elements and - attributes. If `canonicalise' is true, we remove things like - position information and make sure that attribute sets are in - sorded order. */ -Expr strictEvalExpr(EvalState & state, Expr e); - -/* Specific results. */ -string evalString(EvalState & state, Expr e, PathSet & context); -int evalInt(EvalState & state, Expr e); -bool evalBool(EvalState & state, Expr e); -ATermList evalList(EvalState & state, Expr e); - -/* Flatten nested lists into a single list (or expand a singleton into - a list). */ -ATermList flattenList(EvalState & state, Expr e); - -/* Automatically call a function for which each argument has a default - value or has a binding in the `args' map. Note: result is a call, - not a normal form; it should be evaluated by calling evalExpr(). */ -Expr autoCallFunction(Expr e, const ATermMap & args); -#endif - - } diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 6e651d77f..f5e7242f9 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -155,11 +155,12 @@ static string addToPath(const string & s1, const string & s2) } -static void getDerivations(EvalState & state, Value & v, - const string & pathPrefix, const ATermMap & autoArgs, +static void getDerivations(EvalState & state, Value & vIn, + const string & pathPrefix, const Bindings & autoArgs, DrvInfos & drvs, Done & done) { - // !!! autoCallFunction(evalExpr(state, e), autoArgs) + Value v; + state.autoCallFunction(autoArgs, vIn, v); /* Process the expression. */ DrvInfo drv; @@ -216,7 +217,7 @@ static void getDerivations(EvalState & state, Value & v, void getDerivations(EvalState & state, Value & v, const string & pathPrefix, - const ATermMap & autoArgs, DrvInfos & drvs) + const Bindings & autoArgs, DrvInfos & drvs) { Done done; getDerivations(state, v, pathPrefix, autoArgs, drvs, done); diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 733f20201..f7d1987ea 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -65,7 +65,7 @@ typedef list DrvInfos; bool getDerivation(EvalState & state, Value & v, DrvInfo & drv); void getDerivations(EvalState & state, Value & v, const string & pathPrefix, - const ATermMap & autoArgs, DrvInfos & drvs); + const Bindings & autoArgs, DrvInfos & drvs); } diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index ea85656a2..20affa83d 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -47,7 +47,7 @@ struct InstallSourceInfo Path profile; /* for srcProfile */ string systemFilter; /* for srcNixExprDrvs */ bool prebuiltOnly; - ATermMap autoArgs; + Bindings autoArgs; InstallSourceInfo() : prebuiltOnly(false) { }; }; @@ -161,13 +161,11 @@ static Expr loadSourceExpr(EvalState & state, const Path & path) static void loadDerivations(EvalState & state, Path nixExprPath, - string systemFilter, const ATermMap & autoArgs, + string systemFilter, const Bindings & autoArgs, const string & pathPrefix, DrvInfos & elems) { Value v; - state.eval(loadSourceExpr(state, nixExprPath), v); - - // !!! findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath)) + findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath), v); getDerivations(state, v, pathPrefix, autoArgs, elems); @@ -579,14 +577,12 @@ static void queryInstSources(EvalState & state, } case srcAttrPath: { - throw Error("not implemented"); -#if 0 - foreach (Strings::const_iterator, i, args) - getDerivations(state, - findAlongAttrPath(state, *i, instSource.autoArgs, - loadSourceExpr(state, instSource.nixExprPath)), - "", instSource.autoArgs, elems); -#endif + foreach (Strings::const_iterator, i, args) { + Value v; + findAlongAttrPath(state, *i, instSource.autoArgs, + loadSourceExpr(state, instSource.nixExprPath), v); + getDerivations(state, v, "", instSource.autoArgs, elems); + } break; } } diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index ebf1be5b9..ac405aa66 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -37,80 +37,41 @@ static int rootNr = 0; static bool indirectRoot = false; -#if 0 -static void printResult(EvalState & state, Expr e, - bool evalOnly, bool xmlOutput, const ATermMap & autoArgs) -{ - PathSet context; - - if (evalOnly) - if (xmlOutput) - printTermAsXML(e, std::cout, context); - else - std::cout << format("%1%\n") % canonicaliseExpr(e); - - else { - DrvInfos drvs; - getDerivations(state, e, "", autoArgs, drvs); - for (DrvInfos::iterator i = drvs.begin(); i != drvs.end(); ++i) { - Path drvPath = i->queryDrvPath(state); - if (gcRoot == "") - printGCWarning(); - else - drvPath = addPermRoot(drvPath, - makeRootName(gcRoot, rootNr), - indirectRoot); - std::cout << format("%1%\n") % drvPath; - } - } -} -#endif - - void processExpr(EvalState & state, const Strings & attrPaths, - bool parseOnly, bool strict, const ATermMap & autoArgs, + bool parseOnly, bool strict, const Bindings & autoArgs, bool evalOnly, bool xmlOutput, Expr e) { if (parseOnly) std::cout << format("%1%\n") % canonicaliseExpr(e); - else { - Value v; - PathSet context; - state.eval(e, v); - if (evalOnly) - if (xmlOutput) - printValueAsXML(state, strict, v, std::cout, context); + else + foreach (Strings::const_iterator, i, attrPaths) { + Value v; + findAlongAttrPath(state, *i, autoArgs, e, v); + state.forceValue(v); + + PathSet context; + if (evalOnly) + if (xmlOutput) + printValueAsXML(state, strict, v, std::cout, context); + else { + if (strict) state.strictForceValue(v); + std::cout << v << std::endl; + } else { - if (strict) state.strictForceValue(v); - std::cout << v << std::endl; - } - else { - DrvInfos drvs; - getDerivations(state, v, "", autoArgs, drvs); - foreach (DrvInfos::iterator, i, drvs) { - Path drvPath = i->queryDrvPath(state); - if (gcRoot == "") - printGCWarning(); - else - drvPath = addPermRoot(drvPath, - makeRootName(gcRoot, rootNr), - indirectRoot); - std::cout << format("%1%\n") % drvPath; + DrvInfos drvs; + getDerivations(state, v, "", autoArgs, drvs); + foreach (DrvInfos::iterator, i, drvs) { + Path drvPath = i->queryDrvPath(state); + if (gcRoot == "") + printGCWarning(); + else + drvPath = addPermRoot(drvPath, + makeRootName(gcRoot, rootNr), + indirectRoot); + std::cout << format("%1%\n") % drvPath; + } } } - } - -#if 0 - for (Strings::const_iterator i = attrPaths.begin(); i != attrPaths.end(); ++i) { - Expr e2 = findAlongAttrPath(state, *i, autoArgs, e); - if (!parseOnly) - if (strict) - e2 = state.strictEval(e2); - else - e2 = evalExpr(state, e2); - printResult(state, e2, evalOnly, xmlOutput, autoArgs); - } -#endif } @@ -124,11 +85,9 @@ void run(Strings args) bool xmlOutput = false; bool strict = false; Strings attrPaths; - ATermMap autoArgs(128); + Bindings autoArgs; - for (Strings::iterator i = args.begin(); - i != args.end(); ) - { + for (Strings::iterator i = args.begin(); i != args.end(); ) { string arg = *i++; if (arg == "-") @@ -175,9 +134,7 @@ void run(Strings args) evalOnly, xmlOutput, e); } - for (Strings::iterator i = files.begin(); - i != files.end(); i++) - { + foreach (Strings::iterator, i, files) { Path path = absPath(*i); Expr e = parseExprFromFile(state, path); processExpr(state, attrPaths, parseOnly, strict, autoArgs, From 7e048eddf55637b9e81d704f6b9f1fdeca98a5ea Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 8 Apr 2010 11:25:14 +0000 Subject: [PATCH 048/103] * Fix blackholing. If evaluation fails due to an assertion failure, then the blackhole has to be removed to ensure that repeated evaluation of the same value gives an assertion failure again rather than an "infinite recursion" error. --- src/libexpr/eval.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 98b6b7bdd..e72538e60 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -681,8 +681,14 @@ bool EvalState::evalBool(Env & env, Expr e) void EvalState::forceValue(Value & v) { if (v.type == tThunk) { - //v.type = tBlackhole; - eval(*v.thunk.env, v.thunk.expr, v); + ValueType saved = v.type; + try { + v.type = tBlackhole; + eval(*v.thunk.env, v.thunk.expr, v); + } catch (Error & e) { + v.type = saved; + throw; + } } else if (v.type == tCopy) { forceValue(*v.val); From b7b3dd55f9e30e2eccb54aca22830852c5c2b514 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 8 Apr 2010 11:41:19 +0000 Subject: [PATCH 049/103] * Remove a lot of dead code. --- src/libexpr/eval.cc | 500 +------------------------ src/libexpr/nixexpr.cc | 217 ----------- src/libexpr/nixexpr.hh | 54 +-- src/nix-instantiate/nix-instantiate.cc | 2 +- 4 files changed, 10 insertions(+), 763 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index e72538e60..2d22a4fdc 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -27,7 +27,14 @@ std::ostream & operator << (std::ostream & str, Value & v) str << (v.boolean ? "true" : "false"); break; case tString: - str << "\"" << v.string.s << "\""; // !!! escaping + str << "\""; + for (const char * i = v.string.s; *i; i++) + if (*i == '\"' || *i == '\\') str << "\\" << *i; + else if (*i == '\n') str << "\\n"; + else if (*i == '\r') str << "\\r"; + else if (*i == '\t') str << "\\t"; + else str << *i; + str << "\""; break; case tPath: str << v.path; // !!! escaping? @@ -930,497 +937,6 @@ bool EvalState::eqValues(Value & v1, Value & v2) } -#if 0 -/* Pattern-match `pat' against `arg'. The result is a set of - substitutions (`subs') and a set of recursive substitutions - (`subsRecursive'). The latter can refer to the variables bound by - both `subs' and `subsRecursive'. */ -static void patternMatch(EvalState & state, - Pattern pat, Expr arg, ATermMap & subs, ATermMap & subsRecursive) -{ - ATerm name; - ATermList formals; - ATermBool ellipsis; - - if (matchVarPat(pat, name)) - subs.set(name, arg); - - else if (matchAttrsPat(pat, formals, ellipsis, name)) { - - arg = evalExpr(state, arg); - - if (name != sNoAlias) subs.set(name, arg); - - /* Get the actual arguments. */ - ATermMap attrs; - queryAllAttrs(arg, attrs); - unsigned int nrAttrs = attrs.size(); - - /* For each formal argument, get the actual argument. If - there is no matching actual argument but the formal - argument has a default, use the default. */ - unsigned int attrsUsed = 0; - for (ATermIterator i(formals); i; ++i) { - Expr name, def; - DefaultValue def2; - if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ - - Expr value = attrs[name]; - - if (value == 0) { - if (!matchDefaultValue(def2, def)) def = 0; - if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") - % aterm2String(name)); - subsRecursive.set(name, def); - } else { - attrsUsed++; - attrs.remove(name); - subs.set(name, value); - } - - } - - /* Check that each actual argument is listed as a formal - argument (unless the attribute match specifies a `...'). */ - if (ellipsis == eFalse && attrsUsed != nrAttrs) - throw TypeError(format("the function does not expect an argument named `%1%'") - % aterm2String(attrs.begin()->key)); - } - - else abort(); -} - - -/* Substitute an argument set into the body of a function. */ -static Expr substArgs(EvalState & state, - Expr body, Pattern pat, Expr arg) -{ - ATermMap subs(16), subsRecursive(16); - - patternMatch(state, pat, arg, subs, subsRecursive); - - /* If we used any default values, make a recursive attribute set - out of the (argument-name, value) tuples. This is so that we - can support default values that refer to each other, e.g. ({x, - y ? x + x}: y) {x = "foo";} evaluates to "foofoo". */ - if (subsRecursive.size() != 0) { - ATermList recAttrs = ATempty; - foreach (ATermMap::const_iterator, i, subs) - recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos())); - foreach (ATermMap::const_iterator, i, subsRecursive) - recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos())); - Expr rec = makeRec(recAttrs, ATempty); - foreach (ATermMap::const_iterator, i, subsRecursive) - subs.set(i->key, makeSelect(rec, i->key)); - } - - return substitute(Substitution(0, &subs), body); -} - - -/* Transform a mutually recursive set into a non-recursive set. Each - attribute is transformed into an expression that has all references - to attributes substituted with selection expressions on the - original set. E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f - (e.x) (e.y); y = e.x;}'. */ -LocalNoInline(ATerm expandRec(EvalState & state, ATerm e, ATermList rbnds, ATermList nrbnds)) -{ - ATerm name; - Expr e2; - Pos pos; - Expr eOverrides = 0; - - /* Create the substitution list. */ - ATermMap subs(ATgetLength(rbnds) + ATgetLength(nrbnds)); - for (ATermIterator i(rbnds); i; ++i) { - if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - subs.set(name, makeSelect(e, name)); - } - for (ATermIterator i(nrbnds); i; ++i) { - if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - if (name == sOverrides) eOverrides = e2; - subs.set(name, e2); - } - - /* If the rec contains an attribute called `__overrides', then - evaluate it, and add the attributes in that set to the rec. - This allows overriding of recursive attributes, which is - otherwise not possible. (You can use the // operator to - replace an attribute, but other attributes in the rec will - still reference the original value, because that value has been - substituted into the bodies of the other attributes. Hence we - need __overrides.) */ - ATermMap overrides; - if (eOverrides) { - eOverrides = evalExpr(state, eOverrides); - queryAllAttrs(eOverrides, overrides, false); - foreach (ATermMap::const_iterator, i, overrides) - subs.set(i->key, i->value); - } - - Substitution subs_(0, &subs); - - /* Create the non-recursive set. */ - ATermMap as(ATgetLength(rbnds) + ATgetLength(nrbnds)); - for (ATermIterator i(rbnds); i; ++i) { - if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - as.set(name, makeAttrRHS(substitute(subs_, e2), pos)); - } - - if (eOverrides) - foreach (ATermMap::const_iterator, i, overrides) - as.set(i->key, makeAttrRHS(i->value, makeNoPos())); - - /* Copy the non-recursive bindings. !!! inefficient */ - for (ATermIterator i(nrbnds); i; ++i) { - if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - as.set(name, makeAttrRHS(e2, pos)); - } - - return makeAttrs(as); -} - - -static void flattenList(EvalState & state, Expr e, ATermList & result) -{ - ATermList es; - e = evalExpr(state, e); - if (matchList(e, es)) - for (ATermIterator i(es); i; ++i) - flattenList(state, *i, result); - else - result = ATinsert(result, e); -} - - -ATermList flattenList(EvalState & state, Expr e) -{ - ATermList result = ATempty; - flattenList(state, e, result); - return ATreverse(result); -} - - -/* Evaluation of various language constructs. These have been taken - out of evalExpr2 to reduce stack space usage. (GCC is really dumb - about stack space: it just adds up all the local variables and - temporaries of every scope into one huge stack frame. This is - really bad for deeply recursive functions.) */ - - -LocalNoInline(Expr evalVar(EvalState & state, ATerm name)) -{ - ATerm primOp = state.primOps.get(name); - if (!primOp) - throw EvalError(format("impossible: undefined variable `%1%'") % aterm2String(name)); - int arity; - ATermBlob fun; - if (!matchPrimOpDef(primOp, arity, fun)) abort(); - if (arity == 0) - /* !!! backtrace for primop call */ - return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector()); - else - return makePrimOp(arity, fun, ATempty); -} - - -LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg)) -{ - Pattern pat; - ATerm pos; - Expr body; - - /* Evaluate the left-hand side. */ - fun = evalExpr(state, fun); - - /* Is it a primop or a function? */ - int arity; - ATermBlob funBlob; - ATermList args; - if (matchPrimOp(fun, arity, funBlob, args)) { - args = ATinsert(args, arg); - if (ATgetLength(args) == arity) { - /* Put the arguments in a vector in reverse (i.e., - actual) order. */ - ATermVector args2(arity); - for (ATermIterator i(args); i; ++i) - args2[--arity] = *i; - /* !!! backtrace for primop call */ - return ((PrimOp) ATgetBlobData(funBlob)) - (state, args2); - } else - /* Need more arguments, so propagate the primop. */ - return makePrimOp(arity, funBlob, args); - } - - else if (matchFunction(fun, pat, body, pos)) { - try { - return evalExpr(state, substArgs(state, body, pat, arg)); - } catch (Error & e) { - addErrorPrefix(e, "while evaluating the function at %1%:\n", - showPos(pos)); - throw; - } - } - - else throwTypeError( - "attempt to call something which is neither a function nor a primop (built-in operation) but %1%", - showType(fun)); -} - - -LocalNoInline(Expr evalWith(EvalState & state, Expr defs, Expr body, ATerm pos)) -{ - ATermMap attrs; - try { - defs = evalExpr(state, defs); - queryAllAttrs(defs, attrs); - } catch (Error & e) { - addErrorPrefix(e, "while evaluating the `with' definitions at %1%:\n", - showPos(pos)); - throw; - } - try { - body = substitute(Substitution(0, &attrs), body); - checkVarDefs(state.primOps, body); - return evalExpr(state, body); - } catch (Error & e) { - addErrorPrefix(e, "while evaluating the `with' body at %1%:\n", - showPos(pos)); - throw; - } -} - - -/* Implementation of the `==' and `!=' operators. */ -LocalNoInline(bool areEqual(EvalState & state, Expr e1, Expr e2)) -{ - e1 = evalExpr(state, e1); - e2 = evalExpr(state, e2); - - /* We cannot test functions/primops for equality, and we currently - don't support testing equality between attribute sets or lists - - that would have to be a deep equality test to be sound. */ - AFun sym1 = ATgetAFun(e1); - AFun sym2 = ATgetAFun(e2); - - if (sym1 != sym2) return false; - - /* Functions are incomparable. */ - if (sym1 == symFunction || sym1 == symPrimOp) return false; - - if (!state.allowUnsafeEquality && sym1 == symAttrs) - throw EvalError("comparison of attribute sets is not implemented"); - - /* !!! This allows comparisons of infinite data structures to - succeed, such as `let x = [x]; in x == x'. This is - undesirable, since equivalent (?) terms such as `let x = [x]; y - = [y]; in x == y' don't terminate. */ - if (e1 == e2) return true; - - if (sym1 == symList) { - ATermList es1; matchList(e1, es1); - ATermList es2; matchList(e2, es2); - if (ATgetLength(es1) != ATgetLength(es2)) return false; - ATermIterator i(es1), j(es2); - while (*i) { - if (!areEqual(state, *i, *j)) return false; - ++i; ++j; - } - return true; - } - - return false; -} - - -Expr evalExpr2(EvalState & state, Expr e) -{ - /* When changing this function, make sure that you don't cause a - (large) increase in stack consumption! */ - - char x; - if (&x < deepestStack) deepestStack = &x; - - Expr e1, e2, e3; - ATerm name, pos; - AFun sym = ATgetAFun(e); - - /* Normal forms. */ - if (sym == symStr || - sym == symPath || - sym == symNull || - sym == symInt || - sym == symBool || - sym == symFunction || - sym == symAttrs || - sym == symList || - sym == symPrimOp) - return e; - - /* The `Closed' constructor is just a way to prevent substitutions - into expressions not containing free variables. */ - if (matchClosed(e, e1)) - return evalExpr(state, e1); - - /* Any encountered variables must be primops (since undefined - variables are detected after parsing). */ - if (matchVar(e, name)) return evalVar(state, name); - - /* Function application. */ - if (matchCall(e, e1, e2)) return evalCall(state, e1, e2); - - /* Attribute selection. */ - if (matchSelect(e, e1, name)) return evalSelect(state, e1, name); - - /* Mutually recursive sets. */ - ATermList rbnds, nrbnds; - if (matchRec(e, rbnds, nrbnds)) - return expandRec(state, e, rbnds, nrbnds); - - /* Conditionals. */ - if (matchIf(e, e1, e2, e3)) - return evalExpr(state, evalBool(state, e1) ? e2 : e3); - - /* Assertions. */ - if (matchAssert(e, e1, e2, pos)) return evalAssert(state, e1, e2, pos); - - /* Withs. */ - if (matchWith(e, e1, e2, pos)) return evalWith(state, e1, e2, pos); - - /* Generic equality/inequality. Note that the behaviour on - composite data (lists, attribute sets) and functions is - undefined, since the subterms of those terms are not evaluated. - However, we don't want to make (==) strict, because that would - make operations like `big_derivation == null' very slow (unless - we were to evaluate them side-by-side). */ - if (matchOpEq(e, e1, e2)) return makeBool(areEqual(state, e1, e2)); - - if (matchOpNEq(e, e1, e2)) return makeBool(!areEqual(state, e1, e2)); - - /* Negation. */ - if (matchOpNot(e, e1)) - return makeBool(!evalBool(state, e1)); - - /* Implication. */ - if (matchOpImpl(e, e1, e2)) - return makeBool(!evalBool(state, e1) || evalBool(state, e2)); - - /* Conjunction (logical AND). */ - if (matchOpAnd(e, e1, e2)) - return makeBool(evalBool(state, e1) && evalBool(state, e2)); - - /* Disjunction (logical OR). */ - if (matchOpOr(e, e1, e2)) - return makeBool(evalBool(state, e1) || evalBool(state, e2)); - - /* Attribute set update (//). */ - if (matchOpUpdate(e, e1, e2)) - return updateAttrs(evalExpr(state, e1), evalExpr(state, e2)); - - /* Attribute existence test (?). */ - if (matchOpHasAttr(e, e1, name)) return evalHasAttr(state, e1, name); - - /* String or path concatenation. */ - if (sym == symOpPlus || sym == symConcatStrings) - return evalPlusConcat(state, e); - - /* Backwards compatability: subpath operator (~). */ - if (matchSubPath(e, e1, e2)) return evalSubPath(state, e1, e2); - - /* List concatenation. */ - if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2); - - /* Barf. */ - abort(); -} - - -Expr evalExpr(EvalState & state, Expr e) -{ - checkInterrupt(); - -#if 0 - startNest(nest, lvlVomit, - format("evaluating expression: %1%") % e); -#endif - - state.nrEvaluated++; - - /* Consult the memo table to quickly get the normal form of - previously evaluated expressions. */ - Expr nf = state.normalForms.get(e); - if (nf) { - if (nf == makeBlackHole()) - throwEvalError("infinite recursion encountered"); - state.nrCached++; - return nf; - } - - /* Otherwise, evaluate and memoize. */ - state.normalForms.set(e, makeBlackHole()); - try { - nf = evalExpr2(state, e); - } catch (Error & err) { - state.normalForms.remove(e); - throw; - } - state.normalForms.set(e, nf); - return nf; -} - - -static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs); - - -static Expr strictEvalExpr_(EvalState & state, Expr e, ATermMap & nfs) -{ - e = evalExpr(state, e); - - ATermList as; - if (matchAttrs(e, as)) { - ATermList as2 = ATempty; - for (ATermIterator i(as); i; ++i) { - ATerm name; Expr e; ATerm pos; - if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */ - as2 = ATinsert(as2, makeBind(name, strictEvalExpr(state, e, nfs), pos)); - } - return makeAttrs(ATreverse(as2)); - } - - ATermList es; - if (matchList(e, es)) { - ATermList es2 = ATempty; - for (ATermIterator i(es); i; ++i) - es2 = ATinsert(es2, strictEvalExpr(state, *i, nfs)); - return makeList(ATreverse(es2)); - } - - return e; -} - - -static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs) -{ - Expr nf = nfs.get(e); - if (nf) return nf; - - nf = strictEvalExpr_(state, e, nfs); - - nfs.set(e, nf); - - return nf; -} - - -Expr strictEvalExpr(EvalState & state, Expr e) -{ - ATermMap strictNormalForms; - return strictEvalExpr(state, e, strictNormalForms); -} -#endif - - void EvalState::printStats() { char x; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index e581b1329..3c5d02b34 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -52,50 +52,6 @@ ATerm bottomupRewrite(TermFun & f, ATerm e) } -void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos) -{ - ATermList bnds; - if (!matchAttrs(e, bnds)) - throw TypeError(format("value is %1% while an attribute set was expected") % showType(e)); - - for (ATermIterator i(bnds); i; ++i) { - ATerm name; - Expr e; - ATerm pos; - if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */ - attrs.set(name, withPos ? makeAttrRHS(e, pos) : e); - } -} - - -Expr queryAttr(Expr e, const string & name) -{ - ATerm dummy; - return queryAttr(e, name, dummy); -} - - -Expr queryAttr(Expr e, const string & name, ATerm & pos) -{ - ATermList bnds; - if (!matchAttrs(e, bnds)) - throw TypeError(format("value is %1% while an attribute set was expected") % showType(e)); - - for (ATermIterator i(bnds); i; ++i) { - ATerm name2, pos2; - Expr e; - if (!matchBind(*i, name2, e, pos2)) - abort(); /* can't happen */ - if (aterm2String(name2) == name) { - pos = pos2; - return e; - } - } - - return 0; -} - - Expr makeAttrs(const ATermMap & attrs) { ATermList bnds = ATempty; @@ -131,88 +87,6 @@ static void varsBoundByPattern(ATermMap & map, Pattern pat) } -Expr substitute(const Substitution & subs, Expr e) -{ - checkInterrupt(); - - //if (subs.size() == 0) return e; - - ATerm name, pos, e2; - - /* As an optimisation, don't substitute in subterms known to be - closed. */ - if (matchClosed(e, e2)) return e; - - if (matchVar(e, name)) { - Expr sub = subs.lookup(name); - if (sub == makeRemoved()) sub = 0; - Expr wrapped; - /* Add a "closed" wrapper around terms that aren't already - closed. The check is necessary to prevent repeated - wrapping, e.g., closed(closed(closed(...))), which kills - caching. */ - return sub ? (matchClosed(sub, wrapped) ? sub : makeClosed(sub)) : e; - } - - /* In case of a function, filter out all variables bound by this - function. */ - Pattern pat; - ATerm body; - if (matchFunction(e, pat, body, pos)) { - ATermMap map(16); - varsBoundByPattern(map, pat); - Substitution subs2(&subs, &map); - return makeFunction( - (Pattern) substitute(subs2, (Expr) pat), - substitute(subs2, body), pos); - } - - /* Idem for a mutually recursive attribute set. */ - ATermList rbnds, nrbnds; - if (matchRec(e, rbnds, nrbnds)) { - ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds)); - for (ATermIterator i(rbnds); i; ++i) - if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved()); - else abort(); /* can't happen */ - for (ATermIterator i(nrbnds); i; ++i) - if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved()); - else abort(); /* can't happen */ - return makeRec( - (ATermList) substitute(Substitution(&subs, &map), (ATerm) rbnds), - (ATermList) substitute(subs, (ATerm) nrbnds)); - } - - if (ATgetType(e) == AT_APPL) { - AFun fun = ATgetAFun(e); - int arity = ATgetArity(fun); - ATerm args[arity]; - bool changed = false; - - for (int i = 0; i < arity; ++i) { - ATerm arg = ATgetArgument(e, i); - args[i] = substitute(subs, arg); - if (args[i] != arg) changed = true; - } - - return changed ? (ATerm) ATmakeApplArray(fun, args) : e; - } - - if (ATgetType(e) == AT_LIST) { - unsigned int len = ATgetLength((ATermList) e); - ATerm es[len]; - ATermIterator i((ATermList) e); - for (unsigned int j = 0; i; ++i, ++j) - es[j] = substitute(subs, *i); - ATermList out = ATempty; - for (unsigned int j = len; j; --j) - out = ATinsert(out, es[j - 1]); - return (ATerm) out; - } - - return e; -} - - /* We use memoisation to prevent exponential complexity on heavily shared ATerms (remember, an ATerm is a graph, not a tree!). Note that using an STL set is fine here wrt to ATerm garbage collection @@ -287,51 +161,6 @@ void checkVarDefs(const ATermMap & defs, Expr e) } -struct Canonicalise : TermFun -{ - ATerm operator () (ATerm e) - { - /* Remove position info. */ - ATerm path; - int line, column; - if (matchPos(e, path, line, column)) - return makeNoPos(); - - /* Sort attribute sets. */ - ATermList _; - if (matchAttrs(e, _)) { - ATermMap attrs; - queryAllAttrs(e, attrs); - StringSet names; - for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) - names.insert(aterm2String(i->key)); - - ATermList attrs2 = ATempty; - for (StringSet::reverse_iterator i = names.rbegin(); i != names.rend(); ++i) - attrs2 = ATinsert(attrs2, - makeBind(toATerm(*i), attrs.get(toATerm(*i)), makeNoPos())); - - return makeAttrs(attrs2); - } - - return e; - } -}; - - -Expr canonicaliseExpr(Expr e) -{ - Canonicalise canonicalise; - return bottomupRewrite(canonicalise, e); -} - - -Expr makeBool(bool b) -{ - return b ? eTrue : eFalse; -} - - bool matchStr(Expr e, string & s, PathSet & context) { ATermList l; @@ -354,50 +183,4 @@ Expr makeStr(const string & s, const PathSet & context) } -string showType(Expr e) -{ - ATerm t1, t2; - ATermList l1; - ATermBlob b1; - int i1; - Pattern p1; - if (matchStr(e, t1, l1)) return "a string"; - if (matchPath(e, t1)) return "a path"; - if (matchNull(e)) return "null"; - if (matchInt(e, i1)) return "an integer"; - if (matchBool(e, t1)) return "a boolean"; - if (matchFunction(e, p1, t1, t2)) return "a function"; - if (matchAttrs(e, l1)) return "an attribute set"; - if (matchList(e, l1)) return "a list"; - if (matchPrimOp(e, i1, b1, l1)) return "a partially applied built-in function"; - return "an unknown type"; -} - - -string showValue(Expr e) -{ - PathSet context; - string s; - ATerm s2; - int i; - if (matchStr(e, s, context)) { - string u; - for (string::iterator i = s.begin(); i != s.end(); ++i) - if (*i == '\"' || *i == '\\') u += "\\" + *i; - else if (*i == '\n') u += "\\n"; - else if (*i == '\r') u += "\\r"; - else if (*i == '\t') u += "\\t"; - else u += *i; - return "\"" + u + "\""; - } - if (matchPath(e, s2)) return aterm2String(s2); - if (matchNull(e)) return "null"; - if (matchInt(e, i)) return (format("%1%") % i).str(); - if (e == eTrue) return "true"; - if (e == eFalse) return "false"; - /* !!! incomplete */ - return ""; -} - - } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index c7cdf46df..9f1050d80 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -34,34 +34,10 @@ typedef ATerm ATermBool; typedef vector ATermVector; -/* A substitution is a linked list of ATermMaps that map names to - identifiers. We use a list of ATermMaps rather than a single to - make it easy to grow or shrink a substitution when entering a - scope. */ -struct Substitution -{ - ATermMap * map; - const Substitution * prev; - - Substitution(const Substitution * prev, ATermMap * map) - { - this->prev = prev; - this->map = map; - } - - Expr lookup(Expr name) const - { - Expr x; - for (const Substitution * s(this); s; s = s->prev) - if ((x = s->map->get(name))) return x; - return 0; - } -}; - - /* Show a position. */ string showPos(ATerm pos); + /* Generic bottomup traversal over ATerms. The traversal first recursively descends into subterms, and then applies the given term function to the resulting term. */ @@ -73,37 +49,15 @@ struct TermFun ATerm bottomupRewrite(TermFun & f, ATerm e); -/* Query all attributes in an attribute set expression. The - expression must be in normal form. */ -void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos = false); - -/* Query a specific attribute from an attribute set expression. The - expression must be in normal form. */ -Expr queryAttr(Expr e, const string & name); -Expr queryAttr(Expr e, const string & name, ATerm & pos); - /* Create an attribute set expression from an Attrs value. */ Expr makeAttrs(const ATermMap & attrs); -/* Perform a set of substitutions on an expression. */ -Expr substitute(const Substitution & subs, Expr e); - - /* Check whether all variables are defined in the given expression. Throw an exception if this isn't the case. */ void checkVarDefs(const ATermMap & def, Expr e); -/* Canonicalise a Nix expression by sorting attributes and removing - location information. */ -Expr canonicaliseExpr(Expr e); - - -/* Create an expression representing a boolean. */ -Expr makeBool(bool b); - - /* Manipulation of Str() nodes. Note: matchStr() does not clear context! */ bool matchStr(Expr e, string & s, PathSet & context); @@ -111,12 +65,6 @@ bool matchStr(Expr e, string & s, PathSet & context); Expr makeStr(const string & s, const PathSet & context = PathSet()); -/* Showing types, values. */ -string showType(Expr e); - -string showValue(Expr e); - - } diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index ac405aa66..7931af6ab 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -42,7 +42,7 @@ void processExpr(EvalState & state, const Strings & attrPaths, bool evalOnly, bool xmlOutput, Expr e) { if (parseOnly) - std::cout << format("%1%\n") % canonicaliseExpr(e); + std::cout << format("%1%\n"); else foreach (Strings::const_iterator, i, attrPaths) { Value v; From f3dc7ab877c736ec1500f6b503ab71a62ce90305 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 9 Apr 2010 12:00:49 +0000 Subject: [PATCH 050/103] * Keep more statistics about stack space usage. * Reduce stack space usage. --- src/libexpr/eval.cc | 105 ++++++++++++++++++++++++++++---------------- src/libexpr/eval.hh | 16 +++++-- 2 files changed, 78 insertions(+), 43 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2d22a4fdc..56a73b494 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -93,7 +93,8 @@ string showType(Value & v) EvalState::EvalState() : baseEnv(allocEnv()) { - nrValues = nrEnvs = nrEvaluated = 0; + nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0; + deepestStack = (char *) -1; initNixExprHelpers(); @@ -103,6 +104,12 @@ EvalState::EvalState() : baseEnv(allocEnv()) } +EvalState::~EvalState() +{ + assert(recursionDepth == 0); +} + + void EvalState::addConstant(const string & name, Value & v) { baseEnv.bindings[toATerm(name)] = v; @@ -141,6 +148,11 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2)) throw EvalError(format(s) % s2); } +LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3)) +{ + throw EvalError(format(s) % s2 % s3); +} + LocalNoInlineNoReturn(void throwTypeError(const char * s)) { throw TypeError(s); @@ -151,6 +163,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2)) throw TypeError(format(s) % s2); } +LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s2)) +{ + throw TypeError(format(s) % s2); +} + LocalNoInline(void addErrorPrefix(Error & e, const char * s)) { e.addPrefix(s); @@ -234,7 +251,7 @@ static Value * lookupVar(Env * env, Sym name) } #endif - throw Error(format("undefined variable `%1%'") % aterm2String(name)); + throwEvalError("undefined variable `%1%'", aterm2String(name)); } @@ -295,21 +312,35 @@ void EvalState::evalFile(const Path & path, Value & v) try { eval(e, v); } catch (Error & e) { - e.addPrefix(format("while evaluating the file `%1%':\n") - % path); + addErrorPrefix(e, "while evaluating the file `%1%':\n", path); throw; } } -static char * deepestStack = (char *) -1; /* for measuring stack usage */ +struct RecursionCounter +{ + EvalState & state; + RecursionCounter(EvalState & state) : state(state) + { + state.recursionDepth++; + if (state.recursionDepth > state.maxRecursionDepth) + state.maxRecursionDepth = state.recursionDepth; + } + ~RecursionCounter() + { + state.recursionDepth--; + } +}; void EvalState::eval(Env & env, Expr e, Value & v) { /* When changing this function, make sure that you don't cause a (large) increase in stack consumption! */ - + + /* !!! Disable this eventually. */ + RecursionCounter r(*this); char x; if (&x < deepestStack) deepestStack = &x; @@ -482,8 +513,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) } if (isPath && !context.empty()) - throw EvalError(format("a string that refers to a store path cannot be appended to a path, in `%1%'") - % s.str()); + throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str()); if (isPath) mkPath(v, s.str().c_str()); @@ -498,7 +528,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) /* Assertions. */ else if (matchAssert(e, e1, e2, pos)) { if (!evalBool(env, e1)) - throw AssertionError(format("assertion failed at %1%") % showPos(pos)); + throwAssertionError("assertion failed at %1%", showPos(pos)); eval(env, e2, v); } @@ -538,7 +568,7 @@ void EvalState::eval(Env & env, Expr e, Value & v) mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end()); } - else throw Error("unsupported term"); + else abort(); } @@ -615,8 +645,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) if (j == arg.attrs->end()) { if (!matchDefaultValue(def2, def)) def = 0; - if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") - % aterm2String(name)); + if (def == 0) throwTypeError("the argument named `%1%' required by the function is missing", + aterm2String(name)); mkThunk(v, env2, def); } else { attrsUsed++; @@ -629,7 +659,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) TODO: show the names of the expected/unexpected arguments. */ if (ellipsis == eFalse && attrsUsed != arg.attrs->size()) - throw TypeError("function called with unexpected argument"); + throwTypeError("function called with unexpected argument"); } else abort(); @@ -661,8 +691,7 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res if (j != args.end()) (*actualArgs.attrs)[name] = j->second; else if (!matchDefaultValue(def2, def)) - throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1% ')") - % aterm2String(name)); + throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", aterm2String(name)); } callFunction(fun, actualArgs, res); @@ -680,7 +709,7 @@ bool EvalState::evalBool(Env & env, Expr e) Value v; eval(env, e, v); if (v.type != tBool) - throw TypeError(format("value is %1% while a Boolean was expected") % showType(v)); + throwTypeError("value is %1% while a Boolean was expected", showType(v)); return v.boolean; } @@ -704,7 +733,7 @@ void EvalState::forceValue(Value & v) else if (v.type == tApp) callFunction(*v.app.left, *v.app.right, v); else if (v.type == tBlackhole) - throw EvalError("infinite recursion encountered"); + throwEvalError("infinite recursion encountered"); } @@ -728,7 +757,7 @@ int EvalState::forceInt(Value & v) { forceValue(v); if (v.type != tInt) - throw TypeError(format("value is %1% while an integer was expected") % showType(v)); + throwTypeError("value is %1% while an integer was expected", showType(v)); return v.integer; } @@ -737,7 +766,7 @@ bool EvalState::forceBool(Value & v) { forceValue(v); if (v.type != tBool) - throw TypeError(format("value is %1% while a Boolean was expected") % showType(v)); + throwTypeError("value is %1% while a Boolean was expected", showType(v)); return v.boolean; } @@ -746,7 +775,7 @@ void EvalState::forceAttrs(Value & v) { forceValue(v); if (v.type != tAttrs) - throw TypeError(format("value is %1% while an attribute set was expected") % showType(v)); + throwTypeError("value is %1% while an attribute set was expected", showType(v)); } @@ -754,7 +783,7 @@ void EvalState::forceList(Value & v) { forceValue(v); if (v.type != tList) - throw TypeError(format("value is %1% while a list was expected") % showType(v)); + throwTypeError("value is %1% while a list was expected", showType(v)); } @@ -762,7 +791,7 @@ void EvalState::forceFunction(Value & v) { forceValue(v); if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp) - throw TypeError(format("value is %1% while a function was expected") % showType(v)); + throwTypeError("value is %1% while a function was expected", showType(v)); } @@ -770,7 +799,7 @@ string EvalState::forceString(Value & v) { forceValue(v); if (v.type != tString) - throw TypeError(format("value is %1% while a string was expected") % showType(v)); + throwTypeError("value is %1% while a string was expected", showType(v)); return string(v.string.s); } @@ -778,10 +807,9 @@ string EvalState::forceString(Value & v) string EvalState::forceString(Value & v, PathSet & context) { string s = forceString(v); - if (v.string.context) { + if (v.string.context) for (const char * * p = v.string.context; *p; ++p) context.insert(*p); - } return s; } @@ -790,8 +818,8 @@ string EvalState::forceStringNoCtx(Value & v) { string s = forceString(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]); + throwEvalError("the string `%1%' is not allowed to refer to a store path (such as `%2%')", + v.string.s, v.string.context[0]); return s; } @@ -824,8 +852,7 @@ string EvalState::coerceToString(Value & v, PathSet & context, if (!copyToStore) return path; if (nix::isDerivation(path)) - throw EvalError(format("file names are not allowed to end in `%1%'") - % drvExtension); + throwEvalError("file names are not allowed to end in `%1%'", drvExtension); Path dstPath; if (srcToStore[path] != "") @@ -881,7 +908,7 @@ Path EvalState::coerceToPath(Value & v, PathSet & context) { string path = coerceToString(v, context, false, false); if (path == "" || path[0] != '/') - throw EvalError(format("string `%1%' doesn't represent an absolute path") % path); + throwEvalError("string `%1%' doesn't represent an absolute path", path); return path; } @@ -932,7 +959,7 @@ bool EvalState::eqValues(Value & v1, Value & v2) return false; default: - throw Error(format("cannot compare %1% with %2%") % showType(v1) % showType(v2)); + throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2)); } } @@ -941,14 +968,14 @@ void EvalState::printStats() { char x; bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0"; - printMsg(showStats ? lvlInfo : lvlDebug, - format("evaluated %1% expressions, used %2% bytes of stack space, allocated %3% values, allocated %4% environments") - % nrEvaluated - % (&x - deepestStack) - % nrValues - % nrEnvs); - if (showStats) - printATermMapStats(); + Verbosity v = showStats ? lvlInfo : lvlDebug; + printMsg(v, "evaluation statistics:"); + printMsg(v, format(" expressions evaluated: %1%") % nrEvaluated); + printMsg(v, format(" stack space used: %1% bytes") % (&x - deepestStack)); + printMsg(v, format(" max eval() nesting depth: %1%") % maxRecursionDepth); + printMsg(v, format(" stack space per eval() level: %1% bytes") % ((&x - deepestStack) / (float) maxRecursionDepth)); + printMsg(v, format(" values allocated: %1%") % nrValues); + printMsg(v, format(" environments allocated: %1%") % nrEnvs); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 0ea474447..fb4dd802e 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -144,10 +144,6 @@ public: private: SrcToStore srcToStore; - unsigned long nrValues; - unsigned long nrEnvs; - unsigned long nrEvaluated; - bool allowUnsafeEquality; ATermMap parseTrees; @@ -155,6 +151,7 @@ private: public: EvalState(); + ~EvalState(); /* Evaluate an expression read from the given file to normal form. */ @@ -242,6 +239,17 @@ public: /* Print statistics. */ void printStats(); + +private: + + unsigned long nrValues; + unsigned long nrEnvs; + unsigned long nrEvaluated; + unsigned int recursionDepth; + unsigned int maxRecursionDepth; + char * deepestStack; /* for measuring stack usage */ + + friend class RecursionCounter; }; From c3f228f296321991ef54e46fc621a292824b13e1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Apr 2010 09:14:27 +0000 Subject: [PATCH 051/103] --- src/libexpr/eval-test.cc | 113 ++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index eac95094f..77861dea9 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -10,9 +10,8 @@ using namespace nix; -void doTest(string s) +void doTest(EvalState & state, string s) { - EvalState state; Expr e = parseExprFromString(state, s, absPath(".")); printMsg(lvlError, format(">>>>> %1%") % e); Value v; @@ -24,61 +23,65 @@ void doTest(string s) void run(Strings args) { + EvalState state; + printMsg(lvlError, format("size of value: %1% bytes") % sizeof(Value)); - doTest("123"); - doTest("{ x = 1; y = 2; }"); - doTest("{ x = 1; y = 2; }.y"); - doTest("rec { x = 1; y = x; }.y"); - doTest("(x: x) 1"); - doTest("(x: y: y) 1 2"); - doTest("x: x"); - doTest("({x, y}: x) { x = 1; y = 2; }"); - doTest("({x, y}@args: args.x) { x = 1; y = 2; }"); - doTest("(args@{x, y}: args.x) { x = 1; y = 2; }"); - doTest("({x ? 1}: x) { }"); - doTest("({x ? 1, y ? x}: y) { x = 2; }"); - doTest("({x, y, ...}: x) { x = 1; y = 2; z = 3; }"); - doTest("({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); - //doTest("({x ? y, y ? x}: y) { }"); - doTest("let x = 1; in x"); - doTest("with { x = 1; }; x"); - doTest("let x = 2; in with { x = 1; }; x"); // => 2 - doTest("with { x = 1; }; with { x = 2; }; x"); // => 1 - doTest("[ 1 2 3 ]"); - doTest("[ 1 2 ] ++ [ 3 4 5 ]"); - doTest("123 == 123"); - doTest("123 == 456"); - doTest("let id = x: x; in [1 2] == [(id 1) (id 2)]"); - doTest("let id = x: x; in [1 2] == [(id 1) (id 3)]"); - doTest("[1 2] == [3 (let x = x; in x)]"); - doTest("{ x = 1; y.z = 2; } == { y = { z = 2; }; x = 1; }"); - doTest("{ x = 1; y = 2; } == { x = 2; }"); - doTest("{ x = [ 1 2 ]; } == { x = [ 1 ] ++ [ 2 ]; }"); - doTest("1 != 1"); - doTest("true"); - doTest("builtins.true"); - doTest("true == false"); - doTest("__head [ 1 2 3 ]"); - doTest("__add 1 2"); - doTest("null"); - doTest("null"); - doTest("\"foo\""); - doTest("let s = \"bar\"; in \"foo${s}\""); - doTest("if true then 1 else 2"); - doTest("if false then 1 else 2"); - doTest("if false || true then 1 else 2"); - doTest("let x = x; in if true || x then 1 else 2"); - doTest("/etc/passwd"); - //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; }"); - doTest("let x = 1; as = rec { inherit x; y = as.x; }; in as.y"); - doTest("let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); - doTest("let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); - doTest("builtins.toXML 123"); - doTest("builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); + doTest(state, "123"); + doTest(state, "{ x = 1; y = 2; }"); + doTest(state, "{ x = 1; y = 2; }.y"); + doTest(state, "rec { x = 1; y = x; }.y"); + doTest(state, "(x: x) 1"); + doTest(state, "(x: y: y) 1 2"); + doTest(state, "x: x"); + doTest(state, "({x, y}: x) { x = 1; y = 2; }"); + doTest(state, "({x, y}@args: args.x) { x = 1; y = 2; }"); + doTest(state, "(args@{x, y}: args.x) { x = 1; y = 2; }"); + doTest(state, "({x ? 1}: x) { }"); + doTest(state, "({x ? 1, y ? x}: y) { x = 2; }"); + doTest(state, "({x, y, ...}: x) { x = 1; y = 2; z = 3; }"); + doTest(state, "({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); + //doTest(state, "({x ? y, y ? x}: y) { }"); + doTest(state, "let x = 1; in 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, "[ 1 2 3 ]"); + doTest(state, "[ 1 2 ] ++ [ 3 4 5 ]"); + doTest(state, "123 == 123"); + doTest(state, "123 == 456"); + doTest(state, "let id = x: x; in [1 2] == [(id 1) (id 2)]"); + doTest(state, "let id = x: x; in [1 2] == [(id 1) (id 3)]"); + doTest(state, "[1 2] == [3 (let x = x; in x)]"); + doTest(state, "{ x = 1; y.z = 2; } == { y = { z = 2; }; x = 1; }"); + doTest(state, "{ x = 1; y = 2; } == { x = 2; }"); + doTest(state, "{ x = [ 1 2 ]; } == { x = [ 1 ] ++ [ 2 ]; }"); + doTest(state, "1 != 1"); + doTest(state, "true"); + doTest(state, "builtins.true"); + doTest(state, "true == false"); + doTest(state, "__head [ 1 2 3 ]"); + doTest(state, "__add 1 2"); + doTest(state, "null"); + doTest(state, "null"); + doTest(state, "\"foo\""); + doTest(state, "let s = \"bar\"; in \"foo${s}\""); + doTest(state, "if true then 1 else 2"); + doTest(state, "if false then 1 else 2"); + doTest(state, "if false || true then 1 else 2"); + doTest(state, "let x = x; in if true || x then 1 else 2"); + doTest(state, "/etc/passwd"); + //doTest(state, "import ./foo.nix"); + doTest(state, "map (x: __add 1 x) [ 1 2 3 ]"); + doTest(state, "map (builtins.add 1) [ 1 2 3 ]"); + doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); + doTest(state, "let x = 1; as = rec { inherit x; y = as.x; }; in as.y"); + doTest(state, "let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); + doTest(state, "let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); + doTest(state, "builtins.toXML 123"); + doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); + + state.printStats(); } From 4e490025767093e287a8b7b01de52fee7ae36830 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Apr 2010 09:45:00 +0000 Subject: [PATCH 052/103] * Doh. --- src/libexpr/eval.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 56a73b494..02f5e3dc8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -165,7 +165,7 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2)) LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s2)) { - throw TypeError(format(s) % s2); + throw AssertionError(format(s) % s2); } LocalNoInline(void addErrorPrefix(Error & e, const char * s)) From db90b88e655a0d8e501beddee966a124b2f097d8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Apr 2010 09:50:20 +0000 Subject: [PATCH 053/103] * Hack to support builderDefs expressions. --- src/libexpr/eval.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 02f5e3dc8..7434aa842 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -918,8 +918,14 @@ bool EvalState::eqValues(Value & v1, Value & v2) forceValue(v1); forceValue(v2); + /* !!! Hack to support some old broken code that relies on pointer + equality tests between attribute sets. (Specifically, + builderDefs calls uniqList on a list of attribute sets.) Will + remove this eventually. */ + if (&v1 == &v2) return true; + if (v1.type != v2.type) return false; - + switch (v1.type) { case tInt: From ed711f73bce8786b1a37bd718eb97276d0916484 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Apr 2010 10:38:18 +0000 Subject: [PATCH 054/103] * Don't use ATerms to represent integers in the lexer. --- src/libexpr/lexer.l | 2 +- src/libexpr/parser.y | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 81aec99e1..82c350020 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -107,7 +107,7 @@ inherit { return INHERIT; } {ID} { yylval->t = toATerm(yytext); return ID; /* !!! alloc */ } {INT} { int n = atoi(yytext); /* !!! overflow */ - yylval->t = ATmake("", n); + yylval->n = n; return INT; } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 3463a5c5d..a28d56d24 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -317,6 +317,7 @@ static void freeAndUnprotect(void * p) ATermList formals; bool ellipsis; } formals; + int n; } %type start expr expr_function expr_if expr_op @@ -324,7 +325,8 @@ static void freeAndUnprotect(void * p) %type pattern %type binds ids attrpath expr_list string_parts ind_string_parts %type formals -%token ID INT STR IND_STR PATH URI +%token ID STR IND_STR PATH URI +%token INT %token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL %token DOLLAR_CURLY /* == ${ */ %token IND_STRING_OPEN IND_STRING_CLOSE @@ -393,7 +395,7 @@ expr_select expr_simple : ID { $$ = makeVar($1); } - | INT { $$ = makeInt(ATgetInt((ATermInt) $1)); } + | INT { $$ = makeInt($1); } | '"' string_parts '"' { /* For efficiency, and to simplify parse trees a bit. */ if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty); From 4d6ad5be1738c64b1de4274cafbd4b8f23ca287c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Apr 2010 18:30:11 +0000 Subject: [PATCH 055/103] * Don't use ATerms for the abstract syntax trees anymore. Not finished yet. --- src/libexpr/Makefile.am | 15 +- src/libexpr/attr-path.cc | 5 +- src/libexpr/attr-path.hh | 2 +- src/libexpr/common-opts.cc | 4 +- src/libexpr/eval-test.cc | 22 +- src/libexpr/eval.cc | 467 +++++++++++++------------ src/libexpr/eval.hh | 46 ++- src/libexpr/get-drvs.cc | 21 +- src/libexpr/lexer.l | 24 +- src/libexpr/nixexpr-ast.def | 97 ----- src/libexpr/nixexpr.cc | 110 ++++-- src/libexpr/nixexpr.hh | 163 +++++++-- src/libexpr/parser.hh | 5 +- src/libexpr/parser.y | 191 +++++----- src/libexpr/primops.cc | 38 +- src/libexpr/value-to-xml.cc | 43 +-- src/libmain/shared.cc | 4 +- src/nix-env/nix-env.cc | 5 +- src/nix-instantiate/nix-instantiate.cc | 10 +- 19 files changed, 693 insertions(+), 579 deletions(-) delete mode 100644 src/libexpr/nixexpr-ast.def diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am index 99f742ffe..60b1815d9 100644 --- a/src/libexpr/Makefile.am +++ b/src/libexpr/Makefile.am @@ -8,15 +8,15 @@ libexpr_la_SOURCES = \ pkginclude_HEADERS = \ nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \ get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \ - names.hh nixexpr-ast.hh + names.hh libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \ ../boost/format/libformat.la -BUILT_SOURCES = nixexpr-ast.cc nixexpr-ast.hh \ +BUILT_SOURCES = \ parser-tab.hh lexer-tab.hh parser-tab.cc lexer-tab.cc -EXTRA_DIST = lexer.l parser.y nixexpr-ast.def nixexpr-ast.cc +EXTRA_DIST = lexer.l parser.y AM_CXXFLAGS = \ -I$(srcdir)/.. ${aterm_include} \ @@ -34,15 +34,6 @@ lexer-tab.cc lexer-tab.hh: lexer.l $(flex) --outfile lexer-tab.cc --header-file=lexer-tab.hh $(srcdir)/lexer.l -# ATerm helper function generation. - -nixexpr-ast.cc nixexpr-ast.hh: ../aterm-helper.pl nixexpr-ast.def - $(perl) $(srcdir)/../aterm-helper.pl nixexpr-ast.hh nixexpr-ast.cc < $(srcdir)/nixexpr-ast.def - - -CLEANFILES = - - # SDF stuff (not built by default). nix.tbl: nix.sdf sdf2table -m Nix -s -i nix.sdf -o nix.tbl diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 5f1c0ad78..aa421ab43 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -1,5 +1,4 @@ #include "attr-path.hh" -#include "nixexpr-ast.hh" #include "util.hh" @@ -7,7 +6,7 @@ namespace nix { void findAlongAttrPath(EvalState & state, const string & attrPath, - const Bindings & autoArgs, Expr e, Value & v) + const Bindings & autoArgs, Expr * e, Value & v) { Strings tokens = tokenizeString(attrPath, "."); @@ -46,7 +45,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath, format("the expression selected by the selection path `%1%' should be an attribute set but is %2%") % curPath % showType(v)); - Bindings::iterator a = v.attrs->find(toATerm(attr)); + Bindings::iterator a = v.attrs->find(attr); if (a == v.attrs->end()) throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath); v = a->second; diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh index 518a7a95d..33587e5ed 100644 --- a/src/libexpr/attr-path.hh +++ b/src/libexpr/attr-path.hh @@ -11,7 +11,7 @@ namespace nix { void findAlongAttrPath(EvalState & state, const string & attrPath, - const Bindings & autoArgs, Expr e, Value & v); + const Bindings & autoArgs, Expr * e, Value & v); } diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc index d5d3df40a..6171de0f0 100644 --- a/src/libexpr/common-opts.cc +++ b/src/libexpr/common-opts.cc @@ -20,10 +20,10 @@ bool parseOptionArg(const string & arg, Strings::iterator & i, if (i == argsEnd) throw error; string value = *i++; - Value & v(autoArgs[toATerm(name)]); + Value & v(autoArgs[name]); if (arg == "--arg") - state.mkThunk_(v, parseExprFromString(state, value, absPath("."))); + state.mkThunk_(v, parseExprFromString(value, absPath("."))); else mkString(v, value); diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 77861dea9..32f4940df 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -2,8 +2,8 @@ #include "parser.hh" #include "hash.hh" #include "util.hh" -#include "nixexpr-ast.hh" +#include #include #include @@ -12,8 +12,8 @@ using namespace nix; void doTest(EvalState & state, string s) { - Expr e = parseExprFromString(state, s, absPath(".")); - printMsg(lvlError, format(">>>>> %1%") % e); + Expr * e = parseExprFromString(s, absPath(".")); + std::cerr << ">>>>> " << *e << std::endl; Value v; state.eval(e, v); state.strictForceValue(v); @@ -24,8 +24,10 @@ void doTest(EvalState & state, string s) void run(Strings args) { EvalState state; - + printMsg(lvlError, format("size of value: %1% bytes") % sizeof(Value)); + printMsg(lvlError, format("size of int AST node: %1% bytes") % sizeof(ExprInt)); + printMsg(lvlError, format("size of attrset AST node: %1% bytes") % sizeof(ExprAttrs)); doTest(state, "123"); doTest(state, "{ x = 1; y = 2; }"); @@ -53,7 +55,7 @@ void run(Strings args) doTest(state, "let id = x: x; in [1 2] == [(id 1) (id 2)]"); doTest(state, "let id = x: x; in [1 2] == [(id 1) (id 3)]"); doTest(state, "[1 2] == [3 (let x = x; in x)]"); - doTest(state, "{ x = 1; y.z = 2; } == { y = { z = 2; }; x = 1; }"); + //doTest(state, "{ x = 1; y.z = 2; } == { y = { z = 2; }; x = 1; }"); doTest(state, "{ x = 1; y = 2; } == { x = 2; }"); doTest(state, "{ x = [ 1 2 ]; } == { x = [ 1 ] ++ [ 2 ]; }"); doTest(state, "1 != 1"); @@ -63,23 +65,23 @@ void run(Strings args) doTest(state, "__head [ 1 2 3 ]"); doTest(state, "__add 1 2"); doTest(state, "null"); - doTest(state, "null"); - doTest(state, "\"foo\""); - doTest(state, "let s = \"bar\"; in \"foo${s}\""); + //doTest(state, "\"foo\""); + //doTest(state, "let s = \"bar\"; in \"foo${s}\""); doTest(state, "if true then 1 else 2"); doTest(state, "if false then 1 else 2"); doTest(state, "if false || true then 1 else 2"); doTest(state, "let x = x; in if true || x then 1 else 2"); + doTest(state, "http://nixos.org/"); doTest(state, "/etc/passwd"); //doTest(state, "import ./foo.nix"); doTest(state, "map (x: __add 1 x) [ 1 2 3 ]"); doTest(state, "map (builtins.add 1) [ 1 2 3 ]"); - doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); + //doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); doTest(state, "let x = 1; as = rec { inherit x; y = as.x; }; in as.y"); doTest(state, "let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); doTest(state, "let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); doTest(state, "builtins.toXML 123"); - doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); + //doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); state.printStats(); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 7434aa842..8ead986b8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -4,7 +4,6 @@ #include "util.hh" #include "store-api.hh" #include "derivations.hh" -#include "nixexpr-ast.hh" #include "globals.hh" #include @@ -45,7 +44,7 @@ std::ostream & operator << (std::ostream & str, Value & v) case tAttrs: str << "{ "; foreach (Bindings::iterator, i, *v.attrs) - str << aterm2String(i->first) << " = " << i->second << "; "; + str << i->first << " = " << i->second << "; "; str << "}"; break; case tList: @@ -96,8 +95,6 @@ EvalState::EvalState() : baseEnv(allocEnv()) nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0; deepestStack = (char *) -1; - initNixExprHelpers(); - createBaseEnv(); allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == ""; @@ -112,9 +109,9 @@ EvalState::~EvalState() void EvalState::addConstant(const string & name, Value & v) { - baseEnv.bindings[toATerm(name)] = v; + baseEnv.bindings[name] = v; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; - (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm(name2)] = v; + (*baseEnv.bindings["builtins"].attrs)[name2] = v; nrValues += 2; } @@ -126,9 +123,9 @@ void EvalState::addPrimOp(const string & name, v.type = tPrimOp; v.primOp.arity = arity; v.primOp.fun = primOp; - baseEnv.bindings[toATerm(name)] = v; + baseEnv.bindings[name] = v; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; - (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm(name2)] = v; + (*baseEnv.bindings["builtins"].attrs)[name2] = v; nrValues += 2; } @@ -212,12 +209,12 @@ void mkPath(Value & v, const char * s) } -static Value * lookupWith(Env * env, Sym name) +static Value * lookupWith(Env * env, const Sym & name) { if (!env) return 0; Value * v = lookupWith(env->up, name); if (v) return v; - Bindings::iterator i = env->bindings.find(sWith); + Bindings::iterator i = env->bindings.find(""); if (i == env->bindings.end()) return 0; Bindings::iterator j = i->second.attrs->find(name); if (j != i->second.attrs->end()) return &j->second; @@ -225,7 +222,7 @@ static Value * lookupWith(Env * env, Sym name) } -static Value * lookupVar(Env * env, Sym name) +static Value * lookupVar(Env * env, const Sym & name) { /* First look for a regular variable binding for `name'. */ for (Env * env2 = env; env2; env2 = env2->up) { @@ -251,7 +248,7 @@ static Value * lookupVar(Env * env, Sym name) } #endif - throwEvalError("undefined variable `%1%'", aterm2String(name)); + throwEvalError("undefined variable `%1%'", name); } @@ -284,7 +281,7 @@ void EvalState::mkAttrs(Value & v) } -void EvalState::mkThunk_(Value & v, Expr expr) +void EvalState::mkThunk_(Value & v, Expr * expr) { mkThunk(v, baseEnv, expr); } @@ -302,11 +299,11 @@ void EvalState::evalFile(const Path & path, Value & v) { startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path); - Expr e = parseTrees.get(toATerm(path)); + Expr * e = parseTrees[path]; if (!e) { - e = parseExprFromFile(*this, path); - parseTrees.set(toATerm(path), e); + e = parseExprFromFile(path); + parseTrees[path] = e; } try { @@ -334,7 +331,7 @@ struct RecursionCounter }; -void EvalState::eval(Env & env, Expr e, Value & v) +void EvalState::eval(Env & env, Expr * e, Value & v) { /* When changing this function, make sure that you don't cause a (large) increase in stack consumption! */ @@ -350,6 +347,9 @@ void EvalState::eval(Env & env, Expr e, Value & v) nrEvaluated++; + e->eval(*this, env, v); + +#if 0 Sym name; int n; ATerm s; ATermList context, es; @@ -357,138 +357,6 @@ void EvalState::eval(Env & env, Expr e, Value & v) 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; - } - - else if (matchInt(e, n)) - mkInt(v, n); - - else if (matchStr(e, s, context)) { - assert(context == ATempty); - mkString(v, ATgetName(ATgetAFun(s))); - } - - else if (matchPath(e, s)) - mkPath(v, ATgetName(ATgetAFun(s))); - - else if (matchAttrs(e, es)) { - mkAttrs(v); - ATerm e2, pos; - for (ATermIterator i(es); i; ++i) { - if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value & v2 = (*v.attrs)[name]; - nrValues++; - mkThunk(v2, env, e2); - } - } - - else if (matchRec(e, rbnds, nrbnds)) { - /* Create a new environment that contains the attributes in - this `rec'. */ - Env & env2(allocEnv()); - env2.up = &env; - - v.type = tAttrs; - v.attrs = &env2.bindings; - - /* The recursive attributes are evaluated in the new - environment. */ - ATerm name, e2, pos; - for (ATermIterator i(rbnds); i; ++i) { - if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value & v2 = env2.bindings[name]; - nrValues++; - mkThunk(v2, env2, e2); - } - - /* The non-recursive attributes, on the other hand, are - evaluated in the original environment. */ - for (ATermIterator i(nrbnds); i; ++i) { - if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ - Value & v2 = env2.bindings[name]; - nrValues++; - mkThunk(v2, env, e2); - } - } - - else if (matchSelect(e, e2, name)) { - Value v2; - eval(env, e2, v2); - forceAttrs(v2); // !!! eval followed by force is slightly inefficient - Bindings::iterator i = v2.attrs->find(name); - if (i == v2.attrs->end()) - throwEvalError("attribute `%1%' missing", aterm2String(name)); - try { - forceValue(i->second); - } catch (Error & e) { - addErrorPrefix(e, "while evaluating the attribute `%1%':\n", aterm2String(name)); - throw; - } - v = i->second; - } - - else if (matchFunction(e, pat, body, pos)) { - v.type = tLambda; - v.lambda.env = &env; - v.lambda.pat = pat; - v.lambda.body = body; - } - - else if (matchCall(e, fun, arg)) { - Value vFun; - eval(env, fun, vFun); - Value vArg; - mkThunk(vArg, env, arg); // !!! should this be on the heap? - callFunction(vFun, vArg, v); - } - - else if (matchWith(e, attrs, body, pos)) { - Env & env2(allocEnv()); - env2.up = &env; - - Value & vAttrs = env2.bindings[sWith]; - nrValues++; - eval(env, attrs, vAttrs); - forceAttrs(vAttrs); - - eval(env2, body, v); - } - - 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)); - } - - else if (matchOpEq(e, e1, e2)) { - Value v1; eval(env, e1, v1); - Value v2; eval(env, e2, v2); - mkBool(v, eqValues(v1, v2)); - } - - else if (matchOpNEq(e, e1, e2)) { - Value v1; eval(env, e1, v1); - Value v2; eval(env, e2, v2); - mkBool(v, !eqValues(v1, v2)); - } - - else if (matchOpConcat(e, e1, e2)) { - Value v1; eval(env, e1, v1); - forceList(v1); - Value v2; eval(env, e2, v2); - forceList(v2); - mkList(v, v1.list.length + v2.list.length); - /* !!! This loses sharing with the original lists. We could - use a tCopy node, but that would use more memory. */ - for (unsigned int n = 0; n < v1.list.length; ++n) - 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]; - } - else if (matchConcatStrings(e, es)) { PathSet context; std::ostringstream s; @@ -521,10 +389,6 @@ void EvalState::eval(Env & env, Expr e, Value & v) mkString(v, s.str(), context); } - /* Conditionals. */ - else if (matchIf(e, e1, e2, e3)) - eval(env, evalBool(env, e1) ? e2 : e3, v); - /* Assertions. */ else if (matchAssert(e, e1, e2, pos)) { if (!evalBool(env, e1)) @@ -536,30 +400,6 @@ void EvalState::eval(Env & env, Expr e, Value & v) 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)); - - /* Attribute set update (//). */ - else if (matchOpUpdate(e, e1, e2)) { - Value v2; - eval(env, e1, v2); - - cloneAttrs(v2, v); - - eval(env, e2, v2); - foreach (Bindings::iterator, i, *v2.attrs) - (*v.attrs)[i->first] = i->second; // !!! sharing - } - /* Attribute existence test (?). */ else if (matchOpHasAttr(e, e1, name)) { Value vAttrs; @@ -567,8 +407,130 @@ void EvalState::eval(Env & env, Expr e, Value & v) forceAttrs(vAttrs); mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end()); } +#endif +} - else abort(); + +void EvalState::eval(Expr * e, Value & v) +{ + eval(baseEnv, e, v); +} + + +bool EvalState::evalBool(Env & env, Expr * e) +{ + Value v; + eval(env, e, v); + if (v.type != tBool) + throwTypeError("value is %1% while a Boolean was expected", showType(v)); + return v.boolean; +} + + +void ExprInt::eval(EvalState & state, Env & env, Value & v) +{ + mkInt(v, n); +} + + +void ExprString::eval(EvalState & state, Env & env, Value & v) +{ + mkString(v, s.c_str()); +} + + +void ExprPath::eval(EvalState & state, Env & env, Value & v) +{ + mkPath(v, s.c_str()); +} + + +void ExprAttrs::eval(EvalState & state, Env & env, Value & v) +{ + if (recursive) { + + /* Create a new environment that contains the attributes in + this `rec'. */ + Env & env2(state.allocEnv()); + env2.up = &env; + + v.type = tAttrs; + v.attrs = &env2.bindings; + + /* The recursive attributes are evaluated in the new + environment. */ + foreach (Attrs::iterator, i, attrs) { + Value & v2 = env2.bindings[i->first]; + mkThunk(v2, env2, i->second); + } + + /* The inherited attributes, on the other hand, are + evaluated in the original environment. */ + foreach (list::iterator, i, inherited) { + Value & v2 = env2.bindings[*i]; + mkCopy(v2, *lookupVar(&env, *i)); + } + } + + else { + state.mkAttrs(v); + foreach (Attrs::iterator, i, attrs) { + Value & v2 = (*v.attrs)[i->first]; + mkThunk(v2, env, i->second); + } + } +} + + +void ExprList::eval(EvalState & state, Env & env, Value & v) +{ + state.mkList(v, elems.size()); + for (unsigned int n = 0; n < v.list.length; ++n) + mkThunk(v.list.elems[n], env, elems[n]); +} + + +void ExprVar::eval(EvalState & state, Env & env, Value & v) +{ + Value * v2 = lookupVar(&env, name); + state.forceValue(*v2); + v = *v2; +} + + +void ExprSelect::eval(EvalState & state, Env & env, Value & v) +{ + Value v2; + state.eval(env, e, v2); + state.forceAttrs(v2); // !!! eval followed by force is slightly inefficient + Bindings::iterator i = v2.attrs->find(name); + if (i == v2.attrs->end()) + throwEvalError("attribute `%1%' missing", name); + try { + state.forceValue(i->second); + } catch (Error & e) { + addErrorPrefix(e, "while evaluating the attribute `%1%':\n", name); + throw; + } + v = i->second; +} + + +void ExprLambda::eval(EvalState & state, Env & env, Value & v) +{ + v.type = tLambda; + v.lambda.env = &env; + v.lambda.fun = this; +} + + +void ExprApp::eval(EvalState & state, Env & env, Value & v) +{ + Value vFun; + state.eval(env, e1, vFun); + Value vArg; + mkThunk(vArg, env, e2); // !!! should this be on the heap? + state.callFunction(vFun, vArg, v); } @@ -613,19 +575,17 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) Env & env2(allocEnv()); env2.up = fun.lambda.env; - ATermList formals; ATerm ellipsis, name; - - if (matchVarPat(fun.lambda.pat, name)) { - Value & vArg = env2.bindings[name]; + if (!fun.lambda.fun->matchAttrs) { + Value & vArg = env2.bindings[fun.lambda.fun->arg]; nrValues++; vArg = arg; } - else if (matchAttrsPat(fun.lambda.pat, formals, ellipsis, name)) { + else { forceAttrs(arg); - if (name != sNoAlias) { - env2.bindings[name] = arg; + if (!fun.lambda.fun->arg.empty()) { + env2.bindings[fun.lambda.fun->arg] = arg; nrValues++; } @@ -633,21 +593,15 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) there is no matching actual argument but the formal argument has a default, use the default. */ unsigned int attrsUsed = 0; - for (ATermIterator i(formals); i; ++i) { - Expr def; Sym name; - DefaultValue def2; - if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ - - Bindings::iterator j = arg.attrs->find(name); + foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) { + Bindings::iterator j = arg.attrs->find(i->name); - Value & v = env2.bindings[name]; + Value & v = env2.bindings[i->name]; nrValues++; if (j == arg.attrs->end()) { - if (!matchDefaultValue(def2, def)) def = 0; - if (def == 0) throwTypeError("the argument named `%1%' required by the function is missing", - aterm2String(name)); - mkThunk(v, env2, def); + if (!i->def) throwTypeError("the argument named `%1%' required by the function is missing", i->name); + mkThunk(v, env2, i->def); } else { attrsUsed++; mkCopy(v, j->second); @@ -658,13 +612,11 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) argument (unless the attribute match specifies a `...'). TODO: show the names of the expected/unexpected arguments. */ - if (ellipsis == eFalse && attrsUsed != arg.attrs->size()) + if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size()) throwTypeError("function called with unexpected argument"); } - else abort(); - - eval(env2, fun.lambda.body, v); + eval(env2, fun.lambda.fun->body, v); } @@ -672,45 +624,114 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res { forceValue(fun); - ATerm name; - ATermList formals; - ATermBool ellipsis; - - if (fun.type != tLambda || !matchAttrsPat(fun.lambda.pat, formals, ellipsis, name)) { + if (fun.type != tLambda || !fun.lambda.fun->matchAttrs) { res = fun; return; } Value actualArgs; mkAttrs(actualArgs); - - for (ATermIterator i(formals); i; ++i) { - Expr name, def; ATerm def2; - if (!matchFormal(*i, name, def2)) abort(); - Bindings::const_iterator j = args.find(name); + + foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) { + Bindings::const_iterator j = args.find(i->name); if (j != args.end()) - (*actualArgs.attrs)[name] = j->second; - else if (!matchDefaultValue(def2, def)) - throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", aterm2String(name)); + (*actualArgs.attrs)[i->name] = j->second; + else if (!i->def) + throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", i->name); } callFunction(fun, actualArgs, res); } -void EvalState::eval(Expr e, Value & v) +void ExprWith::eval(EvalState & state, Env & env, Value & v) { - eval(baseEnv, e, v); + Env & env2(state.allocEnv()); + env2.up = &env; + + Value & vAttrs = env2.bindings[""]; + state.eval(env, attrs, vAttrs); + state.forceAttrs(vAttrs); + + state.eval(env2, body, v); } -bool EvalState::evalBool(Env & env, Expr e) +void ExprIf::eval(EvalState & state, Env & env, Value & v) { - Value v; - eval(env, e, v); - if (v.type != tBool) - throwTypeError("value is %1% while a Boolean was expected", showType(v)); - return v.boolean; + state.eval(env, state.evalBool(env, cond) ? then : else_, v); +} + + +void ExprOpEq::eval(EvalState & state, Env & env, Value & v) +{ + Value v1; state.eval(env, e1, v1); + Value v2; state.eval(env, e2, v2); + mkBool(v, state.eqValues(v1, v2)); +} + + +void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) +{ + Value v1; state.eval(env, e1, v1); + Value v2; state.eval(env, e2, v2); + mkBool(v, !state.eqValues(v1, v2)); +} + + +void ExprOpAnd::eval(EvalState & state, Env & env, Value & v) +{ + mkBool(v, state.evalBool(env, e1) && state.evalBool(env, e2)); +} + + +void ExprOpOr::eval(EvalState & state, Env & env, Value & v) +{ + mkBool(v, state.evalBool(env, e1) || state.evalBool(env, e2)); +} + + +void ExprOpImpl::eval(EvalState & state, Env & env, Value & v) +{ + mkBool(v, !state.evalBool(env, e1) || state.evalBool(env, e2)); +} + + +void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) +{ + Value v2; + state.eval(env, e1, v2); + state.forceAttrs(v2); + + state.cloneAttrs(v2, v); + + state.eval(env, e2, v2); + state.forceAttrs(v2); + + foreach (Bindings::iterator, i, *v2.attrs) + (*v.attrs)[i->first] = i->second; // !!! sharing +} + + +void ExprOpConcatStrings::eval(EvalState & state, Env & env, Value & v) +{ + abort(); +} + + +void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) +{ + Value v1; state.eval(env, e1, v1); + state.forceList(v1); + Value v2; state.eval(env, e2, v2); + state.forceList(v2); + state.mkList(v, v1.list.length + v2.list.length); + /* !!! This loses sharing with the original lists. We could use a + tCopy node, but that would use more memory. */ + for (unsigned int n = 0; n < v1.list.length; ++n) + 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]; } @@ -827,7 +848,7 @@ string EvalState::forceStringNoCtx(Value & v) bool EvalState::isDerivation(Value & v) { if (v.type != tAttrs) return false; - Bindings::iterator i = v.attrs->find(toATerm("type")); + Bindings::iterator i = v.attrs->find("type"); return i != v.attrs->end() && forceStringNoCtx(i->second) == "derivation"; } @@ -871,7 +892,7 @@ string EvalState::coerceToString(Value & v, PathSet & context, } if (v.type == tAttrs) { - Bindings::iterator i = v.attrs->find(toATerm("outPath")); + Bindings::iterator i = v.attrs->find("outPath"); if (i == v.attrs->end()) throwTypeError("cannot coerce an attribute set (except a derivation) to a string"); return coerceToString(i->second, context, coerceMore, copyToStore); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index fb4dd802e..cbdd09085 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -3,7 +3,6 @@ #include -#include "aterm.hh" #include "nixexpr.hh" @@ -15,7 +14,7 @@ class EvalState; struct Env; struct Value; -typedef ATerm Sym; +typedef string Sym; typedef std::map Bindings; @@ -55,10 +54,32 @@ struct Value { int integer; bool boolean; + + /* Strings in the evaluator carry a so-called `context' (the + ATermList) which is a list of strings representing store + paths. This is to allow users to write things like + + "--with-freetype2-library=" + freetype + "/lib" + + where `freetype' is a derivation (or a source to be copied + to the store). If we just concatenated the strings without + keeping track of the referenced store paths, then if the + string is used as a derivation attribute, the derivation + will not have the correct dependencies in its inputDrvs and + inputSrcs. + + The semantics of the context is as follows: when a string + with context C is used as a derivation attribute, then the + derivations in C will be added to the inputDrvs of the + derivation, and the other store paths in C will be added to + the inputSrcs of the derivations. + + For canonicity, the store paths should be in sorted order. */ struct { const char * s; const char * * context; } string; + const char * path; Bindings * attrs; struct { @@ -67,15 +88,14 @@ struct Value } list; struct { Env * env; - Expr expr; + Expr * expr; } thunk; struct { Value * left, * right; } app; struct { Env * env; - Pattern pat; - Expr body; + ExprLambda * fun; } lambda; Value * val; struct { @@ -104,7 +124,7 @@ static inline void mkBool(Value & v, bool b) } -static inline void mkThunk(Value & v, Env & env, Expr expr) +static inline void mkThunk(Value & v, Env & env, Expr * expr) { v.type = tThunk; v.thunk.env = &env; @@ -146,7 +166,7 @@ private: bool allowUnsafeEquality; - ATermMap parseTrees; + std::map parseTrees; public: @@ -159,12 +179,12 @@ public: /* Evaluate an expression to normal form, storing the result in value `v'. */ - void eval(Expr e, Value & v); - void eval(Env & env, Expr e, Value & v); + void eval(Expr * e, Value & v); + void eval(Env & env, Expr * e, Value & v); /* Evaluation the expression, then verify that it has the expected type. */ - bool evalBool(Env & env, Expr e); + bool evalBool(Env & env, Expr * e); /* If `v' is a thunk, enter it and overwrite `v' with the result of the evaluation of the thunk. If `v' is a delayed function @@ -215,12 +235,12 @@ private: void addPrimOp(const string & name, unsigned int arity, PrimOp primOp); +public: + /* Do a deep equality test between two values. That is, list elements and attributes are compared recursively. */ bool eqValues(Value & v1, Value & v2); -public: - void callFunction(Value & fun, Value & arg, Value & v); /* Automatically call a function for which each argument has a @@ -233,7 +253,7 @@ public: void mkList(Value & v, unsigned int length); void mkAttrs(Value & v); - void mkThunk_(Value & v, Expr expr); + void mkThunk_(Value & v, Expr * expr); void cloneAttrs(Value & src, Value & dst); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index f5e7242f9..95938d5c1 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -1,5 +1,4 @@ #include "get-drvs.hh" -#include "nixexpr-ast.hh" #include "util.hh" @@ -9,7 +8,7 @@ namespace nix { string DrvInfo::queryDrvPath(EvalState & state) const { if (drvPath == "") { - Bindings::iterator i = attrs->find(toATerm("drvPath")); + Bindings::iterator i = attrs->find("drvPath"); PathSet context; (string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; } @@ -20,7 +19,7 @@ string DrvInfo::queryDrvPath(EvalState & state) const string DrvInfo::queryOutPath(EvalState & state) const { if (outPath == "") { - Bindings::iterator i = attrs->find(toATerm("outPath")); + Bindings::iterator i = attrs->find("outPath"); PathSet context; (string &) outPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; } @@ -32,7 +31,7 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const { MetaInfo meta; - Bindings::iterator a = attrs->find(toATerm("meta")); + Bindings::iterator a = attrs->find("meta"); if (a == attrs->end()) return meta; /* fine, empty meta information */ state.forceAttrs(a->second); @@ -51,7 +50,7 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const for (unsigned int j = 0; j < i->second.list.length; ++j) value.stringValues.push_back(state.forceStringNoCtx(i->second.list.elems[j])); } else continue; - meta[aterm2String(i->first)] = value; + meta[i->first] = value; } return meta; @@ -114,12 +113,12 @@ static bool getDerivation(EvalState & state, Value & v, DrvInfo drv; - Bindings::iterator i = v.attrs->find(toATerm("name")); + Bindings::iterator i = v.attrs->find("name"); /* !!! We really would like to have a decent back trace here. */ if (i == v.attrs->end()) throw TypeError("derivation name missing"); drv.name = state.forceStringNoCtx(i->second); - i = v.attrs->find(toATerm("system")); + i = v.attrs->find("system"); if (i == v.attrs->end()) drv.system = "unknown"; else @@ -171,7 +170,7 @@ static void getDerivations(EvalState & state, Value & vIn, /* !!! undocumented hackery to support combining channels in nix-env.cc. */ - bool combineChannels = v.attrs->find(toATerm("_combineChannels")) != v.attrs->end(); + bool combineChannels = v.attrs->find("_combineChannels") != v.attrs->end(); /* Consider the attributes in sorted order to get more deterministic behaviour in nix-env operations (e.g. when @@ -180,12 +179,12 @@ static void getDerivations(EvalState & state, Value & vIn, precedence). */ StringSet attrs; foreach (Bindings::iterator, i, *v.attrs) - attrs.insert(aterm2String(i->first)); + attrs.insert(i->first); foreach (StringSet::iterator, i, attrs) { startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % *i); string pathPrefix2 = addToPath(pathPrefix, *i); - Value & v2((*v.attrs)[toATerm(*i)]); + Value & v2((*v.attrs)[*i]); if (combineChannels) getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); else if (getDerivation(state, v2, pathPrefix2, drvs, done)) { @@ -194,7 +193,7 @@ static void getDerivations(EvalState & state, Value & vIn, if it has a `recurseForDerivations = true' attribute. */ if (v2.type == tAttrs) { - Bindings::iterator j = v2.attrs->find(toATerm("recurseForDerivations")); + Bindings::iterator j = v2.attrs->find("recurseForDerivations"); if (j != v2.attrs->end() && state.forceBool(j->second)) getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 82c350020..f750cfd02 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -8,9 +8,7 @@ %{ -#include "aterm.hh" #include "nixexpr.hh" -#include "nixexpr-ast.hh" #define BISON_HEADER_HACK #include "parser-tab.hh" @@ -45,8 +43,9 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) } -static Expr unescapeStr(const char * s) +static Expr * unescapeStr(const char * s) { +#if 0 string t; char c; while ((c = *s++)) { @@ -66,6 +65,7 @@ static Expr unescapeStr(const char * s) else t += c; } return makeStr(toATerm(t), ATempty); +#endif } @@ -105,7 +105,7 @@ inherit { return INHERIT; } \/\/ { return UPDATE; } \+\+ { return CONCAT; } -{ID} { yylval->t = toATerm(yytext); return ID; /* !!! alloc */ } +{ID} { yylval->id = strdup(yytext); return ID; } {INT} { int n = atoi(yytext); /* !!! overflow */ yylval->n = n; return INT; @@ -117,7 +117,7 @@ inherit { return INHERIT; } shouldn't be followed by a "{". Right now "$\"" will be consumed as part of a string, rather than a "$" followed by the string terminator. Disallow "$\"" for now. */ - yylval->t = unescapeStr(yytext); /* !!! alloc */ + yylval->e = unescapeStr(yytext); return STR; } \$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; } @@ -126,31 +126,31 @@ inherit { return INHERIT; } \'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; } ([^\$\']|\$[^\{\']|\'[^\'\$])+ { - yylval->t = makeIndStr(toATerm(yytext)); + //yylval->t = makeIndStr(toATerm(yytext)); return IND_STR; } \'\'\$ { - yylval->t = makeIndStr(toATerm("$")); + //yylval->t = makeIndStr(toATerm("$")); return IND_STR; } \'\'\' { - yylval->t = makeIndStr(toATerm("''")); + //yylval->t = makeIndStr(toATerm("''")); return IND_STR; } \'\'\\. { - yylval->t = unescapeStr(yytext + 2); + //yylval->t = unescapeStr(yytext + 2); return IND_STR; } \$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; } \'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; } \' { - yylval->t = makeIndStr(toATerm("'")); + //yylval->t = makeIndStr(toATerm("'")); return IND_STR; } . return yytext[0]; /* just in case: shouldn't be reached */ -{PATH} { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ } -{URI} { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ } +{PATH} { yylval->path = strdup(yytext); return PATH; } +{URI} { yylval->uri = strdup(yytext); return URI; } [ \t\r\n]+ /* eat up whitespace */ \#[^\r\n]* /* single-line comments */ diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def deleted file mode 100644 index 36c25dcfe..000000000 --- a/src/libexpr/nixexpr-ast.def +++ /dev/null @@ -1,97 +0,0 @@ -init initNixExprHelpers - -Pos | string int int | Pos | -NoPos | | Pos | - -Function | Pattern Expr Pos | Expr | -Assert | Expr Expr Pos | Expr | -With | Expr Expr Pos | Expr | -If | Expr Expr Expr | Expr | -OpNot | Expr | Expr | -OpEq | Expr Expr | Expr | -OpNEq | Expr Expr | Expr | -OpAnd | Expr Expr | Expr | -OpOr | Expr Expr | Expr | -OpImpl | Expr Expr | Expr | -OpUpdate | Expr Expr | Expr | -OpHasAttr | Expr string | Expr | -OpPlus | Expr Expr | Expr | -OpConcat | Expr Expr | Expr | -ConcatStrings | ATermList | Expr | -Call | Expr Expr | Expr | -Select | Expr string | Expr | -Var | string | Expr | -Int | int | Expr | - -# Strings in the evaluator carry a so-called `context' (the ATermList) -# which is a list of strings representing store paths. This is to -# allow users to write things like -# -# "--with-freetype2-library=" + freetype + "/lib" -# -# where `freetype' is a derivation (or a source to be copied to the -# store). If we just concatenated the strings without keeping track -# of the referenced store paths, then if the string is used as a -# derivation attribute, the derivation will not have the correct -# dependencies in its inputDrvs and inputSrcs. -# -# The semantics of the context is as follows: when a string with -# context C is used as a derivation attribute, then the derivations in -# C will be added to the inputDrvs of the derivation, and the other -# store paths in C will be added to the inputSrcs of the derivations. -# -# For canonicity, the store paths should be in sorted order. -Str | string ATermList | Expr | -Str | string | Expr | ObsoleteStr - -# Internal to the parser, doesn't occur in ASTs. -IndStr | string | Expr | - -# A path is a reference to a file system object that is to be copied -# to the Nix store when used as a derivation attribute. When it is -# concatenated to a string (i.e., `str + path'), it is also copied and -# the resulting store path is concatenated to the string (with the -# store path in the context). If a string or path is concatenated to -# a path (i.e., `path + str' or `path + path'), the result is a new -# path (if the right-hand side is a string, the context must be -# empty). -Path | string | Expr | - -List | ATermList | Expr | -BlackHole | | Expr | -Undefined | | Expr | -Removed | | Expr | -PrimOp | int ATermBlob ATermList | Expr | -Attrs | ATermList | Expr | -Closed | Expr | Expr | -Rec | ATermList ATermList | Expr | -Bool | ATermBool | Expr | -Null | | Expr | - -Bind | string Expr Pos | ATerm | -BindAttrPath | ATermList Expr Pos | ATerm | # desugared during parsing -Bind | string Expr | ATerm | ObsoleteBind -Inherit | Expr ATermList Pos | ATerm | - -Scope | | Expr | - -VarPat | string | Pattern | -AttrsPat | ATermList ATermBool string | Pattern | # bool = `...' - -Formal | string DefaultValue | ATerm | - -DefaultValue | Expr | DefaultValue | -NoDefaultValue | | DefaultValue | - -True | | ATermBool | -False | | ATermBool | - -PrimOpDef | int ATermBlob | ATerm | - -AttrRHS | Expr Pos | ATerm | - -eTrue = makeBool(makeTrue()) -eFalse = makeBool(makeFalse()) -sOverrides = toATerm("__overrides") -sNoAlias = toATerm("") -sWith = toATerm("") diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 3c5d02b34..05dfbd322 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -1,17 +1,94 @@ #include "nixexpr.hh" #include "derivations.hh" #include "util.hh" -#include "aterm.hh" - -#include "nixexpr-ast.hh" -#include "nixexpr-ast.cc" #include namespace nix { - + +std::ostream & operator << (std::ostream & str, Expr & e) +{ + e.show(str); + return str; +} + + +void ExprInt::show(std::ostream & str) +{ + str << n; +} + +void ExprString::show(std::ostream & str) +{ + str << "\"" << s << "\""; // !!! escaping +} + +void ExprPath::show(std::ostream & str) +{ + str << s; +} + +void ExprVar::show(std::ostream & str) +{ + str << name; +} + +void ExprSelect::show(std::ostream & str) +{ + str << "(" << *e << ")." << name; +} + +void ExprAttrs::show(std::ostream & str) +{ + if (recursive) str << "rec "; + str << "{ "; + foreach (list::iterator, i, inherited) + str << "inherited " << *i << "; "; + foreach (Attrs::iterator, i, attrs) + str << i->first << " = " << *i->second << "; "; + str << "}"; +} + +void ExprList::show(std::ostream & str) +{ + str << "[ "; + foreach (vector::iterator, i, elems) + str << "(" << **i << ") "; + str << "]"; +} + +void ExprLambda::show(std::ostream & str) +{ + str << "("; + if (matchAttrs) { + str << "{ "; + bool first = true; + foreach (Formals::Formals_::iterator, i, formals->formals) { + if (first) first = false; else str << ", "; + str << i->name; + if (i->def) str << " ? " << *i->def; + } + str << " }"; + if (arg != "") str << " @ "; + } + if (arg != "") str << arg; + str << ": " << *body << ")"; +} + +void ExprWith::show(std::ostream & str) +{ + str << "with " << *attrs << "; " << *body; +} + +void ExprIf::show(std::ostream & str) +{ + str << "if " << *cond << " then " << *then << " else " << *else_; +} + + +#if 0 string showPos(ATerm pos) { ATerm path; @@ -159,28 +236,7 @@ void checkVarDefs(const ATermMap & defs, Expr e) set done; checkVarDefs2(done, defs, e); } - - -bool matchStr(Expr e, string & s, PathSet & context) -{ - ATermList l; - ATerm s_; - - if (!matchStr(e, s_, l)) return false; - - s = aterm2String(s_); - - for (ATermIterator i(l); i; ++i) - context.insert(aterm2String(*i)); - - return true; -} - - -Expr makeStr(const string & s, const PathSet & context) -{ - return makeStr(toATerm(s), toATermList(context)); -} +#endif } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 9f1050d80..ebdfd0a15 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -3,7 +3,6 @@ #include -#include "aterm-map.hh" #include "types.hh" @@ -18,22 +17,152 @@ MakeError(Abort, EvalError) MakeError(TypeError, EvalError) -/* Nix expressions are represented as ATerms. The maximal sharing - property of the ATerm library allows us to implement caching of - normals forms efficiently. */ -typedef ATerm Expr; -typedef ATerm DefaultValue; -typedef ATerm Pos; -typedef ATerm Pattern; -typedef ATerm ATermBool; +struct Pos +{ + string file; + unsigned int line, column; +}; -/* A STL vector of ATerms. Should be used with great care since it's - stored on the heap, and the elements are therefore not roots to the - ATerm garbage collector. */ -typedef vector ATermVector; +/* Abstract syntax of Nix expressions. */ + +struct Env; +struct Value; +struct EvalState; + +struct Expr +{ + virtual void show(std::ostream & str) = 0; + virtual void eval(EvalState & state, Env & env, Value & v) + { + throw Error("not implemented"); + } +}; + +std::ostream & operator << (std::ostream & str, Expr & e); + +#define COMMON_METHODS \ + void show(std::ostream & str); \ + void eval(EvalState & state, Env & env, Value & v); + +struct ExprInt : Expr +{ + int n; + ExprInt(int n) : n(n) { }; + COMMON_METHODS +}; + +struct ExprString : Expr +{ + string s; + ExprString(const string & s) : s(s) { }; + COMMON_METHODS +}; + +struct ExprPath : Expr +{ + string s; + ExprPath(const string & s) : s(s) { }; + COMMON_METHODS +}; + +struct ExprVar : Expr +{ + string name; + ExprVar(const string & name) : name(name) { }; + COMMON_METHODS +}; + +struct ExprSelect : Expr +{ + Expr * e; + string name; + ExprSelect(Expr * e, const string & name) : e(e), name(name) { }; + COMMON_METHODS +}; + +struct ExprAttrs : Expr +{ + bool recursive; + typedef std::map Attrs; + Attrs attrs; + list inherited; + ExprAttrs() : recursive(false) { }; + COMMON_METHODS +}; + +struct ExprList : Expr +{ + std::vector elems; + ExprList() { }; + COMMON_METHODS +}; + +struct Formal +{ + string name; + Expr * def; + Formal(const string & name, Expr * def) : name(name), def(def) { }; +}; + +struct Formals +{ + typedef std::list Formals_; + Formals_ formals; + bool ellipsis; +}; + +struct ExprLambda : Expr +{ + Pos pos; + string arg; + bool matchAttrs; + Formals * formals; + Expr * body; + ExprLambda(const Pos & pos, const string & arg, bool matchAttrs, Formals * formals, Expr * body) + : pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body) { }; + COMMON_METHODS +}; + +struct ExprWith : Expr +{ + Pos pos; + Expr * attrs, * body; + ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { }; + COMMON_METHODS +}; + +struct ExprIf : Expr +{ + Expr * cond, * then, * else_; + ExprIf(Expr * cond, Expr * then, Expr * else_) : cond(cond), then(then), else_(else_) { }; + COMMON_METHODS +}; + +#define MakeBinOp(name, s) \ + struct Expr##name : Expr \ + { \ + Expr * e1, * e2; \ + Expr##name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \ + void show(std::ostream & str) \ + { \ + str << *e1 << " " s " " << *e2; \ + } \ + void eval(EvalState & state, Env & env, Value & v); \ + }; + +MakeBinOp(App, "") +MakeBinOp(OpEq, "==") +MakeBinOp(OpNEq, "!=") +MakeBinOp(OpAnd, "&&") +MakeBinOp(OpOr, "||") +MakeBinOp(OpImpl, "->") +MakeBinOp(OpUpdate, "//") +MakeBinOp(OpConcatStrings, "+") +MakeBinOp(OpConcatLists, "++") +#if 0 /* Show a position. */ string showPos(ATerm pos); @@ -56,13 +185,7 @@ Expr makeAttrs(const ATermMap & attrs); /* Check whether all variables are defined in the given expression. Throw an exception if this isn't the case. */ void checkVarDefs(const ATermMap & def, Expr e); - - -/* Manipulation of Str() nodes. Note: matchStr() does not clear - context! */ -bool matchStr(Expr e, string & s, PathSet & context); - -Expr makeStr(const string & s, const PathSet & context = PathSet()); +#endif } diff --git a/src/libexpr/parser.hh b/src/libexpr/parser.hh index 3f430eb32..d1e531ca2 100644 --- a/src/libexpr/parser.hh +++ b/src/libexpr/parser.hh @@ -9,11 +9,10 @@ namespace nix { /* Parse a Nix expression from the specified file. If `path' refers to a directory, then "/default.nix" is appended. */ -Expr parseExprFromFile(EvalState & state, Path path); +Expr * parseExprFromFile(Path path); /* Parse a Nix expression from the specified string. */ -Expr parseExprFromString(EvalState & state, const string & s, - const Path & basePath); +Expr * parseExprFromString(const string & s, const Path & basePath); } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index a28d56d24..96fbe2cb4 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -23,13 +23,12 @@ #include "aterm.hh" #include "util.hh" +#include "nixexpr.hh" + #include "parser-tab.hh" #include "lexer-tab.hh" #define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4 -#include "nixexpr.hh" -#include "nixexpr-ast.hh" - using namespace nix; @@ -39,13 +38,14 @@ namespace nix { struct ParseData { - Expr result; + Expr * result; Path basePath; Path path; string error; }; - + +#if 0 static string showAttrPath(ATermList attrPath) { string s; @@ -79,10 +79,12 @@ static ATermList buildAttrs(const Tree & t, ATermList & nonrec) : makeBind(i->first, makeAttrs(buildAttrs(i->second, nonrec)), makeNoPos())); return res; } +#endif -static Expr fixAttrs(bool recursive, ATermList as) +static void fixAttrs(ExprAttrs & attrs) { +#if 0 Tree attrs; /* This ATermMap is needed to ensure that the `leaf' fields in the @@ -135,9 +137,11 @@ static Expr fixAttrs(bool recursive, ATermList as) ATermList rec = buildAttrs(attrs, nonrec); return recursive ? makeRec(rec, nonrec) : makeAttrs(rec); +#endif } +#if 0 static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat) { ATerm name = sNoAlias; @@ -261,6 +265,7 @@ static Expr stripIndentation(ATermList es) return makeConcatStrings(ATreverse(es2)); } +#endif void backToString(yyscan_t scanner); @@ -269,8 +274,11 @@ void backToIndString(yyscan_t scanner); static Pos makeCurPos(YYLTYPE * loc, ParseData * data) { - return makePos(toATerm(data->path), - loc->first_line, loc->first_column); + Pos pos; + pos.file = data->path; + pos.line = loc->first_line; + pos.column = loc->first_column; + return pos; } #define CUR_POS makeCurPos(yylocp, data) @@ -311,22 +319,31 @@ static void freeAndUnprotect(void * p) %} %union { - ATerm t; - ATermList ts; - struct { - ATermList formals; - bool ellipsis; - } formals; + nix::Expr * e; + nix::ExprList * list; + nix::ExprAttrs * attrs; + nix::Formals * formals; + nix::Formal * formal; int n; + char * id; + char * path; + char * uri; + std::list * ids; } -%type start expr expr_function expr_if expr_op -%type expr_app expr_select expr_simple bind inheritsrc formal -%type pattern -%type binds ids attrpath expr_list string_parts ind_string_parts +%type start expr expr_function expr_if expr_op +%type expr_app expr_select expr_simple +%type expr_list +%type binds +%type attrpath string_parts ind_string_parts %type formals -%token ID STR IND_STR PATH URI +%type formal +%type ids +%token ID ATTRPATH +%token STR IND_STR %token INT +%token PATH +%token URI %token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL %token DOLLAR_CURLY /* == ${ */ %token IND_STRING_OPEN IND_STRING_CLOSE @@ -350,54 +367,63 @@ start: expr { data->result = $1; }; expr: expr_function; expr_function - : pattern ':' expr_function - { checkPatternVars(CUR_POS, $1); $$ = makeFunction($1, $3, CUR_POS); } - | ASSERT expr ';' expr_function + : ID ':' expr_function + { $$ = new ExprLambda(CUR_POS, $1, false, 0, $3); /* checkPatternVars(CUR_POS, $1); $$ = makeFunction($1, $3, CUR_POS); */ } + | '{' formals '}' ':' expr_function + { $$ = new ExprLambda(CUR_POS, "", true, $2, $5); } + | '{' formals '}' '@' ID ':' expr_function + { $$ = new ExprLambda(CUR_POS, $5, true, $2, $7); } + | ID '@' '{' formals '}' ':' expr_function + { $$ = new ExprLambda(CUR_POS, $1, true, $4, $7); } + /* | ASSERT expr ';' expr_function { $$ = makeAssert($2, $4, CUR_POS); } + */ | WITH expr ';' expr_function - { $$ = makeWith($2, $4, CUR_POS); } + { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN expr_function - { $$ = makeSelect(fixAttrs(true, ATinsert($2, makeBindAttrPath(ATmakeList1(toATerm("")), $4, CUR_POS))), toATerm("")); } + { $2->attrs[""] = $4; $2->recursive = true; fixAttrs(*$2); $$ = new ExprSelect($2, ""); } | expr_if ; expr_if - : IF expr THEN expr ELSE expr - { $$ = makeIf($2, $4, $6); } + : IF expr THEN expr ELSE expr { $$ = new ExprIf($2, $4, $6); } | expr_op ; expr_op - : '!' expr_op %prec NEG { $$ = makeOpNot($2); } - | expr_op EQ expr_op { $$ = makeOpEq($1, $3); } - | expr_op NEQ expr_op { $$ = makeOpNEq($1, $3); } - | expr_op AND expr_op { $$ = makeOpAnd($1, $3); } - | expr_op OR expr_op { $$ = makeOpOr($1, $3); } - | expr_op IMPL expr_op { $$ = makeOpImpl($1, $3); } - | expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); } + : /* '!' expr_op %prec NEG { $$ = makeOpNot($2); } + | */ + expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); } + | expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); } + | expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); } + | expr_op OR expr_op { $$ = new ExprOpOr($1, $3); } + | expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); } + | expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); } + /* | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); } - | expr_op '+' expr_op { $$ = makeConcatStrings(ATmakeList2($1, $3)); } - | expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); } + */ + | expr_op '+' expr_op { $$ = new ExprOpConcatStrings($1, $3); } + | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); } | expr_app ; expr_app : expr_app expr_select - { $$ = makeCall($1, $2); } + { $$ = new ExprApp($1, $2); } | expr_select { $$ = $1; } ; expr_select : expr_select '.' ID - { $$ = makeSelect($1, $3); } + { $$ = new ExprSelect($1, $3); } | expr_simple { $$ = $1; } ; expr_simple - : ID { $$ = makeVar($1); } - | INT { $$ = makeInt($1); } + : ID { $$ = new ExprVar($1); } + | INT { $$ = new ExprInt($1); } /* | '"' string_parts '"' { - /* For efficiency, and to simplify parse trees a bit. */ + /* For efficiency, and to simplify parse trees a bit. * / if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty); else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2); else $$ = makeConcatStrings(ATreverse($2)); @@ -405,18 +431,21 @@ expr_simple | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { $$ = stripIndentation(ATreverse($2)); } - | PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); } - | URI { $$ = makeStr($1, ATempty); } + */ + | PATH { $$ = new ExprPath(absPath($1, data->basePath)); } + | URI { $$ = new ExprString($1); } | '(' expr ')' { $$ = $2; } +/* /* Let expressions `let {..., body = ...}' are just desugared - into `(rec {..., body = ...}).body'. */ + into `(rec {..., body = ...}).body'. * / | LET '{' binds '}' { $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); } + */ | REC '{' binds '}' - { $$ = fixAttrs(true, $3); } + { fixAttrs(*$3); $3->recursive = true; $$ = $3; } | '{' binds '}' - { $$ = fixAttrs(false, $2); } - | '[' expr_list ']' { $$ = makeList(ATreverse($2)); } + { fixAttrs(*$2); $$ = $2; } + | '[' expr_list ']' { $$ = $2; } ; string_parts @@ -431,63 +460,56 @@ ind_string_parts | { $$ = ATempty; } ; -pattern - : ID { $$ = makeVarPat($1); } - | '{' formals '}' { $$ = makeAttrsPat($2.formals, $2.ellipsis ? eTrue : eFalse, sNoAlias); } - | '{' formals '}' '@' ID { $$ = makeAttrsPat($2.formals, $2.ellipsis ? eTrue : eFalse, $5); } - | ID '@' '{' formals '}' { $$ = makeAttrsPat($4.formals, $4.ellipsis ? eTrue : eFalse, $1); } - ; - binds - : binds bind { $$ = ATinsert($1, $2); } - | { $$ = ATempty; } + : binds ID '=' expr ';' { $$ = $1; $$->attrs[$2] = $4; } + | binds INHERIT ids ';' + { $$ = $1; + foreach (list::iterator, i, *$3) + $$->inherited.push_back(*i); + } + | binds INHERIT '(' expr ')' ids ';' + { $$ = $1; + /* !!! Should ensure sharing of the expression in $4. */ + foreach (list::iterator, i, *$6) + $$->attrs[*i] = new ExprSelect($4, *i); + } + | { $$ = new ExprAttrs; } ; -bind - : attrpath '=' expr ';' - { $$ = makeBindAttrPath(ATreverse($1), $3, CUR_POS); } - | INHERIT inheritsrc ids ';' - { $$ = makeInherit($2, $3, CUR_POS); } +ids + : ids ID { $$ = $1; $1->push_back($2); /* !!! dangerous */ } + | { $$ = new list; } ; -inheritsrc - : '(' expr ')' { $$ = $2; } - | { $$ = makeScope(); } - ; - -ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; }; - attrpath : attrpath '.' ID { $$ = ATinsert($1, $3); } | ID { $$ = ATmakeList1($1); } ; expr_list - : expr_list expr_select { $$ = ATinsert($1, $2); } - | { $$ = ATempty; } + : expr_list expr_select { $$ = $1; $1->elems.push_back($2); /* !!! dangerous */ } + | { $$ = new ExprList; } ; formals - : formal ',' formals /* !!! right recursive */ - { $$.formals = ATinsert($3.formals, $1); $$.ellipsis = $3.ellipsis; } + : formal ',' formals + { $$ = $3; $$->formals.push_front(*$1); /* !!! dangerous */ } | formal - { $$.formals = ATinsert(ATempty, $1); $$.ellipsis = false; } + { $$ = new Formals; $$->formals.push_back(*$1); $$->ellipsis = false; } | - { $$.formals = ATempty; $$.ellipsis = false; } + { $$ = new Formals; $$->ellipsis = false; } | ELLIPSIS - { $$.formals = ATempty; $$.ellipsis = true; } + { $$ = new Formals; $$->ellipsis = true; } ; formal - : ID { $$ = makeFormal($1, makeNoDefaultValue()); } - | ID '?' expr { $$ = makeFormal($1, makeDefaultValue($3)); } + : ID { $$ = new Formal($1, 0); } + | ID '?' expr { $$ = new Formal($1, $3); } ; %% -#include "eval.hh" - #include #include #include @@ -497,9 +519,7 @@ formal namespace nix { -static Expr parse(EvalState & state, - const char * text, const Path & path, - const Path & basePath) +static Expr * parse(const char * text, const Path & path, const Path & basePath) { yyscan_t scanner; ParseData data; @@ -523,7 +543,7 @@ static Expr parse(EvalState & state, } -Expr parseExprFromFile(EvalState & state, Path path) +Expr * parseExprFromFile(Path path) { assert(path[0] == '/'); @@ -544,14 +564,13 @@ Expr parseExprFromFile(EvalState & state, Path path) path = canonPath(path + "/default.nix"); /* Read and parse the input file. */ - return parse(state, readFile(path).c_str(), path, dirOf(path)); + return parse(readFile(path).c_str(), path, dirOf(path)); } -Expr parseExprFromString(EvalState & state, - const string & s, const Path & basePath) +Expr * parseExprFromString(const string & s, const Path & basePath) { - return parse(state, s.c_str(), "(string)", basePath); + return parse(s.c_str(), "(string)", basePath); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 74eb2b26d..cd40ade00 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -5,7 +5,6 @@ #include "util.hh" #include "archive.hh" #include "value-to-xml.hh" -#include "nixexpr-ast.hh" #include "parser.hh" #include "names.hh" @@ -281,7 +280,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) state.forceAttrs(*args[0]); /* Figure out the name first (for stack backtraces). */ - Bindings::iterator attr = args[0]->attrs->find(toATerm("name")); + Bindings::iterator attr = args[0]->attrs->find("name"); if (attr == args[0]->attrs->end()) throw EvalError("required attribute `name' missing"); string drvName; @@ -302,7 +301,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) bool outputHashRecursive = false; foreach (Bindings::iterator, i, *args[0]->attrs) { - string key = aterm2String(i->first); + string key = i->first; startNest(nest, lvlVomit, format("processing attribute `%1%'") % key); try { @@ -449,8 +448,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) /* !!! assumes a single output */ state.mkAttrs(v); - mkString((*v.attrs)[toATerm("outPath")], outPath, singleton(drvPath)); - mkString((*v.attrs)[toATerm("drvPath")], drvPath, singleton("=" + drvPath)); + mkString((*v.attrs)["outPath"], outPath, singleton(drvPath)); + mkString((*v.attrs)["drvPath"], drvPath, singleton("=" + drvPath)); } @@ -655,7 +654,7 @@ static void prim_attrNames(EvalState & state, Value * * args, Value & v) StringSet names; foreach (Bindings::iterator, i, *args[0]->attrs) - names.insert(aterm2String(i->first)); + names.insert(i->first); unsigned int n = 0; foreach (StringSet::iterator, i, names) @@ -668,7 +667,7 @@ static void prim_getAttr(EvalState & state, Value * * args, Value & v) { string attr = state.forceStringNoCtx(*args[0]); state.forceAttrs(*args[1]); - Bindings::iterator i = args[1]->attrs->find(toATerm(attr)); + Bindings::iterator i = args[1]->attrs->find(attr); if (i == args[1]->attrs->end()) throw EvalError(format("attribute `%1%' missing") % attr); state.forceValue(i->second); @@ -681,7 +680,7 @@ static void prim_hasAttr(EvalState & state, Value * * args, Value & v) { string attr = state.forceStringNoCtx(*args[0]); state.forceAttrs(*args[1]); - mkBool(v, args[1]->attrs->find(toATerm(attr)) != args[1]->attrs->end()); + mkBool(v, args[1]->attrs->find(attr) != args[1]->attrs->end()); } @@ -702,7 +701,7 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v) for (unsigned int i = 0; i < args[1]->list.length; ++i) { state.forceStringNoCtx(args[1]->list.elems[i]); - v.attrs->erase(toATerm(args[1]->list.elems[i].string.s)); + v.attrs->erase(args[1]->list.elems[i].string.s); } } @@ -721,16 +720,16 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v) Value & v2(args[0]->list.elems[i]); state.forceAttrs(v2); - Bindings::iterator j = v2.attrs->find(toATerm("name")); + Bindings::iterator j = v2.attrs->find("name"); if (j == v2.attrs->end()) throw TypeError("`name' attribute missing in a call to `listToAttrs'"); string name = state.forceStringNoCtx(j->second); - j = v2.attrs->find(toATerm("value")); + j = v2.attrs->find("value"); if (j == v2.attrs->end()) throw TypeError("`value' attribute missing in a call to `listToAttrs'"); - (*v.attrs)[toATerm(name)] = j->second; // !!! sharing? + (*v.attrs)[name] = j->second; // !!! sharing? } } @@ -977,8 +976,8 @@ static void prim_parseDrvName(EvalState & state, Value * * args, Value & v) string name = state.forceStringNoCtx(*args[0]); DrvName parsed(name); state.mkAttrs(v); - mkString((*v.attrs)[toATerm("name")], parsed.name); - mkString((*v.attrs)[toATerm("version")], parsed.version); + mkString((*v.attrs)["name"], parsed.name); + mkString((*v.attrs)["version"], parsed.version); } @@ -999,10 +998,9 @@ void EvalState::createBaseEnv() { baseEnv.up = 0; - { Value & v = baseEnv.bindings[toATerm("builtins")]; - v.type = tAttrs; - v.attrs = new Bindings; - } + Value & builtins = baseEnv.bindings["builtins"]; + builtins.type = tAttrs; + builtins.attrs = new Bindings; /* Add global constants such as `true' to the base environment. */ Value v; @@ -1025,8 +1023,8 @@ void EvalState::createBaseEnv() /* Add a wrapper around the derivation primop that computes the `drvPath' and `outPath' attributes lazily. */ string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }"; - mkThunk(v, baseEnv, parseExprFromString(*this, s, "/")); - addConstant("derivation", v); + //mkThunk(v, baseEnv, parseExprFromString(s, "/")); + //addConstant("derivation", v); // Miscellaneous addPrimOp("import", 1, prim_import); diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index eff414aba..c8a067aac 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -1,7 +1,5 @@ #include "value-to-xml.hh" #include "xml-writer.hh" -#include "nixexpr-ast.hh" -#include "aterm.hh" #include "util.hh" #include @@ -27,31 +25,10 @@ static void showAttrs(EvalState & state, bool strict, Bindings & attrs, { StringSet names; foreach (Bindings::iterator, i, attrs) - names.insert(aterm2String(i->first)); + names.insert(i->first); foreach (StringSet::iterator, i, names) { XMLOpenElement _(doc, "attr", singletonAttrs("name", *i)); - printValueAsXML(state, strict, attrs[toATerm(*i)], doc, context, drvsSeen); - } -} - - -static void printPatternAsXML(Pattern pat, XMLWriter & doc) -{ - ATerm name; - ATermList formals; - ATermBool ellipsis; - if (matchVarPat(pat, name)) - doc.writeEmptyElement("varpat", singletonAttrs("name", aterm2String(name))); - else if (matchAttrsPat(pat, formals, ellipsis, name)) { - XMLAttrs attrs; - if (name != sNoAlias) attrs["name"] = aterm2String(name); - if (ellipsis == eTrue) attrs["ellipsis"] = "1"; - XMLOpenElement _(doc, "attrspat", attrs); - for (ATermIterator i(formals); i; ++i) { - Expr name; ATerm dummy; - if (!matchFormal(*i, name, dummy)) abort(); - doc.writeEmptyElement("attr", singletonAttrs("name", aterm2String(name))); - } + printValueAsXML(state, strict, attrs[*i], doc, context, drvsSeen); } } @@ -90,14 +67,14 @@ static void printValueAsXML(EvalState & state, bool strict, Value & v, if (state.isDerivation(v)) { XMLAttrs xmlAttrs; - Bindings::iterator a = v.attrs->find(toATerm("derivation")); + Bindings::iterator a = v.attrs->find("derivation"); Path drvPath; - a = v.attrs->find(toATerm("drvPath")); + a = v.attrs->find("drvPath"); if (a != v.attrs->end() && a->second.type == tString) xmlAttrs["drvPath"] = drvPath = a->second.string.s; - a = v.attrs->find(toATerm("outPath")); + a = v.attrs->find("outPath"); if (a != v.attrs->end() && a->second.type == tString) xmlAttrs["outPath"] = a->second.string.s; @@ -126,7 +103,15 @@ static void printValueAsXML(EvalState & state, bool strict, Value & v, case tLambda: { XMLOpenElement _(doc, "function"); - printPatternAsXML(v.lambda.pat, doc); + if (v.lambda.fun->matchAttrs) { + XMLAttrs attrs; + if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg; + if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; + XMLOpenElement _(doc, "attrspat", attrs); + foreach (Formals::Formals_::iterator, i, v.lambda.fun->formals->formals) + doc.writeEmptyElement("attr", singletonAttrs("name", i->name)); + } else + doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg)); break; } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index d9cf9a862..c53c558c1 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -177,7 +177,7 @@ static void initAndRun(int argc, char * * argv) if (lt != "") setLogType(lt); /* ATerm stuff. !!! find a better place to put this */ - initDerivationsHelpers(); + //initDerivationsHelpers(); /* Put the arguments in a vector. */ Strings args, remaining; @@ -335,7 +335,7 @@ int main(int argc, char * * argv) /* ATerm setup. */ ATerm bottomOfStack; - ATinit(argc, argv, &bottomOfStack); + //ATinit(argc, argv, &bottomOfStack); /* Turn on buffering for cerr. */ #if HAVE_PUBSETBUF diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 20affa83d..6286648d1 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -6,7 +6,6 @@ #include "parser.hh" #include "eval.hh" #include "help.txt.hh" -#include "nixexpr-ast.hh" #include "get-drvs.hh" #include "attr-path.hh" #include "pathlocks.hh" @@ -143,9 +142,9 @@ static void getAllExprs(EvalState & state, } -static Expr loadSourceExpr(EvalState & state, const Path & path) +static Expr * loadSourceExpr(EvalState & state, const Path & path) { - if (isNixExpr(path)) return parseExprFromFile(state, absPath(path)); + if (isNixExpr(path)) return parseExprFromFile(absPath(path)); /* The path is a directory. Put the Nix expressions in the directory in an attribute set, with the file name of each diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 7931af6ab..466c7b499 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -23,12 +23,12 @@ void printHelp() } -static Expr parseStdin(EvalState & state) +static Expr * parseStdin(EvalState & state) { startNest(nest, lvlTalkative, format("parsing standard input")); string s, s2; while (getline(std::cin, s2)) s += s2 + "\n"; - return parseExprFromString(state, s, absPath(".")); + return parseExprFromString(s, absPath(".")); } @@ -39,7 +39,7 @@ static bool indirectRoot = false; void processExpr(EvalState & state, const Strings & attrPaths, bool parseOnly, bool strict, const Bindings & autoArgs, - bool evalOnly, bool xmlOutput, Expr e) + bool evalOnly, bool xmlOutput, Expr * e) { if (parseOnly) std::cout << format("%1%\n"); @@ -129,14 +129,14 @@ void run(Strings args) store = openStore(); if (readStdin) { - Expr e = parseStdin(state); + Expr * e = parseStdin(state); processExpr(state, attrPaths, parseOnly, strict, autoArgs, evalOnly, xmlOutput, e); } foreach (Strings::iterator, i, files) { Path path = absPath(*i); - Expr e = parseExprFromFile(state, path); + Expr * e = parseExprFromFile(path); processExpr(state, attrPaths, parseOnly, strict, autoArgs, evalOnly, xmlOutput, e); } From a60317f20fbc8be8e339060d932946f6d99ece6a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Apr 2010 21:21:24 +0000 Subject: [PATCH 056/103] * More missing constructs. --- src/libexpr/eval-test.cc | 8 ++- src/libexpr/eval.cc | 128 ++++++++++++++++++--------------------- src/libexpr/lexer.l | 4 +- src/libexpr/nixexpr.cc | 41 ++++++++++--- src/libexpr/nixexpr.hh | 38 ++++++++++-- src/libexpr/parser.y | 80 ++++++++++-------------- 6 files changed, 161 insertions(+), 138 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 32f4940df..dd8ead04a 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -45,6 +45,7 @@ void run(Strings args) doTest(state, "({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); //doTest(state, "({x ? y, y ? x}: y) { }"); doTest(state, "let x = 1; in x"); + 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 @@ -65,18 +66,19 @@ void run(Strings args) doTest(state, "__head [ 1 2 3 ]"); doTest(state, "__add 1 2"); doTest(state, "null"); - //doTest(state, "\"foo\""); - //doTest(state, "let s = \"bar\"; in \"foo${s}\""); + doTest(state, "\"foo\""); + doTest(state, "let s = \"bar\"; in \"foo${s}\""); doTest(state, "if true then 1 else 2"); doTest(state, "if false then 1 else 2"); doTest(state, "if false || true then 1 else 2"); + doTest(state, "!(true || false)"); doTest(state, "let x = x; in if true || x then 1 else 2"); doTest(state, "http://nixos.org/"); doTest(state, "/etc/passwd"); //doTest(state, "import ./foo.nix"); doTest(state, "map (x: __add 1 x) [ 1 2 3 ]"); doTest(state, "map (builtins.add 1) [ 1 2 3 ]"); - //doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); + doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); doTest(state, "let x = 1; as = rec { inherit x; y = as.x; }; in as.y"); doTest(state, "let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); doTest(state, "let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 8ead986b8..21c22333b 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -160,9 +160,9 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2)) throw TypeError(format(s) % s2); } -LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s2)) +LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos)) { - throw AssertionError(format(s) % s2); + throw AssertionError(format(s) % pos); } LocalNoInline(void addErrorPrefix(Error & e, const char * s)) @@ -341,73 +341,13 @@ void EvalState::eval(Env & env, Expr * e, Value & v) char x; if (&x < deepestStack) deepestStack = &x; - //debug(format("eval: %1%") % e); + //debug(format("eval: %1%") % *e); checkInterrupt(); nrEvaluated++; e->eval(*this, env, v); - -#if 0 - 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; - - else if (matchConcatStrings(e, es)) { - PathSet context; - std::ostringstream s; - - bool first = true, isPath = false; - Value vStr; - - for (ATermIterator i(es); i; ++i) { - eval(env, *i, vStr); - - /* If the first element is a path, then the result will - also be a path, we don't copy anything (yet - that's - done later, since paths are copied when they are used - in a derivation), and none of the strings are allowed - to have contexts. */ - if (first) { - isPath = vStr.type == tPath; - first = false; - } - - s << coerceToString(vStr, context, false, !isPath); - } - - if (isPath && !context.empty()) - throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str()); - - if (isPath) - mkPath(v, s.str().c_str()); - else - mkString(v, s.str(), context); - } - - /* Assertions. */ - else if (matchAssert(e, e1, e2, pos)) { - if (!evalBool(env, e1)) - throwAssertionError("assertion failed at %1%", showPos(pos)); - eval(env, e2, v); - } - - /* Negation. */ - else if (matchOpNot(e, e1)) - mkBool(v, !evalBool(env, e1)); - - /* Attribute existence test (?). */ - else if (matchOpHasAttr(e, e1, name)) { - Value vAttrs; - eval(env, e1, vAttrs); - forceAttrs(vAttrs); - mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end()); - } -#endif } @@ -516,6 +456,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) } +void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) +{ + Value vAttrs; + state.eval(env, e, vAttrs); + state.forceAttrs(vAttrs); + mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end()); +} + + void ExprLambda::eval(EvalState & state, Env & env, Value & v) { v.type = tLambda; @@ -663,6 +612,20 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v) } +void ExprAssert::eval(EvalState & state, Env & env, Value & v) +{ + if (!state.evalBool(env, cond)) + throwAssertionError("assertion failed at %1%", pos); + state.eval(env, body, v); +} + + +void ExprOpNot::eval(EvalState & state, Env & env, Value & v) +{ + mkBool(v, !state.evalBool(env, e)); +} + + void ExprOpEq::eval(EvalState & state, Env & env, Value & v) { Value v1; state.eval(env, e1, v1); @@ -713,12 +676,6 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) } -void ExprOpConcatStrings::eval(EvalState & state, Env & env, Value & v) -{ - abort(); -} - - void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) { Value v1; state.eval(env, e1, v1); @@ -735,6 +692,39 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) } +void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) +{ + PathSet context; + std::ostringstream s; + + bool first = true, isPath = false; + Value vStr; + + foreach (vector::iterator, i, *es) { + state.eval(env, *i, vStr); + + /* If the first element is a path, then the result will also + be a path, we don't copy anything (yet - that's done later, + since paths are copied when they are used in a derivation), + and none of the strings are allowed to have contexts. */ + if (first) { + isPath = vStr.type == tPath; + first = false; + } + + s << state.coerceToString(vStr, context, false, !isPath); + } + + if (isPath && !context.empty()) + throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str()); + + if (isPath) + mkPath(v, s.str().c_str()); + else + mkString(v, s.str(), context); +} + + void EvalState::forceValue(Value & v) { if (v.type == tThunk) { diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index f750cfd02..e905700fd 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -45,7 +45,6 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) static Expr * unescapeStr(const char * s) { -#if 0 string t; char c; while ((c = *s++)) { @@ -64,8 +63,7 @@ static Expr * unescapeStr(const char * s) } else t += c; } - return makeStr(toATerm(t), ATempty); -#endif + return new ExprString(t); } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 05dfbd322..b044aaa94 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -40,6 +40,11 @@ void ExprSelect::show(std::ostream & str) str << "(" << *e << ")." << name; } +void ExprOpHasAttr::show(std::ostream & str) +{ + str << "(" << *e << ") ? " << name; +} + void ExprAttrs::show(std::ostream & str) { if (recursive) str << "rec "; @@ -87,19 +92,37 @@ void ExprIf::show(std::ostream & str) str << "if " << *cond << " then " << *then << " else " << *else_; } - -#if 0 -string showPos(ATerm pos) +void ExprAssert::show(std::ostream & str) { - ATerm path; - int line, column; - if (matchNoPos(pos)) return "undefined position"; - if (!matchPos(pos, path, line, column)) - throw badTerm("position expected", pos); - return (format("`%1%:%2%:%3%'") % aterm2String(path) % line % column).str(); + str << "assert " << *cond << "; " << *body; +} + +void ExprOpNot::show(std::ostream & str) +{ + str << "! " << *e; +} + +void ExprConcatStrings::show(std::ostream & str) +{ + bool first = true; + foreach (vector::iterator, i, *es) { + if (first) first = false; else str << " + "; + str << **i; + } +} + + +std::ostream & operator << (std::ostream & str, const Pos & pos) +{ + if (!pos.line) + str << "undefined position"; + else + str << (format("`%1%:%2%:%3%'") % pos.file % pos.line % pos.column).str(); + return str; } +#if 0 ATerm bottomupRewrite(TermFun & f, ATerm e) { checkInterrupt(); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index ebdfd0a15..d6e088c41 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -24,6 +24,9 @@ struct Pos }; +std::ostream & operator << (std::ostream & str, const Pos & pos); + + /* Abstract syntax of Nix expressions. */ struct Env; @@ -81,6 +84,14 @@ struct ExprSelect : Expr COMMON_METHODS }; +struct ExprOpHasAttr : Expr +{ + Expr * e; + string name; + ExprOpHasAttr(Expr * e, const string & name) : e(e), name(name) { }; + COMMON_METHODS +}; + struct ExprAttrs : Expr { bool recursive; @@ -139,6 +150,21 @@ struct ExprIf : Expr COMMON_METHODS }; +struct ExprAssert : Expr +{ + Pos pos; + Expr * cond, * body; + ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { }; + COMMON_METHODS +}; + +struct ExprOpNot : Expr +{ + Expr * e; + ExprOpNot(Expr * e) : e(e) { }; + COMMON_METHODS +}; + #define MakeBinOp(name, s) \ struct Expr##name : Expr \ { \ @@ -158,15 +184,17 @@ MakeBinOp(OpAnd, "&&") MakeBinOp(OpOr, "||") MakeBinOp(OpImpl, "->") MakeBinOp(OpUpdate, "//") -MakeBinOp(OpConcatStrings, "+") MakeBinOp(OpConcatLists, "++") +struct ExprConcatStrings : Expr +{ + vector * es; + ExprConcatStrings(vector * es) : es(es) { }; + COMMON_METHODS +}; + #if 0 -/* Show a position. */ -string showPos(ATerm pos); - - /* Generic bottomup traversal over ATerms. The traversal first recursively descends into subterms, and then applies the given term function to the resulting term. */ diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 96fbe2cb4..83f454845 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -20,7 +20,6 @@ #include #include -#include "aterm.hh" #include "util.hh" #include "nixexpr.hh" @@ -116,13 +115,13 @@ static void fixAttrs(ExprAttrs & attrs) for (ATermIterator j(attrPath); j; ) { name = *j; ++j; if (t->leaf) throw ParseError(format("attribute set containing `%1%' at %2% already defined at %3%") - % showAttrPath(attrPath) % showPos(pos) % showPos (t->pos)); + % showAttrPath(attrPath) % showPos(pos) % showPos(t->pos)); t = &(t->children[name]); } if (t->leaf) throw ParseError(format("duplicate definition of attribute `%1%' at %2% and %3%") - % showAttrPath(attrPath) % showPos(pos) % showPos (t->pos)); + % showAttrPath(attrPath) % showPos(pos) % showPos(t->pos)); if (!t->children.empty()) throw ParseError(format("duplicate definition of attribute `%1%' at %2%") % showAttrPath(attrPath) % showPos(pos)); @@ -289,30 +288,11 @@ static Pos makeCurPos(YYLTYPE * loc, ParseData * data) void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error) { - data->error = (format("%1%, at `%2%':%3%:%4%") - % error % data->path % loc->first_line % loc->first_column).str(); + data->error = (format("%1%, at %2%") + % error % makeCurPos(loc, data)).str(); } -/* Make sure that the parse stack is scanned by the ATerm garbage - collector. */ -static void * mallocAndProtect(size_t size) -{ - void * p = malloc(size); - if (p) ATprotectMemory(p, size); - return p; -} - -static void freeAndUnprotect(void * p) -{ - ATunprotectMemory(p); - free(p); -} - -#define YYMALLOC mallocAndProtect -#define YYFREE freeAndUnprotect - - #endif @@ -329,18 +309,20 @@ static void freeAndUnprotect(void * p) char * path; char * uri; std::list * ids; + std::vector * string_parts; } %type start expr expr_function expr_if expr_op %type expr_app expr_select expr_simple %type expr_list %type binds -%type attrpath string_parts ind_string_parts +%type attrpath ind_string_parts %type formals %type formal %type ids +%type string_parts %token ID ATTRPATH -%token STR IND_STR +%token STR IND_STR %token INT %token PATH %token URI @@ -375,9 +357,8 @@ expr_function { $$ = new ExprLambda(CUR_POS, $5, true, $2, $7); } | ID '@' '{' formals '}' ':' expr_function { $$ = new ExprLambda(CUR_POS, $1, true, $4, $7); } - /* | ASSERT expr ';' expr_function - { $$ = makeAssert($2, $4, CUR_POS); } - */ + | ASSERT expr ';' expr_function + { $$ = new ExprAssert(CUR_POS, $2, $4); } | WITH expr ';' expr_function { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN expr_function @@ -391,18 +372,20 @@ expr_if ; expr_op - : /* '!' expr_op %prec NEG { $$ = makeOpNot($2); } - | */ - expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); } + : '!' expr_op %prec NEG { $$ = new ExprOpNot($2); } + | expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); } | expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); } | expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); } | expr_op OR expr_op { $$ = new ExprOpOr($1, $3); } | expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); } | expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); } - /* - | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); } - */ - | expr_op '+' expr_op { $$ = new ExprOpConcatStrings($1, $3); } + | expr_op '?' ID { $$ = new ExprOpHasAttr($1, $3); } + | expr_op '+' expr_op + { vector * l = new vector; + l->push_back($1); + l->push_back($3); + $$ = new ExprConcatStrings(l); + } | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); } | expr_app ; @@ -421,26 +404,25 @@ expr_select expr_simple : ID { $$ = new ExprVar($1); } - | INT { $$ = new ExprInt($1); } /* + | INT { $$ = new ExprInt($1); } | '"' string_parts '"' { - /* For efficiency, and to simplify parse trees a bit. * / - if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty); - else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2); - else $$ = makeConcatStrings(ATreverse($2)); + /* For efficiency, and to simplify parse trees a bit. */ + if ($2->empty()) $$ = new ExprString(""); + else if ($2->size() == 1) $$ = $2->front(); + else $$ = new ExprConcatStrings($2); } + /* | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { $$ = stripIndentation(ATreverse($2)); } - */ + */ | PATH { $$ = new ExprPath(absPath($1, data->basePath)); } | URI { $$ = new ExprString($1); } | '(' expr ')' { $$ = $2; } -/* /* Let expressions `let {..., body = ...}' are just desugared - into `(rec {..., body = ...}).body'. * / + into `(rec {..., body = ...}).body'. */ | LET '{' binds '}' - { $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); } - */ + { fixAttrs(*$3); $3->recursive = true; $$ = new ExprSelect($3, "body"); } | REC '{' binds '}' { fixAttrs(*$3); $3->recursive = true; $$ = $3; } | '{' binds '}' @@ -449,9 +431,9 @@ expr_simple ; string_parts - : string_parts STR { $$ = ATinsert($1, $2); } - | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); } - | { $$ = ATempty; } + : string_parts STR { $$ = $1; $1->push_back($2); } + | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); } + | { $$ = new vector; } ; ind_string_parts From d4f0b0fc6cdb9ae2fd5fc057d621bc80b36a7b18 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Apr 2010 22:03:27 +0000 Subject: [PATCH 057/103] * Indented strings. --- src/libexpr/eval-test.cc | 1 + src/libexpr/eval.cc | 6 +++ src/libexpr/lexer.l | 19 +++---- src/libexpr/nixexpr.cc | 19 ++----- src/libexpr/nixexpr.hh | 18 +++---- src/libexpr/parser.y | 70 ++++++++++++-------------- src/nix-instantiate/nix-instantiate.cc | 2 +- 7 files changed, 65 insertions(+), 70 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index dd8ead04a..782e36d38 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -67,6 +67,7 @@ void run(Strings args) doTest(state, "__add 1 2"); doTest(state, "null"); doTest(state, "\"foo\""); + doTest(state, "''\n foo\n bar\n ''"); doTest(state, "let s = \"bar\"; in \"foo${s}\""); doTest(state, "if true then 1 else 2"); doTest(state, "if false then 1 else 2"); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 21c22333b..62b493bbf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -367,6 +367,12 @@ bool EvalState::evalBool(Env & env, Expr * e) } +void Expr::eval(EvalState & state, Env & env, Value & v) +{ + abort(); +} + + void ExprInt::eval(EvalState & state, Env & env, Value & v) { mkInt(v, n); diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index e905700fd..7aa05db8c 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -111,10 +111,11 @@ inherit { return INHERIT; } \" { BEGIN(STRING); return '"'; } ([^\$\"\\]|\$[^\{\"]|\\.)+ { -/* !!! Not quite right: we want a follow restriction on "$", it - shouldn't be followed by a "{". Right now "$\"" will be consumed - as part of a string, rather than a "$" followed by the string - terminator. Disallow "$\"" for now. */ + /* !!! Not quite right: we want a follow restriction on + "$", it shouldn't be followed by a "{". Right now + "$\"" will be consumed as part of a string, rather + than a "$" followed by the string terminator. + Disallow "$\"" for now. */ yylval->e = unescapeStr(yytext); return STR; } @@ -124,25 +125,25 @@ inherit { return INHERIT; } \'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; } ([^\$\']|\$[^\{\']|\'[^\'\$])+ { - //yylval->t = makeIndStr(toATerm(yytext)); + yylval->e = new ExprIndStr(yytext); return IND_STR; } \'\'\$ { - //yylval->t = makeIndStr(toATerm("$")); + yylval->e = new ExprIndStr("$"); return IND_STR; } \'\'\' { - //yylval->t = makeIndStr(toATerm("''")); + yylval->e = new ExprIndStr("''"); return IND_STR; } \'\'\\. { - //yylval->t = unescapeStr(yytext + 2); + yylval->e = new ExprIndStr(yytext + 2); return IND_STR; } \$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; } \'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; } \' { - //yylval->t = makeIndStr(toATerm("'")); + yylval->e = new ExprIndStr("'"); return IND_STR; } . return yytext[0]; /* just in case: shouldn't be reached */ diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index b044aaa94..6ef996bdc 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -15,6 +15,11 @@ std::ostream & operator << (std::ostream & str, Expr & e) } +void Expr::show(std::ostream & str) +{ + abort(); +} + void ExprInt::show(std::ostream & str) { str << n; @@ -152,20 +157,6 @@ ATerm bottomupRewrite(TermFun & f, ATerm e) } -Expr makeAttrs(const ATermMap & attrs) -{ - ATermList bnds = ATempty; - for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) { - Expr e; - ATerm pos; - if (!matchAttrRHS(i->value, e, pos)) - abort(); /* can't happen */ - bnds = ATinsert(bnds, makeBind(i->key, e, pos)); - } - return makeAttrs(bnds); -} - - static void varsBoundByPattern(ATermMap & map, Pattern pat) { ATerm name; diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index d6e088c41..725f9fe88 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -35,11 +35,8 @@ struct EvalState; struct Expr { - virtual void show(std::ostream & str) = 0; - virtual void eval(EvalState & state, Env & env, Value & v) - { - throw Error("not implemented"); - } + virtual void show(std::ostream & str); + virtual void eval(EvalState & state, Env & env, Value & v); }; std::ostream & operator << (std::ostream & str, Expr & e); @@ -62,6 +59,13 @@ struct ExprString : Expr COMMON_METHODS }; +/* Temporary class used during parsing of indented strings. */ +struct ExprIndStr : Expr +{ + string s; + ExprIndStr(const string & s) : s(s) { }; +}; + struct ExprPath : Expr { string s; @@ -206,10 +210,6 @@ struct TermFun ATerm bottomupRewrite(TermFun & f, ATerm e); -/* Create an attribute set expression from an Attrs value. */ -Expr makeAttrs(const ATermMap & attrs); - - /* Check whether all variables are defined in the given expression. Throw an exception if this isn't the case. */ void checkVarDefs(const ATermMap & def, Expr e); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 83f454845..22ce7e3f3 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -174,11 +174,12 @@ static void checkPatternVars(ATerm pos, Pattern pat) ATermMap map; checkPatternVars(pos, map, pat); } +#endif -static Expr stripIndentation(ATermList es) +static Expr * stripIndentation(vector & es) { - if (es == ATempty) return makeStr(""); + if (es.empty()) return new ExprString(""); /* Figure out the minimum indentation. Note that by design whitespace-only final lines are not taken into account. (So @@ -186,9 +187,9 @@ static Expr stripIndentation(ATermList es) bool atStartOfLine = true; /* = seen only whitespace in the current line */ unsigned int minIndent = 1000000; unsigned int curIndent = 0; - ATerm e; - for (ATermIterator i(es); i; ++i) { - if (!matchIndStr(*i, e)) { + foreach (vector::iterator, i, es) { + ExprIndStr * e = dynamic_cast(*i); + if (!e) { /* Anti-quotations end the current start-of-line whitespace. */ if (atStartOfLine) { atStartOfLine = false; @@ -196,12 +197,11 @@ static Expr stripIndentation(ATermList es) } continue; } - string s = aterm2String(e); - for (unsigned int j = 0; j < s.size(); ++j) { + for (unsigned int j = 0; j < e->s.size(); ++j) { if (atStartOfLine) { - if (s[j] == ' ') + if (e->s[j] == ' ') curIndent++; - else if (s[j] == '\n') { + else if (e->s[j] == '\n') { /* Empty line, doesn't influence minimum indentation. */ curIndent = 0; @@ -209,7 +209,7 @@ static Expr stripIndentation(ATermList es) atStartOfLine = false; if (curIndent < minIndent) minIndent = curIndent; } - } else if (s[j] == '\n') { + } else if (e->s[j] == '\n') { atStartOfLine = true; curIndent = 0; } @@ -217,37 +217,37 @@ static Expr stripIndentation(ATermList es) } /* Strip spaces from each line. */ - ATermList es2 = ATempty; + vector * es2 = new vector; atStartOfLine = true; unsigned int curDropped = 0; - unsigned int n = ATgetLength(es); - for (ATermIterator i(es); i; ++i, --n) { - if (!matchIndStr(*i, e)) { + unsigned int n = es.size(); + for (vector::iterator i = es.begin(); i != es.end(); ++i, --n) { + ExprIndStr * e = dynamic_cast(*i); + if (!e) { atStartOfLine = false; curDropped = 0; - es2 = ATinsert(es2, *i); + es2->push_back(*i); continue; } - string s = aterm2String(e); string s2; - for (unsigned int j = 0; j < s.size(); ++j) { + for (unsigned int j = 0; j < e->s.size(); ++j) { if (atStartOfLine) { - if (s[j] == ' ') { + if (e->s[j] == ' ') { if (curDropped++ >= minIndent) - s2 += s[j]; + s2 += e->s[j]; } - else if (s[j] == '\n') { + else if (e->s[j] == '\n') { curDropped = 0; - s2 += s[j]; + s2 += e->s[j]; } else { atStartOfLine = false; curDropped = 0; - s2 += s[j]; + s2 += e->s[j]; } } else { - s2 += s[j]; - if (s[j] == '\n') atStartOfLine = true; + s2 += e->s[j]; + if (e->s[j] == '\n') atStartOfLine = true; } } @@ -258,13 +258,12 @@ static Expr stripIndentation(ATermList es) if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos) s2 = string(s2, 0, p + 1); } - - es2 = ATinsert(es2, makeStr(s2)); + + es2->push_back(new ExprString(s2)); } - return makeConcatStrings(ATreverse(es2)); + return new ExprConcatStrings(es2); } -#endif void backToString(yyscan_t scanner); @@ -316,11 +315,10 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err %type expr_app expr_select expr_simple %type expr_list %type binds -%type attrpath ind_string_parts %type formals %type formal -%type ids -%type string_parts +%type ids attrpath +%type string_parts ind_string_parts %token ID ATTRPATH %token STR IND_STR %token INT @@ -411,11 +409,9 @@ expr_simple else if ($2->size() == 1) $$ = $2->front(); else $$ = new ExprConcatStrings($2); } - /* | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { - $$ = stripIndentation(ATreverse($2)); + $$ = stripIndentation(*$2); } - */ | PATH { $$ = new ExprPath(absPath($1, data->basePath)); } | URI { $$ = new ExprString($1); } | '(' expr ')' { $$ = $2; } @@ -437,9 +433,9 @@ string_parts ; ind_string_parts - : ind_string_parts IND_STR { $$ = ATinsert($1, $2); } - | ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = ATinsert($1, $3); } - | { $$ = ATempty; } + : ind_string_parts IND_STR { $$ = $1; $1->push_back($2); } + | ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = $1; $1->push_back($3); } + | { $$ = new vector; } ; binds diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 466c7b499..ba34fc63b 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -42,7 +42,7 @@ void processExpr(EvalState & state, const Strings & attrPaths, bool evalOnly, bool xmlOutput, Expr * e) { if (parseOnly) - std::cout << format("%1%\n"); + std::cout << format("%1%\n") % *e; else foreach (Strings::const_iterator, i, attrPaths) { Value v; From 0d272fca799f7e6da955875b2935c19542cd6b4d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Apr 2010 23:31:47 +0000 Subject: [PATCH 058/103] * Remove some obsolete functions. --- src/libutil/util.cc | 47 --------------------------------------------- src/libutil/util.hh | 5 ----- 2 files changed, 52 deletions(-) diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 790c25768..d28d0e823 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -951,53 +951,6 @@ void _interrupted() ////////////////////////////////////////////////////////////////////// -string packStrings(const Strings & strings) -{ - string d; - for (Strings::const_iterator i = strings.begin(); - i != strings.end(); ++i) - { - unsigned int len = i->size(); - d += len & 0xff; - d += (len >> 8) & 0xff; - d += (len >> 16) & 0xff; - d += (len >> 24) & 0xff; - d += *i; - } - return d; -} - - -Strings unpackStrings(const string & s) -{ - Strings strings; - - string::const_iterator i = s.begin(); - - while (i != s.end()) { - - if (i + 4 > s.end()) - throw Error(format("short db entry: `%1%'") % s); - - unsigned int len; - len = (unsigned char) *i++; - len |= ((unsigned char) *i++) << 8; - len |= ((unsigned char) *i++) << 16; - len |= ((unsigned char) *i++) << 24; - - if (len == 0xffffffff) return strings; /* explicit end-of-list */ - - if (i + len > s.end()) - throw Error(format("short db entry: `%1%'") % s); - - strings.push_back(string(i, i + len)); - i += len; - } - - return strings; -} - - Strings tokenizeString(const string & s, const string & separators) { Strings result; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index f4478b449..4de33d3ef 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -276,11 +276,6 @@ void inline checkInterrupt() MakeError(Interrupted, BaseError) -/* String packing / unpacking. */ -string packStrings(const Strings & strings); -Strings unpackStrings(const string & s); - - /* String tokenizer. */ Strings tokenizeString(const string & s, const string & separators = " \t\n\r"); From 10e8b1fd15d59dc541c15f6da56f8baf58eb3aa3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Apr 2010 23:33:23 +0000 Subject: [PATCH 059/103] * Finished the ATerm-less parser. --- src/libexpr/eval-test.cc | 4 +- src/libexpr/eval.cc | 33 ++++++++++- src/libexpr/nixexpr.cc | 2 +- src/libexpr/parser.y | 123 ++++++++++----------------------------- src/libexpr/primops.cc | 4 +- src/libmain/shared.cc | 4 +- 6 files changed, 68 insertions(+), 102 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 782e36d38..ffadd41a7 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -56,7 +56,7 @@ void run(Strings args) doTest(state, "let id = x: x; in [1 2] == [(id 1) (id 2)]"); doTest(state, "let id = x: x; in [1 2] == [(id 1) (id 3)]"); doTest(state, "[1 2] == [3 (let x = x; in x)]"); - //doTest(state, "{ x = 1; y.z = 2; } == { y = { z = 2; }; x = 1; }"); + doTest(state, "{ x = 1; y.z = 2; } == { y = { z = 2; }; x = 1; }"); doTest(state, "{ x = 1; y = 2; } == { x = 2; }"); doTest(state, "{ x = [ 1 2 ]; } == { x = [ 1 ] ++ [ 2 ]; }"); doTest(state, "1 != 1"); @@ -84,7 +84,7 @@ void run(Strings args) doTest(state, "let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); doTest(state, "let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); doTest(state, "builtins.toXML 123"); - //doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); + doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); state.printStats(); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 62b493bbf..99149fd7f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -54,6 +54,7 @@ std::ostream & operator << (std::ostream & str, Value & v) str << "]"; break; case tThunk: + case tCopy: str << ""; break; case tLambda: @@ -160,6 +161,16 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2)) throw TypeError(format(s) % s2); } +LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const string & s2)) +{ + throw TypeError(format(s) % pos % s2); +} + +LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos)) +{ + throw TypeError(format(s) % pos); +} + LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos)) { throw AssertionError(format(s) % pos); @@ -175,6 +186,11 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2)) e.addPrefix(format(s) % s2); } +LocalNoInline(void addErrorPrefix(Error & e, const char * s, const Pos & pos)) +{ + e.addPrefix(format(s) % pos); +} + LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const string & s3)) { e.addPrefix(format(s) % s2 % s3); @@ -424,6 +440,11 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) Value & v2 = (*v.attrs)[i->first]; mkThunk(v2, env, i->second); } + + foreach (list::iterator, i, inherited) { + Value & v2 = (*v.attrs)[*i]; + mkCopy(v2, *lookupVar(&env, *i)); + } } } @@ -555,7 +576,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) nrValues++; if (j == arg.attrs->end()) { - if (!i->def) throwTypeError("the argument named `%1%' required by the function is missing", i->name); + if (!i->def) throwTypeError("function at %1% called without required argument `%2%'", + fun.lambda.fun->pos, i->name); mkThunk(v, env2, i->def); } else { attrsUsed++; @@ -568,10 +590,15 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) TODO: show the names of the expected/unexpected arguments. */ if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size()) - throwTypeError("function called with unexpected argument"); + throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos); } - eval(env2, fun.lambda.fun->body, v); + try { + eval(env2, fun.lambda.fun->body, v); + } catch (Error & e) { + addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos); + throw; + } } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 6ef996bdc..1902e23b0 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -55,7 +55,7 @@ void ExprAttrs::show(std::ostream & str) if (recursive) str << "rec "; str << "{ "; foreach (list::iterator, i, inherited) - str << "inherited " << *i << "; "; + str << "inherit " << *i << "; "; foreach (Attrs::iterator, i, attrs) str << i->first << " = " << *i->second << "; "; str << "}"; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 22ce7e3f3..07bf56a1c 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -44,99 +44,38 @@ struct ParseData }; -#if 0 -static string showAttrPath(ATermList attrPath) +static string showAttrPath(const vector & attrPath) { string s; - for (ATermIterator i(attrPath); i; ++i) { + foreach (vector::const_iterator, i, attrPath) { if (!s.empty()) s += '.'; - s += aterm2String(*i); + s += *i; } return s; } -struct Tree +static void addAttr(ExprAttrs * attrs, const vector & attrPath, Expr * e, const Pos & pos) { - Expr leaf; ATerm pos; bool recursive; - typedef std::map Children; - Children children; - Tree() { leaf = 0; recursive = true; } -}; - - -static ATermList buildAttrs(const Tree & t, ATermList & nonrec) -{ - ATermList res = ATempty; - for (Tree::Children::const_reverse_iterator i = t.children.rbegin(); - i != t.children.rend(); ++i) - if (!i->second.recursive) - nonrec = ATinsert(nonrec, makeBind(i->first, i->second.leaf, i->second.pos)); - else - res = ATinsert(res, i->second.leaf - ? makeBind(i->first, i->second.leaf, i->second.pos) - : makeBind(i->first, makeAttrs(buildAttrs(i->second, nonrec)), makeNoPos())); - return res; -} -#endif - - -static void fixAttrs(ExprAttrs & attrs) -{ -#if 0 - Tree attrs; - - /* This ATermMap is needed to ensure that the `leaf' fields in the - Tree nodes are not garbage collected. */ - ATermMap gcRoots; - - for (ATermIterator i(as); i; ++i) { - ATermList names, attrPath; Expr src, e; ATerm name, pos; - - if (matchInherit(*i, src, names, pos)) { - bool fromScope = matchScope(src); - for (ATermIterator j(names); j; ++j) { - if (attrs.children.find(*j) != attrs.children.end()) - throw ParseError(format("duplicate definition of attribute `%1%' at %2%") - % showAttrPath(ATmakeList1(*j)) % showPos(pos)); - Tree & t(attrs.children[*j]); - Expr leaf = fromScope ? makeVar(*j) : makeSelect(src, *j); - gcRoots.set(leaf, leaf); - t.leaf = leaf; - t.pos = pos; - if (recursive && fromScope) t.recursive = false; + unsigned int n = 0; + foreach (vector::const_iterator, i, attrPath) { + n++; + if (attrs->attrs[*i]) { + ExprAttrs * attrs2 = dynamic_cast(attrs->attrs[*i]); + if (!attrs2) + throw ParseError(format("attribute `%1%' at %2% already defined at ") + % showAttrPath(attrPath) % pos); + attrs = attrs2; + } else { + if (n == attrPath.size()) + attrs->attrs[*i] = e; + else { + ExprAttrs * nested = new ExprAttrs; + attrs->attrs[*i] = nested; + attrs = nested; } } - - else if (matchBindAttrPath(*i, attrPath, e, pos)) { - - Tree * t(&attrs); - - for (ATermIterator j(attrPath); j; ) { - name = *j; ++j; - if (t->leaf) throw ParseError(format("attribute set containing `%1%' at %2% already defined at %3%") - % showAttrPath(attrPath) % showPos(pos) % showPos(t->pos)); - t = &(t->children[name]); - } - - if (t->leaf) - throw ParseError(format("duplicate definition of attribute `%1%' at %2% and %3%") - % showAttrPath(attrPath) % showPos(pos) % showPos(t->pos)); - if (!t->children.empty()) - throw ParseError(format("duplicate definition of attribute `%1%' at %2%") - % showAttrPath(attrPath) % showPos(pos)); - - t->leaf = e; t->pos = pos; - } - - else abort(); /* can't happen */ } - - ATermList nonrec = ATempty; - ATermList rec = buildAttrs(attrs, nonrec); - - return recursive ? makeRec(rec, nonrec) : makeAttrs(rec); -#endif } @@ -307,7 +246,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err char * id; char * path; char * uri; - std::list * ids; + std::vector * ids; std::vector * string_parts; } @@ -360,7 +299,7 @@ expr_function | WITH expr ';' expr_function { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN expr_function - { $2->attrs[""] = $4; $2->recursive = true; fixAttrs(*$2); $$ = new ExprSelect($2, ""); } + { $2->attrs[""] = $4; $2->recursive = true; $$ = new ExprSelect($2, ""); } | expr_if ; @@ -418,11 +357,11 @@ expr_simple /* Let expressions `let {..., body = ...}' are just desugared into `(rec {..., body = ...}).body'. */ | LET '{' binds '}' - { fixAttrs(*$3); $3->recursive = true; $$ = new ExprSelect($3, "body"); } + { $3->recursive = true; $$ = new ExprSelect($3, "body"); } | REC '{' binds '}' - { fixAttrs(*$3); $3->recursive = true; $$ = $3; } + { $3->recursive = true; $$ = $3; } | '{' binds '}' - { fixAttrs(*$2); $$ = $2; } + { $$ = $2; } | '[' expr_list ']' { $$ = $2; } ; @@ -439,16 +378,16 @@ ind_string_parts ; binds - : binds ID '=' expr ';' { $$ = $1; $$->attrs[$2] = $4; } + : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, CUR_POS); } | binds INHERIT ids ';' { $$ = $1; - foreach (list::iterator, i, *$3) + foreach (vector::iterator, i, *$3) $$->inherited.push_back(*i); } | binds INHERIT '(' expr ')' ids ';' { $$ = $1; /* !!! Should ensure sharing of the expression in $4. */ - foreach (list::iterator, i, *$6) + foreach (vector::iterator, i, *$6) $$->attrs[*i] = new ExprSelect($4, *i); } | { $$ = new ExprAttrs; } @@ -456,12 +395,12 @@ binds ids : ids ID { $$ = $1; $1->push_back($2); /* !!! dangerous */ } - | { $$ = new list; } + | { $$ = new vector; } ; attrpath - : attrpath '.' ID { $$ = ATinsert($1, $3); } - | ID { $$ = ATmakeList1($1); } + : attrpath '.' ID { $$ = $1; $1->push_back($3); } + | ID { $$ = new vector; $$->push_back($1); } ; expr_list diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index cd40ade00..e50034a04 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1023,8 +1023,8 @@ void EvalState::createBaseEnv() /* Add a wrapper around the derivation primop that computes the `drvPath' and `outPath' attributes lazily. */ string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }"; - //mkThunk(v, baseEnv, parseExprFromString(s, "/")); - //addConstant("derivation", v); + mkThunk(v, baseEnv, parseExprFromString(s, "/")); + addConstant("derivation", v); // Miscellaneous addPrimOp("import", 1, prim_import); diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index c53c558c1..d9cf9a862 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -177,7 +177,7 @@ static void initAndRun(int argc, char * * argv) if (lt != "") setLogType(lt); /* ATerm stuff. !!! find a better place to put this */ - //initDerivationsHelpers(); + initDerivationsHelpers(); /* Put the arguments in a vector. */ Strings args, remaining; @@ -335,7 +335,7 @@ int main(int argc, char * * argv) /* ATerm setup. */ ATerm bottomOfStack; - //ATinit(argc, argv, &bottomOfStack); + ATinit(argc, argv, &bottomOfStack); /* Turn on buffering for cerr. */ #if HAVE_PUBSETBUF From ac1e8f40d4a5c380d68bb6f1c7cef6f1e7987c1a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 13 Apr 2010 12:25:42 +0000 Subject: [PATCH 060/103] * Use a symbol table to represent identifiers and attribute names efficiently. The symbol table ensures that there is only one copy of each symbol, thus allowing symbols to be compared efficiently using a pointer equality test. --- src/libexpr/Makefile.am | 2 +- src/libexpr/attr-path.cc | 2 +- src/libexpr/common-opts.cc | 4 +- src/libexpr/eval-test.cc | 25 ++++++++- src/libexpr/eval.cc | 45 +++++++++------- src/libexpr/eval.hh | 16 ++++-- src/libexpr/get-drvs.cc | 16 +++--- src/libexpr/nixexpr.cc | 6 +-- src/libexpr/nixexpr.hh | 26 ++++----- src/libexpr/parser.hh | 4 +- src/libexpr/parser.y | 69 ++++++++++++++---------- src/libexpr/primops.cc | 27 +++++----- src/libexpr/symbol-table.hh | 75 ++++++++++++++++++++++++++ src/libexpr/value-to-xml.cc | 8 +-- src/nix-instantiate/nix-instantiate.cc | 4 +- 15 files changed, 228 insertions(+), 101 deletions(-) create mode 100644 src/libexpr/symbol-table.hh diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am index 60b1815d9..39423394a 100644 --- a/src/libexpr/Makefile.am +++ b/src/libexpr/Makefile.am @@ -8,7 +8,7 @@ libexpr_la_SOURCES = \ pkginclude_HEADERS = \ nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \ get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \ - names.hh + names.hh symbol-table.hh libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \ ../boost/format/libformat.la diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index aa421ab43..b53837781 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -45,7 +45,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath, format("the expression selected by the selection path `%1%' should be an attribute set but is %2%") % curPath % showType(v)); - Bindings::iterator a = v.attrs->find(attr); + Bindings::iterator a = v.attrs->find(state.symbols.create(attr)); if (a == v.attrs->end()) throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath); v = a->second; diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc index 6171de0f0..a25317de1 100644 --- a/src/libexpr/common-opts.cc +++ b/src/libexpr/common-opts.cc @@ -20,10 +20,10 @@ bool parseOptionArg(const string & arg, Strings::iterator & i, if (i == argsEnd) throw error; string value = *i++; - Value & v(autoArgs[name]); + Value & v(autoArgs[state.symbols.create(name)]); if (arg == "--arg") - state.mkThunk_(v, parseExprFromString(value, absPath("."))); + state.mkThunk_( v, parseExprFromString(state, value, absPath("."))); else mkString(v, value); diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index ffadd41a7..d03d3bdee 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -12,7 +12,7 @@ using namespace nix; void doTest(EvalState & state, string s) { - Expr * e = parseExprFromString(s, absPath(".")); + Expr * e = parseExprFromString(state, s, absPath(".")); std::cerr << ">>>>> " << *e << std::endl; Value v; state.eval(e, v); @@ -23,6 +23,29 @@ void doTest(EvalState & state, string s) void run(Strings args) { + SymbolTable t; + + printMsg(lvlError, format("size of symbol: %1% bytes") % sizeof(Symbol)); + + Symbol s1 = t.create("foo"); + Symbol s2 = t.create("foo"); + Symbol s3 = t.create("bar"); + Symbol s4 = t.create("foo"); + + assert(s1 == s2); + assert(s1 == s4); + assert(s1 != s3); + + std::map m; + + m[s1] = 123; + m[s3] = 456; + + std::cout << m[s1] << std::endl; + std::cout << m[s2] << std::endl; + std::cout << m[s3] << std::endl; + std::cout << m[s4] << std::endl; + EvalState state; printMsg(lvlError, format("size of value: %1% bytes") % sizeof(Value)); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 99149fd7f..ffeae8d73 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -43,8 +43,8 @@ std::ostream & operator << (std::ostream & str, Value & v) break; case tAttrs: str << "{ "; - foreach (Bindings::iterator, i, *v.attrs) - str << i->first << " = " << i->second << "; "; + foreach (Bindings::iterator, i, *v.attrs) + str << (string) i->first << " = " << i->second << "; "; str << "}"; break; case tList: @@ -91,7 +91,14 @@ string showType(Value & v) } -EvalState::EvalState() : baseEnv(allocEnv()) +EvalState::EvalState() + : sWith(symbols.create("")) + , sOutPath(symbols.create("outPath")) + , sDrvPath(symbols.create("drvPath")) + , sType(symbols.create("type")) + , sMeta(symbols.create("meta")) + , sName(symbols.create("name")) + , baseEnv(allocEnv()) { nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0; deepestStack = (char *) -1; @@ -110,9 +117,9 @@ EvalState::~EvalState() void EvalState::addConstant(const string & name, Value & v) { - baseEnv.bindings[name] = v; + baseEnv.bindings[symbols.create(name)] = v; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; - (*baseEnv.bindings["builtins"].attrs)[name2] = v; + (*baseEnv.bindings[symbols.create("builtins")].attrs)[symbols.create(name2)] = v; nrValues += 2; } @@ -124,9 +131,9 @@ void EvalState::addPrimOp(const string & name, v.type = tPrimOp; v.primOp.arity = arity; v.primOp.fun = primOp; - baseEnv.bindings[name] = v; + baseEnv.bindings[symbols.create(name)] = v; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; - (*baseEnv.bindings["builtins"].attrs)[name2] = v; + (*baseEnv.bindings[symbols.create("builtins")].attrs)[symbols.create(name2)] = v; nrValues += 2; } @@ -225,12 +232,12 @@ void mkPath(Value & v, const char * s) } -static Value * lookupWith(Env * env, const Sym & name) +Value * EvalState::lookupWith(Env * env, const Symbol & name) { if (!env) return 0; Value * v = lookupWith(env->up, name); if (v) return v; - Bindings::iterator i = env->bindings.find(""); + Bindings::iterator i = env->bindings.find(sWith); if (i == env->bindings.end()) return 0; Bindings::iterator j = i->second.attrs->find(name); if (j != i->second.attrs->end()) return &j->second; @@ -238,7 +245,7 @@ static Value * lookupWith(Env * env, const Sym & name) } -static Value * lookupVar(Env * env, const Sym & name) +Value * EvalState::lookupVar(Env * env, const Symbol & name) { /* First look for a regular variable binding for `name'. */ for (Env * env2 = env; env2; env2 = env2->up) { @@ -318,7 +325,7 @@ void EvalState::evalFile(const Path & path, Value & v) Expr * e = parseTrees[path]; if (!e) { - e = parseExprFromFile(path); + e = parseExprFromFile(*this, path); parseTrees[path] = e; } @@ -428,9 +435,9 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) /* The inherited attributes, on the other hand, are evaluated in the original environment. */ - foreach (list::iterator, i, inherited) { + foreach (list::iterator, i, inherited) { Value & v2 = env2.bindings[*i]; - mkCopy(v2, *lookupVar(&env, *i)); + mkCopy(v2, *state.lookupVar(&env, *i)); } } @@ -441,9 +448,9 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) mkThunk(v2, env, i->second); } - foreach (list::iterator, i, inherited) { + foreach (list::iterator, i, inherited) { Value & v2 = (*v.attrs)[*i]; - mkCopy(v2, *lookupVar(&env, *i)); + mkCopy(v2, *state.lookupVar(&env, *i)); } } } @@ -459,7 +466,7 @@ void ExprList::eval(EvalState & state, Env & env, Value & v) void ExprVar::eval(EvalState & state, Env & env, Value & v) { - Value * v2 = lookupVar(&env, name); + Value * v2 = state.lookupVar(&env, name); state.forceValue(*v2); v = *v2; } @@ -631,7 +638,7 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) Env & env2(state.allocEnv()); env2.up = &env; - Value & vAttrs = env2.bindings[""]; + Value & vAttrs = env2.bindings[state.sWith]; state.eval(env, attrs, vAttrs); state.forceAttrs(vAttrs); @@ -871,7 +878,7 @@ string EvalState::forceStringNoCtx(Value & v) bool EvalState::isDerivation(Value & v) { if (v.type != tAttrs) return false; - Bindings::iterator i = v.attrs->find("type"); + Bindings::iterator i = v.attrs->find(sType); return i != v.attrs->end() && forceStringNoCtx(i->second) == "derivation"; } @@ -915,7 +922,7 @@ string EvalState::coerceToString(Value & v, PathSet & context, } if (v.type == tAttrs) { - Bindings::iterator i = v.attrs->find("outPath"); + Bindings::iterator i = v.attrs->find(sOutPath); if (i == v.attrs->end()) throwTypeError("cannot coerce an attribute set (except a derivation) to a string"); return coerceToString(i->second, context, coerceMore, copyToStore); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index cbdd09085..fe91db2ef 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -4,6 +4,7 @@ #include #include "nixexpr.hh" +#include "symbol-table.hh" namespace nix { @@ -14,9 +15,7 @@ class EvalState; struct Env; struct Value; -typedef string Sym; - -typedef std::map Bindings; +typedef std::map Bindings; struct Env @@ -161,6 +160,10 @@ class EvalState public: DrvHashes drvHashes; /* normalised derivation hashes */ + SymbolTable symbols; + + const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName; + private: SrcToStore srcToStore; @@ -235,6 +238,13 @@ private: void addPrimOp(const string & name, unsigned int arity, PrimOp primOp); + Value * lookupVar(Env * env, const Symbol & name); + + Value * lookupWith(Env * env, const Symbol & name); + + friend class ExprVar; + friend class ExprAttrs; + public: /* Do a deep equality test between two values. That is, list diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 95938d5c1..399470192 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -8,7 +8,7 @@ namespace nix { string DrvInfo::queryDrvPath(EvalState & state) const { if (drvPath == "") { - Bindings::iterator i = attrs->find("drvPath"); + Bindings::iterator i = attrs->find(state.sDrvPath); PathSet context; (string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; } @@ -19,7 +19,7 @@ string DrvInfo::queryDrvPath(EvalState & state) const string DrvInfo::queryOutPath(EvalState & state) const { if (outPath == "") { - Bindings::iterator i = attrs->find("outPath"); + Bindings::iterator i = attrs->find(state.sOutPath); PathSet context; (string &) outPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; } @@ -31,7 +31,7 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const { MetaInfo meta; - Bindings::iterator a = attrs->find("meta"); + Bindings::iterator a = attrs->find(state.sMeta); if (a == attrs->end()) return meta; /* fine, empty meta information */ state.forceAttrs(a->second); @@ -113,12 +113,12 @@ static bool getDerivation(EvalState & state, Value & v, DrvInfo drv; - Bindings::iterator i = v.attrs->find("name"); + Bindings::iterator i = v.attrs->find(state.sName); /* !!! We really would like to have a decent back trace here. */ if (i == v.attrs->end()) throw TypeError("derivation name missing"); drv.name = state.forceStringNoCtx(i->second); - i = v.attrs->find("system"); + i = v.attrs->find(state.symbols.create("system")); if (i == v.attrs->end()) drv.system = "unknown"; else @@ -170,7 +170,7 @@ static void getDerivations(EvalState & state, Value & vIn, /* !!! undocumented hackery to support combining channels in nix-env.cc. */ - bool combineChannels = v.attrs->find("_combineChannels") != v.attrs->end(); + bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end(); /* Consider the attributes in sorted order to get more deterministic behaviour in nix-env operations (e.g. when @@ -184,7 +184,7 @@ static void getDerivations(EvalState & state, Value & vIn, foreach (StringSet::iterator, i, attrs) { startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % *i); string pathPrefix2 = addToPath(pathPrefix, *i); - Value & v2((*v.attrs)[*i]); + Value & v2((*v.attrs)[state.symbols.create(*i)]); if (combineChannels) getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); else if (getDerivation(state, v2, pathPrefix2, drvs, done)) { @@ -193,7 +193,7 @@ static void getDerivations(EvalState & state, Value & vIn, if it has a `recurseForDerivations = true' attribute. */ if (v2.type == tAttrs) { - Bindings::iterator j = v2.attrs->find("recurseForDerivations"); + Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations")); if (j != v2.attrs->end() && state.forceBool(j->second)) getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 1902e23b0..922066c23 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -54,7 +54,7 @@ void ExprAttrs::show(std::ostream & str) { if (recursive) str << "rec "; str << "{ "; - foreach (list::iterator, i, inherited) + foreach (list::iterator, i, inherited) str << "inherit " << *i << "; "; foreach (Attrs::iterator, i, attrs) str << i->first << " = " << *i->second << "; "; @@ -81,9 +81,9 @@ void ExprLambda::show(std::ostream & str) if (i->def) str << " ? " << *i->def; } str << " }"; - if (arg != "") str << " @ "; + if (!arg.empty()) str << " @ "; } - if (arg != "") str << arg; + if (!arg.empty()) str << arg; str << ": " << *body << ")"; } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 725f9fe88..f0c05d435 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -3,7 +3,7 @@ #include -#include "types.hh" +#include "symbol-table.hh" namespace nix { @@ -75,33 +75,33 @@ struct ExprPath : Expr struct ExprVar : Expr { - string name; - ExprVar(const string & name) : name(name) { }; + Symbol name; + ExprVar(const Symbol & name) : name(name) { }; COMMON_METHODS }; struct ExprSelect : Expr { Expr * e; - string name; - ExprSelect(Expr * e, const string & name) : e(e), name(name) { }; + Symbol name; + ExprSelect(Expr * e, const Symbol & name) : e(e), name(name) { }; COMMON_METHODS }; struct ExprOpHasAttr : Expr { Expr * e; - string name; - ExprOpHasAttr(Expr * e, const string & name) : e(e), name(name) { }; + Symbol name; + ExprOpHasAttr(Expr * e, const Symbol & name) : e(e), name(name) { }; COMMON_METHODS }; struct ExprAttrs : Expr { bool recursive; - typedef std::map Attrs; + typedef std::map Attrs; Attrs attrs; - list inherited; + list inherited; ExprAttrs() : recursive(false) { }; COMMON_METHODS }; @@ -115,9 +115,9 @@ struct ExprList : Expr struct Formal { - string name; + Symbol name; Expr * def; - Formal(const string & name, Expr * def) : name(name), def(def) { }; + Formal(const Symbol & name, Expr * def) : name(name), def(def) { }; }; struct Formals @@ -130,11 +130,11 @@ struct Formals struct ExprLambda : Expr { Pos pos; - string arg; + Symbol arg; bool matchAttrs; Formals * formals; Expr * body; - ExprLambda(const Pos & pos, const string & arg, bool matchAttrs, Formals * formals, Expr * body) + ExprLambda(const Pos & pos, const Symbol & arg, bool matchAttrs, Formals * formals, Expr * body) : pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body) { }; COMMON_METHODS }; diff --git a/src/libexpr/parser.hh b/src/libexpr/parser.hh index d1e531ca2..c8c8ad809 100644 --- a/src/libexpr/parser.hh +++ b/src/libexpr/parser.hh @@ -9,10 +9,10 @@ namespace nix { /* Parse a Nix expression from the specified file. If `path' refers to a directory, then "/default.nix" is appended. */ -Expr * parseExprFromFile(Path path); +Expr * parseExprFromFile(EvalState & state, Path path); /* Parse a Nix expression from the specified string. */ -Expr * parseExprFromString(const string & s, const Path & basePath); +Expr * parseExprFromString(EvalState & state, const string & s, const Path & basePath); } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 07bf56a1c..c1c17e2b2 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -37,17 +37,23 @@ namespace nix { struct ParseData { + SymbolTable & symbols; Expr * result; Path basePath; Path path; string error; + Symbol sLetBody; + ParseData(SymbolTable & symbols) + : symbols(symbols) + , sLetBody(symbols.create("")) + { }; }; -static string showAttrPath(const vector & attrPath) +static string showAttrPath(const vector & attrPath) { string s; - foreach (vector::const_iterator, i, attrPath) { + foreach (vector::const_iterator, i, attrPath) { if (!s.empty()) s += '.'; s += *i; } @@ -55,10 +61,11 @@ static string showAttrPath(const vector & attrPath) } -static void addAttr(ExprAttrs * attrs, const vector & attrPath, Expr * e, const Pos & pos) +static void addAttr(ExprAttrs * attrs, const vector & attrPath, + Expr * e, const Pos & pos) { unsigned int n = 0; - foreach (vector::const_iterator, i, attrPath) { + foreach (vector::const_iterator, i, attrPath) { n++; if (attrs->attrs[*i]) { ExprAttrs * attrs2 = dynamic_cast(attrs->attrs[*i]); @@ -243,10 +250,10 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err nix::Formals * formals; nix::Formal * formal; int n; - char * id; + char * id; // !!! -> Symbol char * path; char * uri; - std::vector * ids; + std::vector * ids; std::vector * string_parts; } @@ -287,19 +294,19 @@ expr: expr_function; expr_function : ID ':' expr_function - { $$ = new ExprLambda(CUR_POS, $1, false, 0, $3); /* checkPatternVars(CUR_POS, $1); $$ = makeFunction($1, $3, CUR_POS); */ } + { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), false, 0, $3); /* checkPatternVars(CUR_POS, $1); */ } | '{' formals '}' ':' expr_function - { $$ = new ExprLambda(CUR_POS, "", true, $2, $5); } + { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), true, $2, $5); } | '{' formals '}' '@' ID ':' expr_function - { $$ = new ExprLambda(CUR_POS, $5, true, $2, $7); } + { $$ = new ExprLambda(CUR_POS, data->symbols.create($5), true, $2, $7); } | ID '@' '{' formals '}' ':' expr_function - { $$ = new ExprLambda(CUR_POS, $1, true, $4, $7); } + { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), true, $4, $7); } | ASSERT expr ';' expr_function { $$ = new ExprAssert(CUR_POS, $2, $4); } | WITH expr ';' expr_function { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN expr_function - { $2->attrs[""] = $4; $2->recursive = true; $$ = new ExprSelect($2, ""); } + { $2->attrs[data->sLetBody] = $4; $2->recursive = true; $$ = new ExprSelect($2, data->sLetBody); } | expr_if ; @@ -316,7 +323,7 @@ expr_op | expr_op OR expr_op { $$ = new ExprOpOr($1, $3); } | expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); } | expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); } - | expr_op '?' ID { $$ = new ExprOpHasAttr($1, $3); } + | expr_op '?' ID { $$ = new ExprOpHasAttr($1, data->symbols.create($3)); } | expr_op '+' expr_op { vector * l = new vector; l->push_back($1); @@ -335,12 +342,12 @@ expr_app expr_select : expr_select '.' ID - { $$ = new ExprSelect($1, $3); } + { $$ = new ExprSelect($1, data->symbols.create($3)); } | expr_simple { $$ = $1; } ; expr_simple - : ID { $$ = new ExprVar($1); } + : ID { $$ = new ExprVar(data->symbols.create($1)); } | INT { $$ = new ExprInt($1); } | '"' string_parts '"' { /* For efficiency, and to simplify parse trees a bit. */ @@ -357,7 +364,7 @@ expr_simple /* Let expressions `let {..., body = ...}' are just desugared into `(rec {..., body = ...}).body'. */ | LET '{' binds '}' - { $3->recursive = true; $$ = new ExprSelect($3, "body"); } + { $3->recursive = true; $$ = new ExprSelect($3, data->symbols.create("body")); } | REC '{' binds '}' { $3->recursive = true; $$ = $3; } | '{' binds '}' @@ -381,26 +388,26 @@ binds : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, CUR_POS); } | binds INHERIT ids ';' { $$ = $1; - foreach (vector::iterator, i, *$3) + foreach (vector::iterator, i, *$3) $$->inherited.push_back(*i); } | binds INHERIT '(' expr ')' ids ';' { $$ = $1; /* !!! Should ensure sharing of the expression in $4. */ - foreach (vector::iterator, i, *$6) + foreach (vector::iterator, i, *$6) $$->attrs[*i] = new ExprSelect($4, *i); } | { $$ = new ExprAttrs; } ; ids - : ids ID { $$ = $1; $1->push_back($2); /* !!! dangerous */ } - | { $$ = new vector; } + : ids ID { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ } + | { $$ = new vector; } ; attrpath - : attrpath '.' ID { $$ = $1; $1->push_back($3); } - | ID { $$ = new vector; $$->push_back($1); } + : attrpath '.' ID { $$ = $1; $1->push_back(data->symbols.create($3)); } + | ID { $$ = new vector; $$->push_back(data->symbols.create($1)); } ; expr_list @@ -420,8 +427,8 @@ formals ; formal - : ID { $$ = new Formal($1, 0); } - | ID '?' expr { $$ = new Formal($1, $3); } + : ID { $$ = new Formal(data->symbols.create($1), 0); } + | ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); } ; %% @@ -432,14 +439,17 @@ formal #include #include +#include + namespace nix { -static Expr * parse(const char * text, const Path & path, const Path & basePath) +static Expr * parse(EvalState & state, const char * text, + const Path & path, const Path & basePath) { yyscan_t scanner; - ParseData data; + ParseData data(state.symbols); data.basePath = basePath; data.path = path; @@ -460,7 +470,7 @@ static Expr * parse(const char * text, const Path & path, const Path & basePath) } -Expr * parseExprFromFile(Path path) +Expr * parseExprFromFile(EvalState & state, Path path) { assert(path[0] == '/'); @@ -481,13 +491,14 @@ Expr * parseExprFromFile(Path path) path = canonPath(path + "/default.nix"); /* Read and parse the input file. */ - return parse(readFile(path).c_str(), path, dirOf(path)); + return parse(state, readFile(path).c_str(), path, dirOf(path)); } -Expr * parseExprFromString(const string & s, const Path & basePath) +Expr * parseExprFromString(EvalState & state, + const string & s, const Path & basePath) { - return parse(s.c_str(), "(string)", basePath); + return parse(state, s.c_str(), "(string)", basePath); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index e50034a04..257dcd2e9 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -280,7 +280,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) state.forceAttrs(*args[0]); /* Figure out the name first (for stack backtraces). */ - Bindings::iterator attr = args[0]->attrs->find("name"); + Bindings::iterator attr = args[0]->attrs->find(state.sName); if (attr == args[0]->attrs->end()) throw EvalError("required attribute `name' missing"); string drvName; @@ -448,8 +448,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) /* !!! assumes a single output */ state.mkAttrs(v); - mkString((*v.attrs)["outPath"], outPath, singleton(drvPath)); - mkString((*v.attrs)["drvPath"], drvPath, singleton("=" + drvPath)); + mkString((*v.attrs)[state.sOutPath], outPath, singleton(drvPath)); + mkString((*v.attrs)[state.sDrvPath], drvPath, singleton("=" + drvPath)); } @@ -667,7 +667,8 @@ static void prim_getAttr(EvalState & state, Value * * args, Value & v) { string attr = state.forceStringNoCtx(*args[0]); state.forceAttrs(*args[1]); - Bindings::iterator i = args[1]->attrs->find(attr); + // !!! Should we create a symbol here or just do a lookup? + Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); if (i == args[1]->attrs->end()) throw EvalError(format("attribute `%1%' missing") % attr); state.forceValue(i->second); @@ -680,7 +681,7 @@ static void prim_hasAttr(EvalState & state, Value * * args, Value & v) { string attr = state.forceStringNoCtx(*args[0]); state.forceAttrs(*args[1]); - mkBool(v, args[1]->attrs->find(attr) != args[1]->attrs->end()); + mkBool(v, args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); } @@ -701,7 +702,7 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v) for (unsigned int i = 0; i < args[1]->list.length; ++i) { state.forceStringNoCtx(args[1]->list.elems[i]); - v.attrs->erase(args[1]->list.elems[i].string.s); + v.attrs->erase(state.symbols.create(args[1]->list.elems[i].string.s)); } } @@ -720,16 +721,16 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v) Value & v2(args[0]->list.elems[i]); state.forceAttrs(v2); - Bindings::iterator j = v2.attrs->find("name"); + Bindings::iterator j = v2.attrs->find(state.sName); if (j == v2.attrs->end()) throw TypeError("`name' attribute missing in a call to `listToAttrs'"); string name = state.forceStringNoCtx(j->second); - j = v2.attrs->find("value"); + j = v2.attrs->find(state.symbols.create("value")); if (j == v2.attrs->end()) throw TypeError("`value' attribute missing in a call to `listToAttrs'"); - (*v.attrs)[name] = j->second; // !!! sharing? + (*v.attrs)[state.symbols.create(name)] = j->second; // !!! sharing? } } @@ -976,8 +977,8 @@ static void prim_parseDrvName(EvalState & state, Value * * args, Value & v) string name = state.forceStringNoCtx(*args[0]); DrvName parsed(name); state.mkAttrs(v); - mkString((*v.attrs)["name"], parsed.name); - mkString((*v.attrs)["version"], parsed.version); + mkString((*v.attrs)[state.sName], parsed.name); + mkString((*v.attrs)[state.symbols.create("version")], parsed.version); } @@ -998,7 +999,7 @@ void EvalState::createBaseEnv() { baseEnv.up = 0; - Value & builtins = baseEnv.bindings["builtins"]; + Value & builtins = baseEnv.bindings[symbols.create("builtins")]; builtins.type = tAttrs; builtins.attrs = new Bindings; @@ -1023,7 +1024,7 @@ void EvalState::createBaseEnv() /* Add a wrapper around the derivation primop that computes the `drvPath' and `outPath' attributes lazily. */ string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }"; - mkThunk(v, baseEnv, parseExprFromString(s, "/")); + mkThunk(v, baseEnv, parseExprFromString(*this, s, "/")); addConstant("derivation", v); // Miscellaneous diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh new file mode 100644 index 000000000..ae0751d11 --- /dev/null +++ b/src/libexpr/symbol-table.hh @@ -0,0 +1,75 @@ +#ifndef __SYMBOL_TABLE_H +#define __SYMBOL_TABLE_H + +#include + +#include "types.hh" + +namespace nix { + +/* Symbol table used by the parser and evaluator to represent and look + up identifiers and attribute sets efficiently. + SymbolTable::create() converts a string into a symbol. Symbols + have the property that they can be compared efficiently (using a + pointer equality test), because the symbol table stores only one + copy of each string. */ + +class Symbol +{ +private: + const string * s; // pointer into SymbolTable + Symbol(const string * s) : s(s) { }; + friend class SymbolTable; + +public: + bool operator == (const Symbol & s2) const + { + return s == s2.s; + } + + bool operator != (const Symbol & s2) const + { + return s != s2.s; + } + + bool operator < (const Symbol & s2) const + { + return s < s2.s; + } + + operator const string & () const + { + return *s; + } + + bool empty() const + { + return s->empty(); + } + + friend std::ostream & operator << (std::ostream & str, const Symbol & sym); +}; + +inline std::ostream & operator << (std::ostream & str, const Symbol & sym) +{ + str << *sym.s; + return str; +} + +class SymbolTable +{ +private: + typedef std::set Symbols; + Symbols symbols; + +public: + Symbol create(const string & s) + { + std::pair res = symbols.insert(s); + return Symbol(&*res.first); + } +}; + +} + +#endif /* !__SYMBOL_TABLE_H */ diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index c8a067aac..0c8fc143c 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -28,7 +28,7 @@ static void showAttrs(EvalState & state, bool strict, Bindings & attrs, names.insert(i->first); foreach (StringSet::iterator, i, names) { XMLOpenElement _(doc, "attr", singletonAttrs("name", *i)); - printValueAsXML(state, strict, attrs[*i], doc, context, drvsSeen); + printValueAsXML(state, strict, attrs[state.symbols.create(*i)], doc, context, drvsSeen); } } @@ -67,14 +67,14 @@ static void printValueAsXML(EvalState & state, bool strict, Value & v, if (state.isDerivation(v)) { XMLAttrs xmlAttrs; - Bindings::iterator a = v.attrs->find("derivation"); + Bindings::iterator a = v.attrs->find(state.symbols.create("derivation")); Path drvPath; - a = v.attrs->find("drvPath"); + a = v.attrs->find(state.sDrvPath); if (a != v.attrs->end() && a->second.type == tString) xmlAttrs["drvPath"] = drvPath = a->second.string.s; - a = v.attrs->find("outPath"); + a = v.attrs->find(state.sOutPath); if (a != v.attrs->end() && a->second.type == tString) xmlAttrs["outPath"] = a->second.string.s; diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index ba34fc63b..c61cf89c9 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -28,7 +28,7 @@ static Expr * parseStdin(EvalState & state) startNest(nest, lvlTalkative, format("parsing standard input")); string s, s2; while (getline(std::cin, s2)) s += s2 + "\n"; - return parseExprFromString(s, absPath(".")); + return parseExprFromString(state, s, absPath(".")); } @@ -136,7 +136,7 @@ void run(Strings args) foreach (Strings::iterator, i, files) { Path path = absPath(*i); - Expr * e = parseExprFromFile(path); + Expr * e = parseExprFromFile(state, path); processExpr(state, attrPaths, parseOnly, strict, autoArgs, evalOnly, xmlOutput, e); } From 7d47498b5ea1ad4685bad954e5407f628f7f5595 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 13 Apr 2010 13:42:25 +0000 Subject: [PATCH 061/103] * Evaluate lets directly (i.e. without desugaring to `rec { attrs...; = e; }.). This prevents the unnecessary allocation of an attribute set. --- src/libexpr/eval.cc | 25 +++++++++++++++++++++++++ src/libexpr/eval.hh | 1 + src/libexpr/nixexpr.cc | 10 ++++++++++ src/libexpr/nixexpr.hh | 8 ++++++++ src/libexpr/parser.y | 2 +- 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ffeae8d73..fbd618d41 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -456,6 +456,31 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) } +void ExprLet::eval(EvalState & state, Env & env, Value & v) +{ + /* Create a new environment that contains the attributes in this + `let'. */ + Env & env2(state.allocEnv()); + env2.up = &env; + + /* The recursive attributes are evaluated in the new + environment. */ + foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) { + Value & v2 = env2.bindings[i->first]; + mkThunk(v2, env2, i->second); + } + + /* The inherited attributes, on the other hand, are evaluated in + the original environment. */ + foreach (list::iterator, i, attrs->inherited) { + Value & v2 = env2.bindings[*i]; + mkCopy(v2, *state.lookupVar(&env, *i)); + } + + state.eval(env2, body, v); +} + + void ExprList::eval(EvalState & state, Env & env, Value & v) { state.mkList(v, elems.size()); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index fe91db2ef..0491fc481 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -244,6 +244,7 @@ private: friend class ExprVar; friend class ExprAttrs; + friend class ExprLet; public: diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 922066c23..0abc2a457 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -87,6 +87,16 @@ void ExprLambda::show(std::ostream & str) str << ": " << *body << ")"; } +void ExprLet::show(std::ostream & str) +{ + str << "let "; + foreach (list::iterator, i, attrs->inherited) + str << "inherit " << *i << "; "; + foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) + str << i->first << " = " << *i->second << "; "; + str << "in " << *body; +} + void ExprWith::show(std::ostream & str) { str << "with " << *attrs << "; " << *body; diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index f0c05d435..ccddb1629 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -139,6 +139,14 @@ struct ExprLambda : Expr COMMON_METHODS }; +struct ExprLet : Expr +{ + ExprAttrs * attrs; + Expr * body; + ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { }; + COMMON_METHODS +}; + struct ExprWith : Expr { Pos pos; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index c1c17e2b2..b746e757e 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -306,7 +306,7 @@ expr_function | WITH expr ';' expr_function { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN expr_function - { $2->attrs[data->sLetBody] = $4; $2->recursive = true; $$ = new ExprSelect($2, data->sLetBody); } + { $$ = new ExprLet($2, $4); } | expr_if ; From 816f9c0f6fae0229961bb573dfa0f75ff42c14eb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 13 Apr 2010 14:34:11 +0000 Subject: [PATCH 062/103] * Use std::tr1::unordered_set instead of std::set for the symbol table. This gives a 10% speed increase on `nix-instantiate /etc/nixos/nixos -A system --readonly-mode'. --- src/libexpr/eval.cc | 1 + src/libexpr/symbol-table.hh | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index fbd618d41..0505c5b24 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1058,6 +1058,7 @@ void EvalState::printStats() printMsg(v, format(" stack space per eval() level: %1% bytes") % ((&x - deepestStack) / (float) maxRecursionDepth)); printMsg(v, format(" values allocated: %1%") % nrValues); printMsg(v, format(" environments allocated: %1%") % nrEnvs); + printMsg(v, format(" symbols in symbol table: %1%") % symbols.size()); } diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index ae0751d11..424c23538 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -2,6 +2,7 @@ #define __SYMBOL_TABLE_H #include +#include #include "types.hh" @@ -59,7 +60,7 @@ inline std::ostream & operator << (std::ostream & str, const Symbol & sym) class SymbolTable { private: - typedef std::set Symbols; + typedef std::tr1::unordered_set Symbols; Symbols symbols; public: @@ -68,6 +69,11 @@ public: std::pair res = symbols.insert(s); return Symbol(&*res.first); } + + unsigned int size() const + { + return symbols.size(); + } }; } From 85d13c8f93c8b251f5883d9b38051b33bab1ad3e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 14 Apr 2010 08:37:08 +0000 Subject: [PATCH 063/103] * Change the semantics of "with" so that inner "withs" take precedence, i.e. `with {x=1;}; with {x=2;}; x' evaluates to 2'. This has a simpler implementation and seems more natural. There doesn't seem to be any code in Nixpkgs or NixOS that relies on the old behaviour. --- src/libexpr/eval.cc | 25 ++----------------------- src/libexpr/eval.hh | 2 -- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0505c5b24..d8acdcb6f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -232,19 +232,6 @@ void mkPath(Value & v, const char * s) } -Value * EvalState::lookupWith(Env * env, const Symbol & name) -{ - if (!env) return 0; - Value * v = lookupWith(env->up, name); - if (v) return v; - Bindings::iterator i = env->bindings.find(sWith); - if (i == env->bindings.end()) return 0; - Bindings::iterator j = i->second.attrs->find(name); - if (j != i->second.attrs->end()) return &j->second; - return 0; -} - - Value * EvalState::lookupVar(Env * env, const Symbol & name) { /* First look for a regular variable binding for `name'. */ @@ -254,22 +241,14 @@ Value * EvalState::lookupVar(Env * env, const Symbol & name) } /* Otherwise, look for a `with' attribute set containing `name'. - Outer `withs' take precedence (i.e. `with {x=1;}; with {x=2;}; - x' evaluates to 1). */ - Value * v = lookupWith(env, name); - if (v) return v; - - /* Alternative implementation where the inner `withs' take - precedence (i.e. `with {x=1;}; with {x=2;}; x' evaluates to - 2). */ -#if 0 + Inner `withs' take precedence (i.e. `with {x=1;}; with {x=2;}; + x' evaluates to 2). */ for (Env * env2 = env; env2; env2 = env2->up) { Bindings::iterator i = env2->bindings.find(sWith); if (i == env2->bindings.end()) continue; Bindings::iterator j = i->second.attrs->find(name); if (j != i->second.attrs->end()) return &j->second; } -#endif throwEvalError("undefined variable `%1%'", name); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 0491fc481..eda081261 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -240,8 +240,6 @@ private: Value * lookupVar(Env * env, const Symbol & name); - Value * lookupWith(Env * env, const Symbol & name); - friend class ExprVar; friend class ExprAttrs; friend class ExprLet; From 011b5da0f46e9796fc68bc5daf5fe4dd4b57f933 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 14 Apr 2010 09:39:06 +0000 Subject: [PATCH 064/103] * Get nix-env to compile again. --- src/nix-env/nix-env.cc | 43 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 6286648d1..d44517ba6 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -111,7 +111,7 @@ static bool isNixExpr(const Path & path) static void getAllExprs(EvalState & state, - const Path & path, ATermMap & attrs) + const Path & path, ExprAttrs & attrs) { Strings names = readDirectory(path); StringSet namesSorted(names.begin(), names.end()); @@ -131,8 +131,8 @@ static void getAllExprs(EvalState & state, string attrName = *i; if (hasSuffix(attrName, ".nix")) attrName = string(attrName, 0, attrName.size() - 4); - attrs.set(toATerm(attrName), makeAttrRHS( - parseExprFromFile(state, absPath(path2)), makeNoPos())); + attrs.attrs[state.symbols.create(attrName)] = + parseExprFromFile(state, absPath(path2)); } else /* `path2' is a directory (with no default.nix in it); @@ -144,7 +144,7 @@ static void getAllExprs(EvalState & state, static Expr * loadSourceExpr(EvalState & state, const Path & path) { - if (isNixExpr(path)) return parseExprFromFile(absPath(path)); + if (isNixExpr(path)) return parseExprFromFile(state, absPath(path)); /* The path is a directory. Put the Nix expressions in the directory in an attribute set, with the file name of each @@ -152,10 +152,10 @@ static Expr * loadSourceExpr(EvalState & state, const Path & path) (but keep the attribute set flat, not nested, to make it easier for a user to have a ~/.nix-defexpr directory that includes some system-wide directory). */ - ATermMap attrs; - attrs.set(toATerm("_combineChannels"), makeAttrRHS(makeList(ATempty), makeNoPos())); - getAllExprs(state, path, attrs); - return makeAttrs(attrs); + ExprAttrs * attrs = new ExprAttrs; + attrs->attrs[state.symbols.create("_combineChannels")] = new ExprInt(1); + getAllExprs(state, path, *attrs); + return attrs; } @@ -192,20 +192,6 @@ static Path getDefNixExprPath() } -struct AddPos : TermFun -{ - ATerm operator () (ATerm e) - { - ATerm x, y; - if (matchObsoleteBind(e, x, y)) - return makeBind(x, y, makeNoPos()); - if (matchObsoleteStr(e, x)) - return makeStr(x, ATempty); - return e; - } -}; - - static DrvInfos queryInstalled(EvalState & state, const Path & userEnv) { Path path = userEnv + "/manifest"; @@ -213,16 +199,15 @@ static DrvInfos queryInstalled(EvalState & state, const Path & userEnv) if (!pathExists(path)) return DrvInfos(); /* not an error, assume nothing installed */ + throw Error("not implemented"); +#if 0 Expr e = ATreadFromNamedFile(path.c_str()); if (!e) throw Error(format("cannot read Nix expression from `%1%'") % path); - /* Compatibility: Bind(x, y) -> Bind(x, y, NoPos). */ - AddPos addPos; - e = bottomupRewrite(addPos, e); - DrvInfos elems; // !!! getDerivations(state, e, "", ATermMap(1), elems); return elems; +#endif } @@ -519,11 +504,11 @@ static void queryInstSources(EvalState & state, (import ./foo.nix)' = `(import ./foo.nix).bar'. */ case srcNixExprs: { - Expr e1 = loadSourceExpr(state, instSource.nixExprPath); + Expr * e1 = loadSourceExpr(state, instSource.nixExprPath); foreach (Strings::const_iterator, i, args) { - Expr e2 = parseExprFromString(state, *i, absPath(".")); - Expr call = makeCall(e2, e1); + Expr * e2 = parseExprFromString(state, *i, absPath(".")); + Expr * call = new ExprApp(e2, e1); Value v; state.eval(call, v); getDerivations(state, v, "", instSource.autoArgs, elems); } From 816dd3f0612111718c338842283c1ee6577b9f0a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 14 Apr 2010 12:49:05 +0000 Subject: [PATCH 065/103] * Remove more obsolete code. --- src/libexpr/nixexpr.cc | 29 ---- src/libexpr/nixexpr.hh | 11 -- src/libutil/Makefile.am | 4 +- src/libutil/aterm-map.cc | 332 --------------------------------------- src/libutil/aterm-map.hh | 129 --------------- 5 files changed, 2 insertions(+), 503 deletions(-) delete mode 100644 src/libutil/aterm-map.cc delete mode 100644 src/libutil/aterm-map.hh diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 0abc2a457..4040cacc8 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -138,35 +138,6 @@ std::ostream & operator << (std::ostream & str, const Pos & pos) #if 0 -ATerm bottomupRewrite(TermFun & f, ATerm e) -{ - checkInterrupt(); - - if (ATgetType(e) == AT_APPL) { - AFun fun = ATgetAFun(e); - int arity = ATgetArity(fun); - ATerm args[arity]; - - for (int i = 0; i < arity; ++i) - args[i] = bottomupRewrite(f, ATgetArgument(e, i)); - - e = (ATerm) ATmakeApplArray(fun, args); - } - - else if (ATgetType(e) == AT_LIST) { - ATermList in = (ATermList) e; - ATermList out = ATempty; - - for (ATermIterator i(in); i; ++i) - out = ATinsert(out, bottomupRewrite(f, *i)); - - e = (ATerm) ATreverse(out); - } - - return f(e); -} - - static void varsBoundByPattern(ATermMap & map, Pattern pat) { ATerm name; diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index ccddb1629..0e595a1b1 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -207,17 +207,6 @@ struct ExprConcatStrings : Expr #if 0 -/* Generic bottomup traversal over ATerms. The traversal first - recursively descends into subterms, and then applies the given term - function to the resulting term. */ -struct TermFun -{ - virtual ~TermFun() { } - virtual ATerm operator () (ATerm e) = 0; -}; -ATerm bottomupRewrite(TermFun & f, ATerm e); - - /* Check whether all variables are defined in the given expression. Throw an exception if this isn't the case. */ void checkVarDefs(const ATermMap & def, Expr e); diff --git a/src/libutil/Makefile.am b/src/libutil/Makefile.am index bd0996543..55135e373 100644 --- a/src/libutil/Makefile.am +++ b/src/libutil/Makefile.am @@ -1,12 +1,12 @@ pkglib_LTLIBRARIES = libutil.la libutil_la_SOURCES = util.cc hash.cc serialise.cc \ - archive.cc aterm.cc aterm-map.cc xml-writer.cc + archive.cc aterm.cc xml-writer.cc libutil_la_LIBADD = ../boost/format/libformat.la pkginclude_HEADERS = util.hh hash.hh serialise.hh \ - archive.hh aterm.hh aterm-map.hh xml-writer.hh types.hh + archive.hh aterm.hh xml-writer.hh types.hh if !HAVE_OPENSSL libutil_la_SOURCES += \ diff --git a/src/libutil/aterm-map.cc b/src/libutil/aterm-map.cc deleted file mode 100644 index c31fcdba3..000000000 --- a/src/libutil/aterm-map.cc +++ /dev/null @@ -1,332 +0,0 @@ -#include "aterm-map.hh" - -#include - -#include -#include - -#include - - -namespace nix { - - -static const unsigned int maxLoadFactor = /* 1 / */ 3; -static unsigned int nrResizes = 0; -static unsigned int sizeTotalAlloc = 0; -static unsigned int sizeCurAlloc = 0; -static unsigned int sizeMaxAlloc = 0; - - -ATermMap::ATermMap(unsigned int expectedCount) -{ - init(expectedCount); -} - - -ATermMap::ATermMap(const ATermMap & map) -{ - init(map.maxCount); - copy(map.hashTable, map.capacity); -} - - -ATermMap & ATermMap::operator = (const ATermMap & map) -{ - if (this == &map) return *this; - free(); - init(map.maxCount); - copy(map.hashTable, map.capacity); - return *this; -} - - -ATermMap::~ATermMap() -{ - free(); -} - - -void ATermMap::init(unsigned int expectedCount) -{ - assert(sizeof(ATerm) * 2 == sizeof(KeyValue)); - capacity = 0; - count = 0; - maxCount = 0; - hashTable = 0; - resizeTable(expectedCount); -} - - -void ATermMap::free() -{ - if (hashTable) { - ATunprotectArray((ATerm *) hashTable); - ::free(hashTable); - sizeCurAlloc -= sizeof(KeyValue) * capacity; - hashTable = 0; - } -} - - -static unsigned int roundToPowerOf2(unsigned int x) -{ - x--; - x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; - x++; - return x; -} - - -void ATermMap::resizeTable(unsigned int expectedCount) -{ - if (expectedCount == 0) expectedCount = 1; -// cout << maxCount << " -> " << expectedCount << endl; -// cout << maxCount << " " << size << endl; -// cout << (double) size / maxCount << endl; - - unsigned int oldCapacity = capacity; - KeyValue * oldHashTable = hashTable; - - maxCount = expectedCount; - capacity = roundToPowerOf2(maxCount * maxLoadFactor); - hashTable = (KeyValue *) calloc(sizeof(KeyValue), capacity); - sizeTotalAlloc += sizeof(KeyValue) * capacity; - sizeCurAlloc += sizeof(KeyValue) * capacity; - if (sizeCurAlloc > sizeMaxAlloc) sizeMaxAlloc = sizeCurAlloc; - ATprotectArray((ATerm *) hashTable, capacity * 2); - -// cout << capacity << endl; - - /* Re-hash the elements in the old table. */ - if (oldCapacity != 0) { - count = 0; - copy(oldHashTable, oldCapacity); - ATunprotectArray((ATerm *) oldHashTable); - ::free(oldHashTable); - sizeCurAlloc -= sizeof(KeyValue) * oldCapacity; - nrResizes++; - } -} - - -void ATermMap::copy(KeyValue * elements, unsigned int capacity) -{ - for (unsigned int i = 0; i < capacity; ++i) - if (elements[i].value) /* i.e., non-empty, non-deleted element */ - set(elements[i].key, elements[i].value); -} - - -/* !!! use a bigger shift for 64-bit platforms? */ -static const unsigned int shift = 16; -static const unsigned long knuth = (unsigned long) (0.6180339887 * (1 << shift)); - - -unsigned long ATermMap::hash1(ATerm key) const -{ - /* Don't care about the least significant bits of the ATerm - pointer since they're always 0. */ - unsigned long key2 = ((unsigned long) key) >> 2; - - /* Approximately equal to: - double d = key2 * 0.6180339887; - unsigned int h = (int) (capacity * (d - floor(d))); - */ - - unsigned long h = (capacity * ((key2 * knuth) & ((1 << shift) - 1))) >> shift; - - return h; -} - - -unsigned long ATermMap::hash2(ATerm key) const -{ - unsigned long key2 = ((unsigned long) key) >> 2; - /* Note: the result must be relatively prime to `capacity' (which - is a power of 2), so we make sure that the result is always - odd. */ - unsigned long h = ((key2 * 134217689) & (capacity - 1)) | 1; - return h; -} - - -static unsigned int nrItemsSet = 0; -static unsigned int nrSetProbes = 0; - - -void ATermMap::set(ATerm key, ATerm value) -{ - if (count == maxCount) resizeTable(capacity * 2 / maxLoadFactor); - - nrItemsSet++; - for (unsigned int i = 0, h = hash1(key); i < capacity; - ++i, h = (h + hash2(key)) & (capacity - 1)) - { - // assert(h < capacity); - nrSetProbes++; - /* Note: to see whether a slot is free, we check - hashTable[h].value, not hashTable[h].key, since we use - value == 0 to mark deleted slots. */ - if (hashTable[h].value == 0 || hashTable[h].key == key) { - if (hashTable[h].value == 0) count++; - hashTable[h].key = key; - hashTable[h].value = value; - return; - } - } - - abort(); -} - - -static unsigned int nrItemsGet = 0; -static unsigned int nrGetProbes = 0; - - -ATerm ATermMap::get(ATerm key) const -{ - nrItemsGet++; - for (unsigned int i = 0, h = hash1(key); i < capacity; - ++i, h = (h + hash2(key)) & (capacity - 1)) - { - nrGetProbes++; - if (hashTable[h].key == 0) return 0; - if (hashTable[h].key == key) return hashTable[h].value; - } - return 0; -} - - -void ATermMap::remove(ATerm key) -{ - for (unsigned int i = 0, h = hash1(key); i < capacity; - ++i, h = (h + hash2(key)) & (capacity - 1)) - { - if (hashTable[h].key == 0) return; - if (hashTable[h].key == key) { - if (hashTable[h].value != 0) { - hashTable[h].value = 0; - count--; - } - return; - } - } -} - - -unsigned int ATermMap::size() -{ - return count; /* STL nomenclature */ -} - - -void printATermMapStats() -{ - using std::cerr; - using std::endl; - - cerr << "RESIZES: " << nrResizes << " " - << sizeTotalAlloc << " " - << sizeCurAlloc << " " - << sizeMaxAlloc << endl; - - cerr << "SET: " - << nrItemsSet << " " - << nrSetProbes << " " - << (double) nrSetProbes / nrItemsSet << endl; - - cerr << "GET: " - << nrItemsGet << " " - << nrGetProbes << " " - << (double) nrGetProbes / nrItemsGet << endl; -} - - -#if 0 -int main(int argc, char * * argv) -{ - ATerm bottomOfStack; - ATinit(argc, argv, &bottomOfStack); - - /* Make test terms. */ - int nrTestTerms = 100000; - ATerm testTerms[nrTestTerms]; - - for (int i = 0; i < nrTestTerms; ++i) { - char name[10]; - sprintf(name, "%d", (int) random() % 37); - - int arity = i == 0 ? 0 : (random() % 37); - ATerm kids[arity]; - for (int j = 0; j < arity; ++j) - kids[j] = testTerms[random() % i]; - - testTerms[i] = (ATerm) ATmakeApplArray(ATmakeAFun(name, arity, ATfalse), kids); -// ATwriteToSharedTextFile(testTerms[i], stdout); -// printf("\n"); - } - - - cout << "testing...\n"; - - - #define someTerm() (testTerms[(int) random() % nrTestTerms]) - - - for (int test = 0; test < 100000; ++test) { - //cerr << test << endl; - unsigned int n = 300; - ATermMap map(300); - ATerm keys[n], values[n]; - for (unsigned int i = 0; i < n; ++i) { - keys[i] = someTerm(); - values[i] = someTerm(); - map.set(keys[i], values[i]); - //cerr << "INSERT: " << keys[i] << " " << values[i] << endl; - } - - unsigned int size = map.size(); - assert(size <= n); - values[n - 1] = 0; - map.remove(keys[n - 1]); - assert(map.size() == size - 1); - - unsigned int checksum; - unsigned int count = 0; - for (ATermMap::const_iterator i = map.begin(); i != map.end(); ++i, ++count) { - assert(i->key); - assert(i->value); - checksum += (unsigned int) (*i).key; - checksum += (unsigned int) (*i).value; - // cout << (*i).key << " " << (*i).value << endl; - } - assert(count == size - 1); - - for (unsigned int i = 0; i < n; ++i) { - for (unsigned int j = i + 1; j < n; ++j) - if (keys[i] == keys[j]) goto x; - if (map.get(keys[i]) != values[i]) { - cerr << "MISMATCH: " << keys[i] << " " << values[i] << " " << map.get(keys[i]) << " " << i << endl; - abort(); - } - if (values[i] != 0) { - checksum -= (unsigned int) keys[i]; - checksum -= (unsigned int) values[i]; - } - x: ; - } - - assert(checksum == 0); - - for (unsigned int i = 0; i < 100; ++i) - map.get(someTerm()); - - } - - printATermMapStats(); -} -#endif - - -} diff --git a/src/libutil/aterm-map.hh b/src/libutil/aterm-map.hh deleted file mode 100644 index b732453a7..000000000 --- a/src/libutil/aterm-map.hh +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef __ATERM_MAP_H -#define __ATERM_MAP_H - -#include -#include - - -namespace nix { - - -class ATermMap -{ -public: - - struct KeyValue - { - ATerm key; - ATerm value; - }; - -private: - - /* Hash table for the map. We use open addressing, i.e., all - key/value pairs are stored directly in the table, and there are - no pointers. Collisions are resolved through probing. */ - KeyValue * hashTable; - - /* Current size of the hash table. */ - unsigned int capacity; - - /* Number of elements in the hash table. */ - unsigned int count; - - /* Maximum number of elements in the hash table. If `count' - exceeds this number, the hash table is expanded. */ - unsigned int maxCount; - -public: - - /* Create a map. `expectedCount' is the number of elements the - map is expected to hold. */ - ATermMap(unsigned int expectedCount = 16); - - ATermMap(const ATermMap & map); - - ~ATermMap(); - - ATermMap & operator = (const ATermMap & map); - - void set(ATerm key, ATerm value); - - ATerm get(ATerm key) const; - - ATerm operator [](ATerm key) const - { - return get(key); - } - - void remove(ATerm key); - - unsigned int size(); - - struct const_iterator - { - const ATermMap & map; - unsigned int pos; - const_iterator(const ATermMap & map, int pos) : map(map) - { - this->pos = pos; - } - bool operator !=(const const_iterator & i) - { - return pos != i.pos; - } - void operator ++() - { - if (pos == map.capacity) return; - do { ++pos; - } while (pos < map.capacity && map.hashTable[pos].value == 0); - } - const KeyValue & operator *() - { - assert(pos < map.capacity); - return map.hashTable[pos]; - } - const KeyValue * operator ->() - { - assert(pos < map.capacity); - return &map.hashTable[pos]; - } - }; - - friend class ATermMap::const_iterator; - - const_iterator begin() const - { - unsigned int i = 0; - while (i < capacity && hashTable[i].value == 0) ++i; - return const_iterator(*this, i); - } - - const_iterator end() const - { - return const_iterator(*this, capacity); - } - -private: - - void init(unsigned int expectedCount); - - void free(); - - void resizeTable(unsigned int expectedCount); - - void copy(KeyValue * elements, unsigned int capacity); - - inline unsigned long hash1(ATerm key) const; - inline unsigned long hash2(ATerm key) const; -}; - - -/* Hack. */ -void printATermMapStats(); - - -} - - -#endif /* !__ATERM_MAP_H */ From 9985230c00226826949473c3862c0c3afea74aaf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 14 Apr 2010 14:42:32 +0000 Subject: [PATCH 066/103] * After parsing, compute level/displacement pairs for each variable use site, allowing environments to be stores as vectors of values rather than maps. This should speed up evaluation and reduce the number of allocations. --- src/libexpr/eval-test.cc | 5 +- src/libexpr/eval.cc | 91 +++++++++------ src/libexpr/eval.hh | 16 +-- src/libexpr/nixexpr.cc | 234 ++++++++++++++++++++++++--------------- src/libexpr/nixexpr.hh | 48 ++++++-- src/libexpr/parser.y | 3 +- src/libexpr/primops.cc | 4 + 7 files changed, 258 insertions(+), 143 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index d03d3bdee..bcd3670df 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -51,11 +51,12 @@ void run(Strings args) printMsg(lvlError, format("size of value: %1% bytes") % sizeof(Value)); printMsg(lvlError, format("size of int AST node: %1% bytes") % sizeof(ExprInt)); printMsg(lvlError, format("size of attrset AST node: %1% bytes") % sizeof(ExprAttrs)); - + doTest(state, "123"); doTest(state, "{ x = 1; y = 2; }"); doTest(state, "{ x = 1; y = 2; }.y"); - doTest(state, "rec { x = 1; y = x; }.y"); + doTest(state, "let x = 1; y = 2; z = 3; in let a = 4; in y"); + doTest(state, "rec { x = 1; y = x; }.x"); doTest(state, "(x: x) 1"); doTest(state, "(x: y: y) 1 2"); doTest(state, "x: x"); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d8acdcb6f..9c3c869bf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -98,7 +98,7 @@ EvalState::EvalState() , sType(symbols.create("type")) , sMeta(symbols.create("meta")) , sName(symbols.create("name")) - , baseEnv(allocEnv()) + , baseEnv(allocEnv(128)) { nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0; deepestStack = (char *) -1; @@ -117,16 +117,19 @@ EvalState::~EvalState() void EvalState::addConstant(const string & name, Value & v) { +#if 0 baseEnv.bindings[symbols.create(name)] = v; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; (*baseEnv.bindings[symbols.create("builtins")].attrs)[symbols.create(name2)] = v; nrValues += 2; +#endif } void EvalState::addPrimOp(const string & name, unsigned int arity, PrimOp primOp) { +#if 0 Value v; v.type = tPrimOp; v.primOp.arity = arity; @@ -135,6 +138,7 @@ void EvalState::addPrimOp(const string & name, string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; (*baseEnv.bindings[symbols.create("builtins")].attrs)[symbols.create(name2)] = v; nrValues += 2; +#endif } @@ -234,8 +238,8 @@ void mkPath(Value & v, const char * s) Value * EvalState::lookupVar(Env * env, const Symbol & name) { +#if 0 /* First look for a regular variable binding for `name'. */ - for (Env * env2 = env; env2; env2 = env2->up) { Bindings::iterator i = env2->bindings.find(name); if (i != env2->bindings.end()) return &i->second; } @@ -250,7 +254,8 @@ Value * EvalState::lookupVar(Env * env, const Symbol & name) if (j != i->second.attrs->end()) return &j->second; } - throwEvalError("undefined variable `%1%'", name); + throwEvalError("urgh! undefined variable `%1%'", name); +#endif } @@ -261,10 +266,11 @@ Value * EvalState::allocValues(unsigned int count) } -Env & EvalState::allocEnv() +Env & EvalState::allocEnv(unsigned int size) { nrEnvs++; - return *(new Env); + Env * env = (Env *) malloc(sizeof(Env) + size * sizeof(Value)); + return *env; } @@ -343,7 +349,7 @@ void EvalState::eval(Env & env, Expr * e, Value & v) char x; if (&x < deepestStack) deepestStack = &x; - //debug(format("eval: %1%") % *e); + debug(format("eval: %1%") % *e); checkInterrupt(); @@ -396,28 +402,33 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v) void ExprAttrs::eval(EvalState & state, Env & env, Value & v) { if (recursive) { - /* Create a new environment that contains the attributes in this `rec'. */ - Env & env2(state.allocEnv()); + Env & env2(state.allocEnv(attrs.size() + inherited.size())); env2.up = &env; v.type = tAttrs; - v.attrs = &env2.bindings; + v.attrs = new Bindings; + unsigned int displ = 0; + /* The recursive attributes are evaluated in the new environment. */ foreach (Attrs::iterator, i, attrs) { - Value & v2 = env2.bindings[i->first]; - mkThunk(v2, env2, i->second); + Value & v2 = (*v.attrs)[i->first]; + mkCopy(v2, env2.values[displ]); + mkThunk(env2.values[displ++], env2, i->second); } +#if 0 /* The inherited attributes, on the other hand, are evaluated in the original environment. */ foreach (list::iterator, i, inherited) { Value & v2 = env2.bindings[*i]; mkCopy(v2, *state.lookupVar(&env, *i)); } +#endif + } else { @@ -439,22 +450,24 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) { /* Create a new environment that contains the attributes in this `let'. */ - Env & env2(state.allocEnv()); + Env & env2(state.allocEnv(attrs->attrs.size() + attrs->inherited.size())); env2.up = &env; - + + unsigned int displ = 0; + /* The recursive attributes are evaluated in the new environment. */ - foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) { - Value & v2 = env2.bindings[i->first]; - mkThunk(v2, env2, i->second); - } + foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) + mkThunk(env2.values[displ++], env2, i->second); +#if 0 /* The inherited attributes, on the other hand, are evaluated in the original environment. */ foreach (list::iterator, i, attrs->inherited) { Value & v2 = env2.bindings[*i]; mkCopy(v2, *state.lookupVar(&env, *i)); } +#endif state.eval(env2, body, v); } @@ -470,9 +483,16 @@ void ExprList::eval(EvalState & state, Env & env, Value & v) void ExprVar::eval(EvalState & state, Env & env, Value & v) { - Value * v2 = state.lookupVar(&env, name); - state.forceValue(*v2); - v = *v2; + printMsg(lvlError, format("eval var %1% %2% %3%") % fromWith % level % displ); + + if (fromWith) { + abort(); + } else { + Env * env2 = &env; + for (unsigned int l = level; l; --l, env2 = env2->up) ; + state.forceValue(env2->values[displ]); + v = env2->values[displ]; + } } @@ -559,22 +579,22 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%", showType(fun)); - Env & env2(allocEnv()); + unsigned int size = + (fun.lambda.fun->arg.empty() ? 0 : 1) + + (fun.lambda.fun->matchAttrs ? fun.lambda.fun->formals->formals.size() : 0); + Env & env2(allocEnv(size)); env2.up = fun.lambda.env; - if (!fun.lambda.fun->matchAttrs) { - Value & vArg = env2.bindings[fun.lambda.fun->arg]; - nrValues++; - vArg = arg; - } + unsigned int displ = 0; + + if (!fun.lambda.fun->matchAttrs) + env2.values[displ++] = arg; else { forceAttrs(arg); - if (!fun.lambda.fun->arg.empty()) { - env2.bindings[fun.lambda.fun->arg] = arg; - nrValues++; - } + if (!fun.lambda.fun->arg.empty()) + env2.values[displ++] = arg; /* For each formal argument, get the actual argument. If there is no matching actual argument but the formal @@ -582,17 +602,13 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) unsigned int attrsUsed = 0; foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) { Bindings::iterator j = arg.attrs->find(i->name); - - Value & v = env2.bindings[i->name]; - nrValues++; - if (j == arg.attrs->end()) { if (!i->def) throwTypeError("function at %1% called without required argument `%2%'", fun.lambda.fun->pos, i->name); - mkThunk(v, env2, i->def); + mkThunk(env2.values[displ++], env2, i->def); } else { attrsUsed++; - mkCopy(v, j->second); + mkCopy(env2.values[displ++], j->second); } } @@ -639,6 +655,8 @@ 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()); env2.up = &env; @@ -647,6 +665,7 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) state.forceAttrs(vAttrs); state.eval(env2, body, v); +#endif } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index eda081261..a24b7345e 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -18,13 +18,6 @@ struct Value; typedef std::map Bindings; -struct Env -{ - Env * up; - Bindings bindings; -}; - - typedef enum { tInt = 1, tBool, @@ -109,6 +102,13 @@ struct Value }; +struct Env +{ + Env * up; + Value values[0]; +}; + + static inline void mkInt(Value & v, int n) { v.type = tInt; @@ -258,7 +258,7 @@ public: /* Allocation primitives. */ Value * allocValues(unsigned int count); - Env & allocEnv(); + Env & allocEnv(unsigned int size); void mkList(Value & v, unsigned int length); void mkAttrs(Value & v); diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 4040cacc8..46cbb48ac 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -8,13 +8,14 @@ namespace nix { +/* Displaying abstract syntax trees. */ + std::ostream & operator << (std::ostream & str, Expr & e) { e.show(str); return str; } - void Expr::show(std::ostream & str) { abort(); @@ -135,103 +136,162 @@ std::ostream & operator << (std::ostream & str, const Pos & pos) str << (format("`%1%:%2%:%3%'") % pos.file % pos.line % pos.column).str(); return str; } - -#if 0 -static void varsBoundByPattern(ATermMap & map, Pattern pat) + +/* Computing levels/displacements for variables. */ + +void Expr::bindVars(const StaticEnv & env) { - ATerm name; - ATermList formals; - ATermBool ellipsis; - /* Use makeRemoved() so that it can be used directly in - substitute(). */ - if (matchVarPat(pat, name)) - map.set(name, makeRemoved()); - else if (matchAttrsPat(pat, formals, ellipsis, name)) { - if (name != sNoAlias) map.set(name, makeRemoved()); - for (ATermIterator i(formals); i; ++i) { - ATerm d1; - if (!matchFormal(*i, name, d1)) abort(); - map.set(name, makeRemoved()); + abort(); +} + +void ExprInt::bindVars(const StaticEnv & env) +{ +} + +void ExprString::bindVars(const StaticEnv & env) +{ +} + +void ExprPath::bindVars(const StaticEnv & env) +{ +} + +void ExprVar::bindVars(const StaticEnv & env) +{ + /* Check whether the variable appears in the environment. If so, + set its level and displacement. */ + const StaticEnv * curEnv; + unsigned int level; + int withLevel = -1; + for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++) { + if (curEnv->isWith) { + if (withLevel == -1) withLevel = level; + } else { + StaticEnv::Vars::const_iterator i = curEnv->vars.find(name); + if (i != curEnv->vars.end()) { + fromWith = false; + this->level = level; + displ = i->second; + return; + } } } - else abort(); + + /* Otherwise, the variable must be obtained from the nearest + enclosing `with'. If there is no `with', then we can issue an + "undefined variable" error now. */ + if (withLevel == -1) throw EvalError(format("undefined variable `%1%'") % name); + + fromWith = true; + this->level = withLevel; } - -/* We use memoisation to prevent exponential complexity on heavily - shared ATerms (remember, an ATerm is a graph, not a tree!). Note - that using an STL set is fine here wrt to ATerm garbage collection - since all the ATerms in the set are already reachable from - somewhere else. */ -static void checkVarDefs2(set & done, const ATermMap & defs, Expr e) +void ExprSelect::bindVars(const StaticEnv & env) { - if (done.find(e) != done.end()) return; - done.insert(e); - - ATerm name, pos, value; - ATerm with, body; - ATermList rbnds, nrbnds; - Pattern pat; - - /* Closed terms don't have free variables, so we don't have to - check by definition. */ - if (matchClosed(e, value)) return; - - else if (matchVar(e, name)) { - if (!defs.get(name)) - throw EvalError(format("undefined variable `%1%'") - % aterm2String(name)); - } - - else if (matchFunction(e, pat, body, pos)) { - ATermMap defs2(defs); - varsBoundByPattern(defs2, pat); - set done2; - checkVarDefs2(done2, defs2, pat); - checkVarDefs2(done2, defs2, body); - } - - else if (matchRec(e, rbnds, nrbnds)) { - checkVarDefs2(done, defs, (ATerm) nrbnds); - ATermMap defs2(defs); - for (ATermIterator i(rbnds); i; ++i) { - if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */ - defs2.set(name, (ATerm) ATempty); - } - for (ATermIterator i(nrbnds); i; ++i) { - if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */ - defs2.set(name, (ATerm) ATempty); - } - set done2; - checkVarDefs2(done2, defs2, (ATerm) rbnds); - } - - else if (matchWith(e, with, body, pos)) { - /* 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'. */ - checkVarDefs2(done, defs, with); - } - - else if (ATgetType(e) == AT_APPL) { - int arity = ATgetArity(ATgetAFun(e)); - for (int i = 0; i < arity; ++i) - checkVarDefs2(done, defs, ATgetArgument(e, i)); - } - - else if (ATgetType(e) == AT_LIST) - for (ATermIterator i((ATermList) e); i; ++i) - checkVarDefs2(done, defs, *i); + e->bindVars(env); } - -void checkVarDefs(const ATermMap & defs, Expr e) +void ExprOpHasAttr::bindVars(const StaticEnv & env) { - set done; - checkVarDefs2(done, defs, e); + e->bindVars(env); +} + +void ExprAttrs::bindVars(const StaticEnv & env) +{ + if (recursive) { + StaticEnv newEnv(false, &env); + + unsigned int displ = 0; + + foreach (ExprAttrs::Attrs::iterator, i, attrs) + newEnv.vars[i->first] = displ++; + + foreach (list::iterator, i, inherited) + newEnv.vars[*i] = displ++; + + foreach (ExprAttrs::Attrs::iterator, i, attrs) + i->second->bindVars(newEnv); + } + + else + foreach (ExprAttrs::Attrs::iterator, i, attrs) + i->second->bindVars(env); +} + +void ExprList::bindVars(const StaticEnv & env) +{ + foreach (vector::iterator, i, elems) + (*i)->bindVars(env); +} + +void ExprLambda::bindVars(const StaticEnv & env) +{ + StaticEnv newEnv(false, &env); + + unsigned int displ = 0; + + if (!arg.empty()) newEnv.vars[arg] = displ++; + + if (matchAttrs) { + foreach (Formals::Formals_::iterator, i, formals->formals) + newEnv.vars[i->name] = displ++; + + foreach (Formals::Formals_::iterator, i, formals->formals) + if (i->def) i->def->bindVars(newEnv); + } + + body->bindVars(newEnv); +} + +void ExprLet::bindVars(const StaticEnv & env) +{ + StaticEnv newEnv(false, &env); + + unsigned int displ = 0; + + foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) + newEnv.vars[i->first] = displ++; + + foreach (list::iterator, i, attrs->inherited) + newEnv.vars[*i] = displ++; + + foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) + i->second->bindVars(newEnv); + + body->bindVars(newEnv); +} + +void ExprWith::bindVars(const StaticEnv & env) +{ + attrs->bindVars(env); + StaticEnv newEnv(true, &env); + body->bindVars(newEnv); +} + +void ExprIf::bindVars(const StaticEnv & env) +{ + cond->bindVars(env); + then->bindVars(env); + else_->bindVars(env); +} + +void ExprAssert::bindVars(const StaticEnv & env) +{ + cond->bindVars(env); + body->bindVars(env); +} + +void ExprOpNot::bindVars(const StaticEnv & env) +{ + e->bindVars(env); +} + +void ExprConcatStrings::bindVars(const StaticEnv & env) +{ + foreach (vector::iterator, i, *es) + (*i)->bindVars(env); } -#endif } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 0e595a1b1..0422e5cf4 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -17,25 +17,29 @@ MakeError(Abort, EvalError) MakeError(TypeError, EvalError) +/* Position objects. */ + struct Pos { string file; unsigned int line, column; }; - std::ostream & operator << (std::ostream & str, const Pos & pos); -/* Abstract syntax of Nix expressions. */ - struct Env; struct Value; struct EvalState; +struct StaticEnv; + + +/* Abstract syntax of Nix expressions. */ struct Expr { virtual void show(std::ostream & str); + virtual void bindVars(const StaticEnv & env); virtual void eval(EvalState & state, Env & env, Value & v); }; @@ -43,7 +47,8 @@ std::ostream & operator << (std::ostream & str, Expr & e); #define COMMON_METHODS \ void show(std::ostream & str); \ - void eval(EvalState & state, Env & env, Value & v); + void eval(EvalState & state, Env & env, Value & v); \ + void bindVars(const StaticEnv & env); struct ExprInt : Expr { @@ -76,6 +81,20 @@ struct ExprPath : Expr struct ExprVar : Expr { Symbol name; + + /* Whether the variable comes from an environment (e.g. a rec, let + or function argument) or from a "with". */ + bool fromWith; + + /* In the former case, the value is obtained by going `level' + levels up from the current environment and getting the + `displ'th value in that environment. In the latter case, the + value is obtained by getting the attribute named `name' from + the attribute set stored in the environment that is `level' + levels up from the current one.*/ + unsigned int level; + unsigned int displ; + ExprVar(const Symbol & name) : name(name) { }; COMMON_METHODS }; @@ -186,6 +205,10 @@ struct ExprOpNot : Expr { \ str << *e1 << " " s " " << *e2; \ } \ + void bindVars(const StaticEnv & env) \ + { \ + e1->bindVars(env); e2->bindVars(env); \ + } \ void eval(EvalState & state, Env & env, Value & v); \ }; @@ -206,11 +229,18 @@ struct ExprConcatStrings : Expr }; -#if 0 -/* Check whether all variables are defined in the given expression. - Throw an exception if this isn't the case. */ -void checkVarDefs(const ATermMap & def, Expr e); -#endif +/* Static environments are used to map variable names onto (level, + displacement) pairs used to obtain the value of the variable at + runtime. */ +struct StaticEnv +{ + bool isWith; + const StaticEnv * up; + typedef std::map Vars; + Vars vars; + StaticEnv(bool isWith, const StaticEnv * up) : isWith(isWith), up(up) { }; +}; + } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index b746e757e..b36244352 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -461,7 +461,8 @@ static Expr * parse(EvalState & state, const char * text, if (res) throw ParseError(data.error); try { - // !!! checkVarDefs(state.primOps, data.result); + StaticEnv env(false, 0); + data.result->bindVars(env); } catch (Error & e) { throw ParseError(format("%1%, in `%2%'") % e.msg() % path); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 257dcd2e9..83af7ada9 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -999,9 +999,11 @@ void EvalState::createBaseEnv() { baseEnv.up = 0; +#if 0 Value & builtins = baseEnv.bindings[symbols.create("builtins")]; builtins.type = tAttrs; builtins.attrs = new Bindings; +#endif /* Add global constants such as `true' to the base environment. */ Value v; @@ -1023,9 +1025,11 @@ void EvalState::createBaseEnv() /* Add a wrapper around the derivation primop that computes the `drvPath' and `outPath' attributes lazily. */ +#if 0 string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }"; mkThunk(v, baseEnv, parseExprFromString(*this, s, "/")); addConstant("derivation", v); +#endif // Miscellaneous addPrimOp("import", 1, prim_import); From 110d1557782fac4f8cafa27e5cbbcdebefb7a4c7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 14 Apr 2010 15:01:04 +0000 Subject: [PATCH 067/103] * Implemented withs. --- src/libexpr/eval-test.cc | 3 ++- src/libexpr/eval.cc | 37 +++++++++++++++++++++++-------------- src/libexpr/nixexpr.cc | 12 ++++++++++++ src/libexpr/nixexpr.hh | 1 + 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index bcd3670df..f87113e59 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 9c3c869bf..d93447010 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 46cbb48ac..ab4fa6cba 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 0422e5cf4..f9ed34f58 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 }; From 81de12bc8fa09a89dae958a3ffc93e7a4c245db1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 14 Apr 2010 15:14:23 +0000 Subject: [PATCH 068/103] * Refactoring: move variable uses to a separate class. --- src/libexpr/eval.cc | 42 +++++++++++++----------------------------- src/libexpr/eval.hh | 2 +- src/libexpr/nixexpr.cc | 9 +++++++-- src/libexpr/nixexpr.hh | 13 ++++++++++--- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d93447010..ccd359ba7 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -236,24 +236,17 @@ void mkPath(Value & v, const char * s) } -Value * EvalState::lookupVar(Env * env, const Symbol & name) +Value * EvalState::lookupVar(Env * env, const VarRef & var) { -#if 0 - /* First look for a regular variable binding for `name'. */ - Bindings::iterator i = env2->bindings.find(name); - if (i != env2->bindings.end()) return &i->second; - } - - /* Otherwise, look for a `with' attribute set containing `name'. - Inner `withs' take precedence (i.e. `with {x=1;}; with {x=2;}; - x' evaluates to 2). */ - for (Env * env2 = env; env2; env2 = env2->up) { - Bindings::iterator i = env2->bindings.find(sWith); - if (i == env2->bindings.end()) continue; - Bindings::iterator j = i->second.attrs->find(name); - if (j != i->second.attrs->end()) return &j->second; - } -#endif + for (unsigned int l = var.level; l; --l, env = env->up) ; + + if (var.fromWith) { + Bindings::iterator j = env->values[0].attrs->find(var.name); + if (j == env->values[0].attrs->end()) + throwEvalError("undefined variable `%1%'", var.name); + return &j->second; + } else + return &env->values[var.displ]; } @@ -481,18 +474,9 @@ void ExprList::eval(EvalState & state, Env & env, Value & v) void ExprVar::eval(EvalState & state, Env & env, Value & v) { - Env * env2 = &env; - for (unsigned int l = level; l; --l, env2 = env2->up) ; - - if (fromWith) { - 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 { - state.forceValue(env2->values[displ]); - v = env2->values[displ]; - } + Value * v2 = state.lookupVar(&env, info); + state.forceValue(*v2); + v = *v2; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index a24b7345e..bc61c84ea 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -238,7 +238,7 @@ private: void addPrimOp(const string & name, unsigned int arity, PrimOp primOp); - Value * lookupVar(Env * env, const Symbol & name); + Value * lookupVar(Env * env, const VarRef & var); friend class ExprVar; friend class ExprAttrs; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index ab4fa6cba..03b849a38 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -38,7 +38,7 @@ void ExprPath::show(std::ostream & str) void ExprVar::show(std::ostream & str) { - str << name; + str << info.name; } void ExprSelect::show(std::ostream & str) @@ -157,7 +157,7 @@ void ExprPath::bindVars(const StaticEnv & env) { } -void ExprVar::bindVars(const StaticEnv & env) +void VarRef::bind(const StaticEnv & env) { /* Check whether the variable appears in the environment. If so, set its level and displacement. */ @@ -187,6 +187,11 @@ void ExprVar::bindVars(const StaticEnv & env) this->level = withLevel; } +void ExprVar::bindVars(const StaticEnv & env) +{ + info.bind(env); +} + void ExprSelect::bindVars(const StaticEnv & env) { e->bindVars(env); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index f9ed34f58..ffea7049f 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -78,7 +78,7 @@ struct ExprPath : Expr COMMON_METHODS }; -struct ExprVar : Expr +struct VarRef { Symbol name; @@ -94,8 +94,15 @@ struct ExprVar : Expr levels up from the current one.*/ unsigned int level; unsigned int displ; - - ExprVar(const Symbol & name) : name(name) { }; + + VarRef(const Symbol & name) : name(name) { }; + void bind(const StaticEnv & env); +}; + +struct ExprVar : Expr +{ + VarRef info; + ExprVar(const Symbol & name) : info(name) { }; COMMON_METHODS }; From 267dc693d2ca8dea13199f92c265fc35fdb047f6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 14 Apr 2010 22:59:39 +0000 Subject: [PATCH 069/103] * Fix builtins. --- src/libexpr/eval.cc | 20 +++++++++----------- src/libexpr/eval.hh | 9 +++++++++ src/libexpr/parser.y | 3 +-- src/libexpr/primops.cc | 26 +++++++++++--------------- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ccd359ba7..f9502d8fb 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -43,7 +43,7 @@ std::ostream & operator << (std::ostream & str, Value & v) break; case tAttrs: str << "{ "; - foreach (Bindings::iterator, i, *v.attrs) + foreach (Bindings::iterator, i, *v.attrs) str << (string) i->first << " = " << i->second << "; "; str << "}"; break; @@ -99,6 +99,8 @@ EvalState::EvalState() , sMeta(symbols.create("meta")) , sName(symbols.create("name")) , baseEnv(allocEnv(128)) + , baseEnvDispl(0) + , staticBaseEnv(false, 0) { nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0; deepestStack = (char *) -1; @@ -117,28 +119,24 @@ EvalState::~EvalState() void EvalState::addConstant(const string & name, Value & v) { -#if 0 - baseEnv.bindings[symbols.create(name)] = v; + staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; + baseEnv.values[baseEnvDispl++] = v; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; - (*baseEnv.bindings[symbols.create("builtins")].attrs)[symbols.create(name2)] = v; - nrValues += 2; -#endif + (*baseEnv.values[0].attrs)[symbols.create(name2)] = v; } void EvalState::addPrimOp(const string & name, unsigned int arity, PrimOp primOp) { -#if 0 Value v; v.type = tPrimOp; v.primOp.arity = arity; v.primOp.fun = primOp; - baseEnv.bindings[symbols.create(name)] = v; + staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; + baseEnv.values[baseEnvDispl++] = v; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; - (*baseEnv.bindings[symbols.create("builtins")].attrs)[symbols.create(name2)] = v; - nrValues += 2; -#endif + (*baseEnv.values[0].attrs)[symbols.create(name2)] = v; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index bc61c84ea..551ae8d48 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -231,6 +231,15 @@ private: values. */ Env & baseEnv; + unsigned int baseEnvDispl; + +public: + + /* The same, but used during parsing to resolve variables. */ + StaticEnv staticBaseEnv; // !!! should be private + +private: + void createBaseEnv(); void addConstant(const string & name, Value & v); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index b36244352..06fcc72fc 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -461,8 +461,7 @@ static Expr * parse(EvalState & state, const char * text, if (res) throw ParseError(data.error); try { - StaticEnv env(false, 0); - data.result->bindVars(env); + data.result->bindVars(state.staticBaseEnv); } catch (Error & e) { throw ParseError(format("%1%, in `%2%'") % e.msg() % path); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 83af7ada9..0d7459fee 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -999,15 +999,13 @@ void EvalState::createBaseEnv() { baseEnv.up = 0; -#if 0 - Value & builtins = baseEnv.bindings[symbols.create("builtins")]; - builtins.type = tAttrs; - builtins.attrs = new Bindings; -#endif - /* Add global constants such as `true' to the base environment. */ Value v; + /* `builtins' must be first! */ + mkAttrs(v); + addConstant("builtins", v); + mkBool(v, true); addConstant("true", v); @@ -1023,14 +1021,6 @@ void EvalState::createBaseEnv() mkString(v, thisSystem.c_str()); addConstant("__currentSystem", v); - /* Add a wrapper around the derivation primop that computes the - `drvPath' and `outPath' attributes lazily. */ -#if 0 - string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }"; - mkThunk(v, baseEnv, parseExprFromString(*this, s, "/")); - addConstant("derivation", v); -#endif - // Miscellaneous addPrimOp("import", 1, prim_import); addPrimOp("isNull", 1, prim_isNull); @@ -1053,6 +1043,12 @@ void EvalState::createBaseEnv() // Derivations addPrimOp("derivationStrict", 1, prim_derivationStrict); + /* Add a wrapper around the derivation primop that computes the + `drvPath' and `outPath' attributes lazily. */ + string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }"; + mkThunk(v, baseEnv, parseExprFromString(*this, s, "/")); + addConstant("derivation", v); + // Paths addPrimOp("__toPath", 1, prim_toPath); #if 0 @@ -1105,7 +1101,7 @@ void EvalState::createBaseEnv() // Versions addPrimOp("__parseDrvName", 1, prim_parseDrvName); - addPrimOp("__compareVersions", 2, prim_compareVersions); + addPrimOp("__compareVersions", 2, prim_compareVersions); } From d39d3c6264707c466b21dfadd4d48653842a9156 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 14 Apr 2010 23:25:05 +0000 Subject: [PATCH 070/103] * Implemented inherit. --- src/libexpr/eval-test.cc | 5 ++++- src/libexpr/eval.cc | 30 ++++++++++++------------------ src/libexpr/nixexpr.cc | 26 +++++++++++++++++--------- src/libexpr/nixexpr.hh | 2 +- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index f87113e59..a7786561e 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -105,9 +105,12 @@ void run(Strings args) doTest(state, "map (x: __add 1 x) [ 1 2 3 ]"); doTest(state, "map (builtins.add 1) [ 1 2 3 ]"); doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); - doTest(state, "let x = 1; as = rec { inherit x; y = as.x; }; in as.y"); + doTest(state, "let x = 1; as = { inherit x; y = as.x; }; in as.y"); + doTest(state, "let x = 1; as = rec { inherit x; y = x; }; in as.y"); doTest(state, "let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); doTest(state, "let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); + doTest(state, "let x = 1; in let inherit x; in x"); + doTest(state, "with { x = 1; }; let inherit x; y = x; in y"); doTest(state, "builtins.toXML 123"); doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f9502d8fb..aa75a2ef0 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -338,7 +338,7 @@ void EvalState::eval(Env & env, Expr * e, Value & v) char x; if (&x < deepestStack) deepestStack = &x; - debug(format("eval: %1%") % *e); + //debug(format("eval: %1%") % *e); checkInterrupt(); @@ -390,14 +390,13 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v) void ExprAttrs::eval(EvalState & state, Env & env, Value & v) { + state.mkAttrs(v); + if (recursive) { /* Create a new environment that contains the attributes in this `rec'. */ Env & env2(state.allocEnv(attrs.size() + inherited.size())); env2.up = &env; - - v.type = tAttrs; - v.attrs = new Bindings; unsigned int displ = 0; @@ -409,26 +408,25 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) mkThunk(env2.values[displ++], env2, i->second); } -#if 0 /* The inherited attributes, on the other hand, are evaluated in the original environment. */ - foreach (list::iterator, i, inherited) { - Value & v2 = env2.bindings[*i]; - mkCopy(v2, *state.lookupVar(&env, *i)); + foreach (list::iterator, i, inherited) { + Value & v2 = (*v.attrs)[i->name]; + Value * v3 = state.lookupVar(&env, *i); + mkCopy(v2, *v3); + mkCopy(env2.values[displ++], *v3); } -#endif } else { - state.mkAttrs(v); foreach (Attrs::iterator, i, attrs) { Value & v2 = (*v.attrs)[i->first]; mkThunk(v2, env, i->second); } - foreach (list::iterator, i, inherited) { - Value & v2 = (*v.attrs)[*i]; + foreach (list::iterator, i, inherited) { + Value & v2 = (*v.attrs)[i->name]; mkCopy(v2, *state.lookupVar(&env, *i)); } } @@ -449,14 +447,10 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) mkThunk(env2.values[displ++], env2, i->second); -#if 0 /* The inherited attributes, on the other hand, are evaluated in the original environment. */ - foreach (list::iterator, i, attrs->inherited) { - Value & v2 = env2.bindings[*i]; - mkCopy(v2, *state.lookupVar(&env, *i)); - } -#endif + foreach (list::iterator, i, attrs->inherited) + mkCopy(env2.values[displ++], *state.lookupVar(&env, *i)); state.eval(env2, body, v); } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 03b849a38..3dfbddf5b 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -55,8 +55,8 @@ void ExprAttrs::show(std::ostream & str) { if (recursive) str << "rec "; str << "{ "; - foreach (list::iterator, i, inherited) - str << "inherit " << *i << "; "; + foreach (list::iterator, i, inherited) + str << "inherit " << i->name << "; "; foreach (Attrs::iterator, i, attrs) str << i->first << " = " << *i->second << "; "; str << "}"; @@ -91,8 +91,8 @@ void ExprLambda::show(std::ostream & str) void ExprLet::show(std::ostream & str) { str << "let "; - foreach (list::iterator, i, attrs->inherited) - str << "inherit " << *i << "; "; + foreach (list::iterator, i, attrs->inherited) + str << "inherit " << i->name << "; "; foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) str << i->first << " = " << *i->second << "; "; str << "in " << *body; @@ -212,16 +212,22 @@ void ExprAttrs::bindVars(const StaticEnv & env) foreach (ExprAttrs::Attrs::iterator, i, attrs) newEnv.vars[i->first] = displ++; - foreach (list::iterator, i, inherited) - newEnv.vars[*i] = displ++; + foreach (list::iterator, i, inherited) { + newEnv.vars[i->name] = displ++; + i->bind(env); + } foreach (ExprAttrs::Attrs::iterator, i, attrs) i->second->bindVars(newEnv); } - else + else { foreach (ExprAttrs::Attrs::iterator, i, attrs) i->second->bindVars(env); + + foreach (list::iterator, i, inherited) + i->bind(env); + } } void ExprList::bindVars(const StaticEnv & env) @@ -258,8 +264,10 @@ void ExprLet::bindVars(const StaticEnv & env) foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) newEnv.vars[i->first] = displ++; - foreach (list::iterator, i, attrs->inherited) - newEnv.vars[*i] = displ++; + foreach (list::iterator, i, attrs->inherited) { + newEnv.vars[i->name] = displ++; + i->bind(env); + } foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) i->second->bindVars(newEnv); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index ffea7049f..9e5a262d7 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -127,7 +127,7 @@ struct ExprAttrs : Expr bool recursive; typedef std::map Attrs; Attrs attrs; - list inherited; + list inherited; ExprAttrs() : recursive(false) { }; COMMON_METHODS }; From e41b5828db0c154e4a3f0ed6299a987fde5bc03f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 14 Apr 2010 23:48:46 +0000 Subject: [PATCH 071/103] * Better stats. --- src/libexpr/eval.cc | 17 +++++++++++++---- src/libexpr/eval.hh | 4 +++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index aa75a2ef0..d6e39f365 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -102,7 +102,8 @@ EvalState::EvalState() , baseEnvDispl(0) , staticBaseEnv(false, 0) { - nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0; + nrEnvs = nrValuesInEnvs = nrValuesInLists = nrValues = 0; + nrEvaluated = recursionDepth = maxRecursionDepth = 0; deepestStack = (char *) -1; createBaseEnv(); @@ -250,7 +251,6 @@ Value * EvalState::lookupVar(Env * env, const VarRef & var) Value * EvalState::allocValues(unsigned int count) { - nrValues += count; return new Value[count]; // !!! check destructor } @@ -258,6 +258,7 @@ Value * EvalState::allocValues(unsigned int count) Env & EvalState::allocEnv(unsigned int size) { nrEnvs++; + nrValuesInEnvs += size; Env * env = (Env *) malloc(sizeof(Env) + size * sizeof(Value)); return *env; } @@ -268,6 +269,7 @@ void EvalState::mkList(Value & v, unsigned int length) v.type = tList; v.list.length = length; v.list.elems = allocValues(length); + nrValuesInLists += length; } @@ -541,6 +543,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) primOp->primOp.fun(*this, vArgs, v); } else { Value * v2 = allocValues(2); + nrValues += 2; v2[0] = fun; v2[1] = arg; v.type = tPrimOpApp; @@ -1039,8 +1042,14 @@ void EvalState::printStats() printMsg(v, format(" stack space used: %1% bytes") % (&x - deepestStack)); printMsg(v, format(" max eval() nesting depth: %1%") % maxRecursionDepth); printMsg(v, format(" stack space per eval() level: %1% bytes") % ((&x - deepestStack) / (float) maxRecursionDepth)); - printMsg(v, format(" values allocated: %1%") % nrValues); - printMsg(v, format(" environments allocated: %1%") % nrEnvs); + printMsg(v, format(" environments allocated: %1% (%2% bytes)") + % nrEnvs % (nrEnvs * sizeof(Env))); + printMsg(v, format(" values allocated in environments: %1% (%2% bytes)") + % nrValuesInEnvs % (nrValuesInEnvs * sizeof(Value))); + printMsg(v, format(" values allocated in lists: %1% (%2% bytes)") + % nrValuesInLists % (nrValuesInLists * sizeof(Value))); + printMsg(v, format(" misc. values allocated: %1% (%2% bytes) ") + % nrValues % (nrValues * sizeof(Value))); printMsg(v, format(" symbols in symbol table: %1%") % symbols.size()); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 551ae8d48..313a1d9b8 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -280,8 +280,10 @@ public: private: - unsigned long nrValues; unsigned long nrEnvs; + unsigned long nrValuesInEnvs; + unsigned long nrValuesInLists; + unsigned long nrValues; unsigned long nrEvaluated; unsigned int recursionDepth; unsigned int maxRecursionDepth; From 04c4bd3624b094043ff0f2410c1e376a51f457f7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 15 Apr 2010 00:37:36 +0000 Subject: [PATCH 072/103] * Store lists as lists of pointers to values rather than as lists of values. This improves sharing and gives another speed up. Evaluation of the NixOS system attribute is now almost 7 times faster than the old evaluator. --- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval-test.cc | 1 + src/libexpr/eval.cc | 33 +++++++++++++++++---------------- src/libexpr/eval.hh | 4 ++-- src/libexpr/get-drvs.cc | 6 +++--- src/libexpr/primops.cc | 27 ++++++++++++++++----------- src/libexpr/value-to-xml.cc | 2 +- 7 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index b53837781..769acb6b8 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -61,7 +61,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath, if (attrIndex >= v.list.length) throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath); - v = v.list.elems[attrIndex]; + v = *v.list.elems[attrIndex]; } } diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index a7786561e..f7f91f503 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -113,6 +113,7 @@ void run(Strings args) doTest(state, "with { x = 1; }; let inherit x; y = x; in y"); doTest(state, "builtins.toXML 123"); doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); + doTest(state, "builtins.attrNames { x = 1; y = 2; }"); state.printStats(); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d6e39f365..69e7bd8b3 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -50,7 +50,7 @@ std::ostream & operator << (std::ostream & str, Value & v) case tList: str << "[ "; for (unsigned int n = 0; n < v.list.length; ++n) - str << v.list.elems[n] << " "; + str << *v.list.elems[n] << " "; str << "]"; break; case tThunk: @@ -102,7 +102,7 @@ EvalState::EvalState() , baseEnvDispl(0) , staticBaseEnv(false, 0) { - nrEnvs = nrValuesInEnvs = nrValuesInLists = nrValues = 0; + nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0; nrEvaluated = recursionDepth = maxRecursionDepth = 0; deepestStack = (char *) -1; @@ -251,6 +251,7 @@ Value * EvalState::lookupVar(Env * env, const VarRef & var) Value * EvalState::allocValues(unsigned int count) { + nrValues += count; return new Value[count]; // !!! check destructor } @@ -268,8 +269,8 @@ void EvalState::mkList(Value & v, unsigned int length) { v.type = tList; v.list.length = length; - v.list.elems = allocValues(length); - nrValuesInLists += length; + v.list.elems = new Value *[length]; + nrListElems += length; } @@ -461,8 +462,11 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) void ExprList::eval(EvalState & state, Env & env, Value & v) { state.mkList(v, elems.size()); - for (unsigned int n = 0; n < v.list.length; ++n) - mkThunk(v.list.elems[n], env, elems[n]); + Value * vs = state.allocValues(v.list.length); + for (unsigned int n = 0; n < v.list.length; ++n) { + v.list.elems[n] = &vs[n]; + mkThunk(vs[n], env, elems[n]); + } } @@ -543,7 +547,6 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) primOp->primOp.fun(*this, vArgs, v); } else { Value * v2 = allocValues(2); - nrValues += 2; v2[0] = fun; v2[1] = arg; v.type = tPrimOpApp; @@ -734,8 +737,6 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) Value v2; state.eval(env, e2, v2); state.forceList(v2); state.mkList(v, v1.list.length + v2.list.length); - /* !!! This loses sharing with the original lists. We could use a - tCopy node, but that would use more memory. */ for (unsigned int n = 0; n < v1.list.length; ++n) v.list.elems[n] = v1.list.elems[n]; for (unsigned int n = 0; n < v2.list.length; ++n) @@ -810,7 +811,7 @@ void EvalState::strictForceValue(Value & v) else if (v.type == tList) { for (unsigned int n = 0; n < v.list.length; ++n) - strictForceValue(v.list.elems[n]); + strictForceValue(*v.list.elems[n]); } } @@ -951,11 +952,11 @@ string EvalState::coerceToString(Value & v, PathSet & context, if (v.type == tList) { string result; for (unsigned int n = 0; n < v.list.length; ++n) { - result += coerceToString(v.list.elems[n], + result += coerceToString(*v.list.elems[n], context, coerceMore, copyToStore); if (n < v.list.length - 1 /* !!! not quite correct */ - && (v.list.elems[n].type != tList || v.list.elems[n].list.length != 0)) + && (v.list.elems[n]->type != tList || v.list.elems[n]->list.length != 0)) result += " "; } return result; @@ -1009,14 +1010,14 @@ bool EvalState::eqValues(Value & v1, Value & v2) case tList: if (v2.type != tList || v1.list.length != v2.list.length) return false; for (unsigned int n = 0; n < v1.list.length; ++n) - if (!eqValues(v1.list.elems[n], v2.list.elems[n])) return false; + if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false; return true; case tAttrs: { if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false; Bindings::iterator i, j; for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) - if (!eqValues(i->second, j->second)) return false; + if (i->first != j->first || !eqValues(i->second, j->second)) return false; return true; } @@ -1046,8 +1047,8 @@ void EvalState::printStats() % nrEnvs % (nrEnvs * sizeof(Env))); printMsg(v, format(" values allocated in environments: %1% (%2% bytes)") % nrValuesInEnvs % (nrValuesInEnvs * sizeof(Value))); - printMsg(v, format(" values allocated in lists: %1% (%2% bytes)") - % nrValuesInLists % (nrValuesInLists * sizeof(Value))); + printMsg(v, format(" list elements: %1% (%2% bytes)") + % nrListElems % (nrListElems * sizeof(Value *))); printMsg(v, format(" misc. values allocated: %1% (%2% bytes) ") % nrValues % (nrValues * sizeof(Value))); printMsg(v, format(" symbols in symbol table: %1%") % symbols.size()); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 313a1d9b8..7252cae4b 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -76,7 +76,7 @@ struct Value Bindings * attrs; struct { unsigned int length; - Value * elems; + Value * * elems; } list; struct { Env * env; @@ -282,8 +282,8 @@ private: unsigned long nrEnvs; unsigned long nrValuesInEnvs; - unsigned long nrValuesInLists; unsigned long nrValues; + unsigned long nrListElems; unsigned long nrEvaluated; unsigned int recursionDepth; unsigned int maxRecursionDepth; diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 399470192..6964e3e3b 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -48,7 +48,7 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const } else if (i->second.type == tList) { value.type = MetaValue::tpStrings; for (unsigned int j = 0; j < i->second.list.length; ++j) - value.stringValues.push_back(state.forceStringNoCtx(i->second.list.elems[j])); + value.stringValues.push_back(state.forceStringNoCtx(*i->second.list.elems[j])); } else continue; meta[i->first] = value; } @@ -206,8 +206,8 @@ static void getDerivations(EvalState & state, Value & vIn, startNest(nest, lvlDebug, format("evaluating list element")); string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); - if (getDerivation(state, v.list.elems[n], pathPrefix2, drvs, done)) - getDerivations(state, v.list.elems[n], pathPrefix2, autoArgs, drvs, done); + if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done)) + getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done); } } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0d7459fee..ae17506ce 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -311,7 +311,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) if (key == "args") { state.forceList(i->second); for (unsigned int n = 0; n < i->second.list.length; ++n) { - string s = state.coerceToString(i->second.list.elems[n], context, true); + string s = state.coerceToString(*i->second.list.elems[n], context, true); drv.args.push_back(s); } } @@ -651,14 +651,17 @@ static void prim_attrNames(EvalState & state, Value * * args, Value & v) state.forceAttrs(*args[0]); state.mkList(v, args[0]->attrs->size()); + Value * vs = state.allocValues(v.list.length); StringSet names; foreach (Bindings::iterator, i, *args[0]->attrs) names.insert(i->first); unsigned int n = 0; - foreach (StringSet::iterator, i, names) - mkString(v.list.elems[n++], *i); + foreach (StringSet::iterator, i, names) { + v.list.elems[n] = &vs[n]; + mkString(vs[n++], *i); + } } @@ -701,8 +704,8 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v) state.cloneAttrs(*args[0], v); for (unsigned int i = 0; i < args[1]->list.length; ++i) { - state.forceStringNoCtx(args[1]->list.elems[i]); - v.attrs->erase(state.symbols.create(args[1]->list.elems[i].string.s)); + state.forceStringNoCtx(*args[1]->list.elems[i]); + v.attrs->erase(state.symbols.create(args[1]->list.elems[i]->string.s)); } } @@ -718,7 +721,7 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v) state.mkAttrs(v); for (unsigned int i = 0; i < args[0]->list.length; ++i) { - Value & v2(args[0]->list.elems[i]); + Value & v2(*args[0]->list.elems[i]); state.forceAttrs(v2); Bindings::iterator j = v2.attrs->find(state.sName); @@ -815,8 +818,8 @@ static void prim_head(EvalState & state, Value * * args, Value & v) state.forceList(*args[0]); if (args[0]->list.length == 0) throw Error("`head' called on an empty list"); - state.forceValue(args[0]->list.elems[0]); - v = args[0]->list.elems[0]; + state.forceValue(*args[0]->list.elems[0]); + v = *args[0]->list.elems[0]; } @@ -840,11 +843,13 @@ static void prim_map(EvalState & state, Value * * args, Value & v) state.forceList(*args[1]); state.mkList(v, args[1]->list.length); + Value * vs = state.allocValues(v.list.length); for (unsigned int n = 0; n < v.list.length; ++n) { - v.list.elems[n].type = tApp; - v.list.elems[n].app.left = args[0]; - v.list.elems[n].app.right = &args[1]->list.elems[n]; + v.list.elems[n] = &vs[n]; + vs[n].type = tApp; + vs[n].app.left = args[0]; + vs[n].app.right = args[1]->list.elems[n]; } } diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 0c8fc143c..58e89925c 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -97,7 +97,7 @@ static void printValueAsXML(EvalState & state, bool strict, Value & v, case tList: { XMLOpenElement _(doc, "list"); for (unsigned int n = 0; n < v.list.length; ++n) - printValueAsXML(state, strict, v.list.elems[n], doc, context, drvsSeen); + printValueAsXML(state, strict, *v.list.elems[n], doc, context, drvsSeen); break; } From 02c1dac90934e1b833c4d6bd9280bda27c146d80 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 16 Apr 2010 13:44:02 +0000 Subject: [PATCH 073/103] * In an nested `with' where the inner with is a variable (`with ...; with someVar; ...'), the contents of the variable would be clobbered. (The attributes in the outer `with' were added to the variable.) --- src/libexpr/eval.cc | 8 +++++++- src/libexpr/get-drvs.cc | 13 +++++++------ src/nix-env/nix-env.cc | 1 + 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 69e7bd8b3..ac475c893 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -649,6 +649,11 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) Env * env3 = &env; for (unsigned int l = prevWith; l; --l, env3 = env3->up) ; + /* Because the first `with' may be a shallow copy of another + attribute set (through a tCopy node), we need to clone its + `attrs' before modifying them. */ + env2.values[0].attrs = new Bindings(*env2.values[0].attrs); + 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()) @@ -1042,7 +1047,8 @@ void EvalState::printStats() printMsg(v, format(" expressions evaluated: %1%") % nrEvaluated); printMsg(v, format(" stack space used: %1% bytes") % (&x - deepestStack)); printMsg(v, format(" max eval() nesting depth: %1%") % maxRecursionDepth); - printMsg(v, format(" stack space per eval() level: %1% bytes") % ((&x - deepestStack) / (float) maxRecursionDepth)); + printMsg(v, format(" stack space per eval() level: %1% bytes") + % ((&x - deepestStack) / (float) maxRecursionDepth)); printMsg(v, format(" environments allocated: %1% (%2% bytes)") % nrEnvs % (nrEnvs * sizeof(Env))); printMsg(v, format(" values allocated in environments: %1% (%2% bytes)") diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 6964e3e3b..8af011d54 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -177,14 +177,15 @@ static void getDerivations(EvalState & state, Value & vIn, there are names clashes between derivations, the derivation bound to the attribute with the "lower" name should take precedence). */ - StringSet attrs; + typedef std::map SortedSymbols; + SortedSymbols attrs; foreach (Bindings::iterator, i, *v.attrs) - attrs.insert(i->first); + attrs.insert(std::pair(i->first, i->first)); - foreach (StringSet::iterator, i, attrs) { - startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % *i); - string pathPrefix2 = addToPath(pathPrefix, *i); - Value & v2((*v.attrs)[state.symbols.create(*i)]); + foreach (SortedSymbols::iterator, i, attrs) { + startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first); + string pathPrefix2 = addToPath(pathPrefix, i->first); + Value & v2((*v.attrs)[i->second]); if (combineChannels) getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); else if (getDerivation(state, v2, pathPrefix2, drvs, done)) { diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index d44517ba6..f9f828fe9 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1089,6 +1089,7 @@ static void opQuery(Globals & globals, foreach (vector::iterator, i, elems2) { try { + startNest(nest, lvlDebug, format("outputting query result `%1%'") % i->attrPath); /* For table output. */ Strings columns; From 497e4ad12650e27ecbaf0e056fe0c54bc12a138b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 16 Apr 2010 13:51:01 +0000 Subject: [PATCH 074/103] * Remove some redundant tests. --- src/libexpr/eval.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ac475c893..89534688e 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1013,13 +1013,13 @@ bool EvalState::eqValues(Value & v1, Value & v2) return true; case tList: - if (v2.type != tList || v1.list.length != v2.list.length) return false; + if (v1.list.length != v2.list.length) return false; for (unsigned int n = 0; n < v1.list.length; ++n) if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false; return true; case tAttrs: { - if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false; + if (v1.attrs->size() != v2.attrs->size()) return false; Bindings::iterator i, j; for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) if (i->first != j->first || !eqValues(i->second, j->second)) return false; @@ -1055,7 +1055,7 @@ void EvalState::printStats() % nrValuesInEnvs % (nrValuesInEnvs * sizeof(Value))); printMsg(v, format(" list elements: %1% (%2% bytes)") % nrListElems % (nrListElems * sizeof(Value *))); - printMsg(v, format(" misc. values allocated: %1% (%2% bytes) ") + printMsg(v, format(" misc. values allocated: %1% (%2% bytes)") % nrValues % (nrValues * sizeof(Value))); printMsg(v, format(" symbols in symbol table: %1%") % symbols.size()); } From 8ca4a001cb9e8ca2556c26a1b559b0322a8fb46a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 16 Apr 2010 14:03:26 +0000 Subject: [PATCH 075/103] * Improve sharing a bit. --- src/libexpr/eval.cc | 11 +++++++---- src/libexpr/primops.cc | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 89534688e..ce466ded4 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -652,15 +652,18 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) /* Because the first `with' may be a shallow copy of another attribute set (through a tCopy node), we need to clone its `attrs' before modifying them. */ - env2.values[0].attrs = new Bindings(*env2.values[0].attrs); + Bindings * old(env2.values[0].attrs); + state.mkAttrs(env2.values[0]); + foreach (Bindings::iterator, i, *old) + mkCopy((*env2.values[0].attrs)[i->first], i->second); 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 + mkCopy((*env2.values[0].attrs)[i->first], i->second); } } - + state.eval(env2, body, v); } @@ -731,7 +734,7 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) state.forceAttrs(v2); foreach (Bindings::iterator, i, *v2.attrs) - (*v.attrs)[i->first] = i->second; // !!! sharing + mkCopy((*v.attrs)[i->first], i->second); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index ae17506ce..c4495e81d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -733,7 +733,7 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v) if (j == v2.attrs->end()) throw TypeError("`value' attribute missing in a call to `listToAttrs'"); - (*v.attrs)[state.symbols.create(name)] = j->second; // !!! sharing? + mkCopy((*v.attrs)[state.symbols.create(name)], j->second); } } From 8bb0210fea89f2df70b3c10c431b1383e74093df Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 16 Apr 2010 14:07:52 +0000 Subject: [PATCH 076/103] * _combineChannels shouldn't be an integer. --- src/nix-env/nix-env.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index f9f828fe9..306b9507d 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -153,7 +153,7 @@ static Expr * loadSourceExpr(EvalState & state, const Path & path) for a user to have a ~/.nix-defexpr directory that includes some system-wide directory). */ ExprAttrs * attrs = new ExprAttrs; - attrs->attrs[state.symbols.create("_combineChannels")] = new ExprInt(1); + attrs->attrs[state.symbols.create("_combineChannels")] = new ExprList(); getAllExprs(state, path, *attrs); return attrs; } From 5c31995bb8adb9189152ebd4f3c41ca9e8049749 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 16 Apr 2010 15:13:47 +0000 Subject: [PATCH 077/103] * Updated some more primops. --- src/libexpr/eval.cc | 23 ++++++++------ src/libexpr/eval.hh | 1 + src/libexpr/primops.cc | 71 ++++++++++++++---------------------------- 3 files changed, 37 insertions(+), 58 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ce466ded4..d259d58a3 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -367,6 +367,14 @@ bool EvalState::evalBool(Env & env, Expr * e) } +void EvalState::evalAttrs(Env & env, Expr * e, Value & v) +{ + eval(env, e, v); + if (v.type != tAttrs) + throwTypeError("value is %1% while an attribute set was expected", showType(v)); +} + + void Expr::eval(EvalState & state, Env & env, Value & v) { abort(); @@ -481,8 +489,7 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v) void ExprSelect::eval(EvalState & state, Env & env, Value & v) { Value v2; - state.eval(env, e, v2); - state.forceAttrs(v2); // !!! eval followed by force is slightly inefficient + state.evalAttrs(env, e, v2); Bindings::iterator i = v2.attrs->find(name); if (i == v2.attrs->end()) throwEvalError("attribute `%1%' missing", name); @@ -499,8 +506,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) { Value vAttrs; - state.eval(env, e, vAttrs); - state.forceAttrs(vAttrs); + state.evalAttrs(env, e, vAttrs); mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end()); } @@ -640,8 +646,7 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) Env & env2(state.allocEnv(1)); env2.up = &env; - state.eval(env, attrs, env2.values[0]); - state.forceAttrs(env2.values[0]); + state.evalAttrs(env, attrs, env2.values[0]); /* If there is an enclosing `with', copy all attributes that don't appear in this `with'. */ @@ -725,13 +730,11 @@ void ExprOpImpl::eval(EvalState & state, Env & env, Value & v) void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) { Value v2; - state.eval(env, e1, v2); - state.forceAttrs(v2); + state.evalAttrs(env, e1, v2); state.cloneAttrs(v2, v); - state.eval(env, e2, v2); - state.forceAttrs(v2); + state.evalAttrs(env, e2, v2); foreach (Bindings::iterator, i, *v2.attrs) mkCopy((*v.attrs)[i->first], i->second); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7252cae4b..6cdc171f5 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -188,6 +188,7 @@ public: /* Evaluation the expression, then verify that it has the expected type. */ bool evalBool(Env & env, Expr * e); + void evalAttrs(Env & env, Expr * e, Value & v); /* If `v' is a thunk, enter it and overwrite `v' with the result of the evaluation of the thunk. If `v' is a delayed function diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index c4495e81d..c5579679c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -462,12 +462,11 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) static void prim_toPath(EvalState & state, Value * * args, Value & v) { PathSet context; - string path = state.coerceToPath(*args[0], context); + Path path = state.coerceToPath(*args[0], context); mkString(v, canonPath(path), context); } -#if 0 /* Allow a valid store path to be used in an expression. This is useful in some generated expressions such as in nix-push, which generates a call to a function with an already existing store path @@ -479,16 +478,15 @@ static void prim_toPath(EvalState & state, Value * * args, Value & v) static void prim_storePath(EvalState & state, Value * * args, Value & v) { PathSet context; - Path path = canonPath(coerceToPath(state, args[0], context)); + Path path = canonPath(state.coerceToPath(*args[0], context)); if (!isInStore(path)) throw EvalError(format("path `%1%' is not in the Nix store") % path); Path path2 = toStorePath(path); if (!store->isValidPath(path2)) throw EvalError(format("store path `%1%' is not valid") % path2); context.insert(path2); - return makeStr(path, context); + mkString(v, path, context); } -#endif static void prim_pathExists(EvalState & state, Value * * args, Value & v) @@ -738,35 +736,20 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v) } -#if 0 /* Return the right-biased intersection of two attribute sets as1 and as2, i.e. a set that contains every attribute from as2 that is also a member of as1. */ static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v) { - ATermMap as1, as2; - queryAllAttrs(evalExpr(state, args[0]), as1, true); - queryAllAttrs(evalExpr(state, args[1]), as2, true); - - ATermMap res; - foreach (ATermMap::const_iterator, i, as2) - if (as1[i->key]) res.set(i->key, i->value); - - return makeAttrs(res); -} - - -static void attrsInPattern(ATermMap & map, Pattern pat) -{ - ATerm name; - ATermList formals; - ATermBool ellipsis; - if (matchAttrsPat(pat, formals, ellipsis, name)) { - for (ATermIterator i(formals); i; ++i) { - ATerm def; - if (!matchFormal(*i, name, def)) abort(); - map.set(name, makeAttrRHS(makeBool(def != constNoDefaultValue), makeNoPos())); - } + state.forceAttrs(*args[0]); + state.forceAttrs(*args[1]); + + state.mkAttrs(v); + + foreach (Bindings::iterator, i, *args[1]->attrs) { + Bindings::iterator j = args[0]->attrs->find(i->first); + if (j != args[0]->attrs->end()) + mkCopy((*v.attrs)[i->first], i->second); } } @@ -786,17 +769,17 @@ static void attrsInPattern(ATermMap & map, Pattern pat) */ static void prim_functionArgs(EvalState & state, Value * * args, Value & v) { - Expr f = evalExpr(state, args[0]); - ATerm pat, body, pos; - if (!matchFunction(f, pat, body, pos)) - throw TypeError("`functionArgs' required a function"); - - ATermMap as; - attrsInPattern(as, pat); + state.forceValue(*args[0]); + if (args[0]->type != tLambda) + throw TypeError("`functionArgs' requires a function"); - return makeAttrs(as); + state.mkAttrs(v); + + if (!args[0]->lambda.fun->matchAttrs) return; + + foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals) + mkBool((*v.attrs)[i->name], i->def); } -#endif /************************************************************* @@ -948,7 +931,6 @@ static void prim_unsafeDiscardStringContext(EvalState & state, Value * * args, V } -#if 0 /* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a builder without causing the derivation to be built (for instance, in the derivation that builds NARs in nix-push, when doing @@ -958,7 +940,7 @@ static void prim_unsafeDiscardStringContext(EvalState & state, Value * * args, V static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args, Value & v) { PathSet context; - string s = coerceToString(state, args[0], context); + string s = state.coerceToString(*args[0], context); PathSet context2; foreach (PathSet::iterator, i, context) { @@ -967,9 +949,8 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args context2.insert(p); } - return makeStr(s, context2); + mkString(v, s, context2); } -#endif /************************************************************* @@ -1056,9 +1037,7 @@ void EvalState::createBaseEnv() // Paths addPrimOp("__toPath", 1, prim_toPath); -#if 0 addPrimOp("__storePath", 1, prim_storePath); -#endif addPrimOp("__pathExists", 1, prim_pathExists); addPrimOp("baseNameOf", 1, prim_baseNameOf); addPrimOp("dirOf", 1, prim_dirOf); @@ -1076,10 +1055,8 @@ void EvalState::createBaseEnv() addPrimOp("__isAttrs", 1, prim_isAttrs); addPrimOp("removeAttrs", 2, prim_removeAttrs); addPrimOp("__listToAttrs", 1, prim_listToAttrs); -#if 0 addPrimOp("__intersectAttrs", 2, prim_intersectAttrs); addPrimOp("__functionArgs", 1, prim_functionArgs); -#endif // Lists addPrimOp("__isList", 1, prim_isList); @@ -1100,9 +1077,7 @@ void EvalState::createBaseEnv() addPrimOp("__substring", 3, prim_substring); addPrimOp("__stringLength", 1, prim_stringLength); addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); -#if 0 addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); -#endif // Versions addPrimOp("__parseDrvName", 1, prim_parseDrvName); From b7ff69eb7c3f97c33ec18c51ab87b7f3dd967052 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 19 Apr 2010 10:47:56 +0000 Subject: [PATCH 078/103] * Refactoring: move the user environment stuff into its own module. --- src/nix-env/Makefile.am | 3 ++- src/nix-env/nix-env.cc | 20 +------------------- src/nix-env/user-env.cc | 28 ++++++++++++++++++++++++++++ src/nix-env/user-env.hh | 16 ++++++++++++++++ 4 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 src/nix-env/user-env.cc create mode 100644 src/nix-env/user-env.hh diff --git a/src/nix-env/Makefile.am b/src/nix-env/Makefile.am index 900524f76..9b0d99513 100644 --- a/src/nix-env/Makefile.am +++ b/src/nix-env/Makefile.am @@ -1,6 +1,7 @@ bin_PROGRAMS = nix-env -nix_env_SOURCES = nix-env.cc profiles.cc profiles.hh help.txt +nix_env_SOURCES = nix-env.cc profiles.cc user-env.cc profiles.hh help.txt + nix_env_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \ ../libstore/libstore.la ../libutil/libutil.la \ ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@ diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 306b9507d..e298c4003 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -12,6 +12,7 @@ #include "common-opts.hh" #include "xml-writer.hh" #include "store-api.hh" +#include "user-env.hh" #include "util.hh" #include @@ -192,25 +193,6 @@ static Path getDefNixExprPath() } -static DrvInfos queryInstalled(EvalState & state, const Path & userEnv) -{ - Path path = userEnv + "/manifest"; - - if (!pathExists(path)) - return DrvInfos(); /* not an error, assume nothing installed */ - - throw Error("not implemented"); -#if 0 - Expr e = ATreadFromNamedFile(path.c_str()); - if (!e) throw Error(format("cannot read Nix expression from `%1%'") % path); - - DrvInfos elems; - // !!! getDerivations(state, e, "", ATermMap(1), elems); - return elems; -#endif -} - - /* Ensure exclusive access to a profile. Any command that modifies the profile first acquires this lock. */ static void lockProfile(PathLocks & lock, const Path & profile) diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc new file mode 100644 index 000000000..0e1fce650 --- /dev/null +++ b/src/nix-env/user-env.cc @@ -0,0 +1,28 @@ +#include "util.hh" +#include "get-drvs.hh" + + +namespace nix { + + +DrvInfos queryInstalled(EvalState & state, const Path & userEnv) +{ + Path path = userEnv + "/manifest"; + + if (!pathExists(path)) + return DrvInfos(); /* not an error, assume nothing installed */ + + throw Error("not implemented"); +#if 0 + Expr e = ATreadFromNamedFile(path.c_str()); + if (!e) throw Error(format("cannot read Nix expression from `%1%'") % path); + + DrvInfos elems; + // !!! getDerivations(state, e, "", ATermMap(1), elems); + return elems; +#endif +} + + +} + diff --git a/src/nix-env/user-env.hh b/src/nix-env/user-env.hh new file mode 100644 index 000000000..6675014f1 --- /dev/null +++ b/src/nix-env/user-env.hh @@ -0,0 +1,16 @@ +#ifndef __USER_ENV_H +#define __USER_ENV_H + +#include "get-drvs.hh" + +namespace nix { + +DrvInfos queryInstalled(EvalState & state, const Path & userEnv); + +} + +#endif /* !__USER_ENV_H */ + + + + From 55b5ddd3ca1ff4dfe4cfbfab92a4025d88ef6443 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 19 Apr 2010 12:10:04 +0000 Subject: [PATCH 079/103] * Added parsing of manifests in ATerm format. --- src/libexpr/get-drvs.cc | 16 ++-- src/libexpr/get-drvs.hh | 5 ++ src/nix-env/user-env.cc | 157 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 165 insertions(+), 13 deletions(-) diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 8af011d54..e9f1063d9 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -7,7 +7,7 @@ namespace nix { string DrvInfo::queryDrvPath(EvalState & state) const { - if (drvPath == "") { + if (drvPath == "" && attrs) { Bindings::iterator i = attrs->find(state.sDrvPath); PathSet context; (string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; @@ -18,7 +18,7 @@ string DrvInfo::queryDrvPath(EvalState & state) const string DrvInfo::queryOutPath(EvalState & state) const { - if (outPath == "") { + if (outPath == "" && attrs) { Bindings::iterator i = attrs->find(state.sOutPath); PathSet context; (string &) outPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; @@ -29,7 +29,9 @@ string DrvInfo::queryOutPath(EvalState & state) const MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const { - MetaInfo meta; + if (metaInfoRead) return meta; + + (bool &) metaInfoRead = true; Bindings::iterator a = attrs->find(state.sMeta); if (a == attrs->end()) return meta; /* fine, empty meta information */ @@ -50,7 +52,7 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const for (unsigned int j = 0; j < i->second.list.length; ++j) value.stringValues.push_back(state.forceStringNoCtx(*i->second.list.elems[j])); } else continue; - meta[i->first] = value; + ((MetaInfo &) meta)[i->first] = value; } return meta; @@ -66,9 +68,11 @@ MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const void DrvInfo::setMetaInfo(const MetaInfo & meta) { - throw Error("not implemented"); + metaInfoRead = true; + this->meta = meta; + #if 0 - ATermMap metaAttrs; + Value * metaAttrs = state.allocValues(1); foreach (MetaInfo::const_iterator, i, meta) { Expr e; switch (i->second.type) { diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index f7d1987ea..6f3c381f8 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -29,6 +29,9 @@ struct DrvInfo private: string drvPath; string outPath; + + bool metaInfoRead; + MetaInfo meta; public: string name; @@ -38,6 +41,8 @@ public: /* !!! make this private */ Bindings * attrs; + DrvInfo() : metaInfoRead(false) { }; + string queryDrvPath(EvalState & state) const; string queryOutPath(EvalState & state) const; MetaInfo queryMetaInfo(EvalState & state) const; diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 0e1fce650..8bb760094 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -5,22 +5,165 @@ namespace nix { +static void readLegacyManifest(const Path & path, DrvInfos & elems); + + DrvInfos queryInstalled(EvalState & state, const Path & userEnv) { + DrvInfos elems; + Path path = userEnv + "/manifest"; if (!pathExists(path)) return DrvInfos(); /* not an error, assume nothing installed */ - throw Error("not implemented"); -#if 0 - Expr e = ATreadFromNamedFile(path.c_str()); - if (!e) throw Error(format("cannot read Nix expression from `%1%'") % path); + readLegacyManifest(path, elems); - DrvInfos elems; - // !!! getDerivations(state, e, "", ATermMap(1), elems); return elems; -#endif +} + + +/* Code for parsing manifests in the old textual ATerm format. */ + +static void expect(std::istream & str, const string & s) +{ + char s2[s.size()]; + str.read(s2, s.size()); + if (string(s2, s.size()) != s) + throw Error(format("expected string `%1%'") % s); +} + + +static string parseString(std::istream & str) +{ + string res; + expect(str, "\""); + int c; + while ((c = str.get()) != '"') + if (c == '\\') { + c = str.get(); + if (c == 'n') res += '\n'; + else if (c == 'r') res += '\r'; + else if (c == 't') res += '\t'; + else res += c; + } + else res += c; + return res; +} + + +static string parseStr(std::istream & str) +{ + expect(str, "Str("); + string s = parseString(str); + expect(str, ",[])"); + return s; +} + + +static string parseWord(std::istream & str) +{ + string res; + while (isalpha(str.peek())) + res += str.get(); + return res; +} + + +static bool endOfList(std::istream & str) +{ + if (str.peek() == ',') { + str.get(); + return false; + } + if (str.peek() == ']') { + str.get(); + return true; + } + return false; +} + + +static MetaInfo parseMeta(std::istream & str) +{ + MetaInfo meta; + + expect(str, "Attrs(["); + while (!endOfList(str)) { + expect(str, "Bind("); + + MetaValue value; + + string name = parseString(str); + expect(str, ","); + + string type = parseWord(str); + + if (type == "Str") { + expect(str, "("); + value.type = MetaValue::tpString; + value.stringValue = parseString(str); + expect(str, ",[])"); + } + + else if (type == "List") { + expect(str, "(["); + value.type = MetaValue::tpStrings; + while (!endOfList(str)) + value.stringValues.push_back(parseStr(str)); + expect(str, ")"); + } + + else throw Error(format("unexpected token `%1%'") % type); + + expect(str, ",NoPos)"); + meta[name] = value; + } + + expect(str, ")"); + + return meta; +} + + +static void readLegacyManifest(const Path & path, DrvInfos & elems) +{ + string manifest = readFile(path); + std::istringstream str(manifest); + expect(str, "List(["); + + unsigned int n = 0; + + while (!endOfList(str)) { + DrvInfo elem; + expect(str, "Attrs(["); + + while (!endOfList(str)) { + expect(str, "Bind("); + string name = parseString(str); + expect(str, ","); + + if (name == "meta") elem.setMetaInfo(parseMeta(str)); + else { + string value = parseStr(str); + if (name == "name") elem.name = value; + else if (name == "outPath") elem.setOutPath(value); + else if (name == "drvPath") elem.setDrvPath(value); + else if (name == "system") elem.system = value; + } + + expect(str, ",NoPos)"); + } + + expect(str, ")"); + + if (elem.name != "") { + elem.attrPath = int2String(n++); + elems.push_back(elem); + } + } + + expect(str, ")"); } From efc7a579e880ec15ebe9afc0d8766c85c7d53ec2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 19 Apr 2010 13:46:58 +0000 Subject: [PATCH 080/103] * Don't use the ATerm library for parsing/printing .drv files. --- src/libexpr/primops.cc | 2 +- src/libmain/shared.cc | 10 -- src/libstore/Makefile.am | 7 - src/libstore/derivations-ast.def | 10 -- src/libstore/derivations.cc | 223 ++++++++++++++++--------------- src/libstore/derivations.hh | 11 +- src/libstore/local-store.cc | 2 - src/libstore/misc.cc | 6 +- src/libutil/Makefile.am | 6 +- src/libutil/aterm.cc | 55 -------- src/libutil/aterm.hh | 55 -------- src/libutil/util.cc | 41 ++++++ src/libutil/util.hh | 35 ++--- src/nix-env/user-env.cc | 41 ------ 14 files changed, 174 insertions(+), 330 deletions(-) delete mode 100644 src/libstore/derivations-ast.def delete mode 100644 src/libutil/aterm.cc delete mode 100644 src/libutil/aterm.hh diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index c5579679c..b28201593 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -262,7 +262,7 @@ static Hash hashDerivationModulo(EvalState & state, Derivation drv) } drv.inputDrvs = inputs2; - return hashTerm(unparseDerivation(drv)); + return hashString(htSHA256, unparseDerivation(drv)); } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index d9cf9a862..825e87621 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -87,9 +87,6 @@ static void setLogType(string lt) } -void initDerivationsHelpers(); - - static void closeStore() { try { @@ -176,9 +173,6 @@ static void initAndRun(int argc, char * * argv) string lt = getEnv("NIX_LOG_TYPE"); if (lt != "") setLogType(lt); - /* ATerm stuff. !!! find a better place to put this */ - initDerivationsHelpers(); - /* Put the arguments in a vector. */ Strings args, remaining; while (argc--) args.push_back(*argv++); @@ -333,10 +327,6 @@ int main(int argc, char * * argv) if (argc == 0) abort(); setuidInit(); - /* ATerm setup. */ - ATerm bottomOfStack; - ATinit(argc, argv, &bottomOfStack); - /* Turn on buffering for cerr. */ #if HAVE_PUBSETBUF std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf)); diff --git a/src/libstore/Makefile.am b/src/libstore/Makefile.am index 863871519..47681242f 100644 --- a/src/libstore/Makefile.am +++ b/src/libstore/Makefile.am @@ -12,12 +12,5 @@ pkginclude_HEADERS = \ libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@ -BUILT_SOURCES = derivations-ast.cc derivations-ast.hh - -EXTRA_DIST = derivations-ast.def derivations-ast.cc - AM_CXXFLAGS = -Wall \ -I$(srcdir)/.. ${aterm_include} -I$(srcdir)/../libutil - -derivations-ast.cc derivations-ast.hh: ../aterm-helper.pl derivations-ast.def - $(perl) $(srcdir)/../aterm-helper.pl derivations-ast.hh derivations-ast.cc < $(srcdir)/derivations-ast.def diff --git a/src/libstore/derivations-ast.def b/src/libstore/derivations-ast.def deleted file mode 100644 index 574529ae7..000000000 --- a/src/libstore/derivations-ast.def +++ /dev/null @@ -1,10 +0,0 @@ -init initDerivationsHelpers - -Derive | ATermList ATermList ATermList string string ATermList ATermList | ATerm | - -| string string | ATerm | EnvBinding | -| string ATermList | ATerm | DerivationInput | -| string string string string | ATerm | DerivationOutput | - -Closure | ATermList ATermList | ATerm | OldClosure | -| string ATermList | ATerm | OldClosureElem | diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index bc2ec1f90..3d0f1eb42 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -1,22 +1,12 @@ #include "derivations.hh" #include "store-api.hh" -#include "aterm.hh" #include "globals.hh" #include "util.hh" -#include "derivations-ast.hh" -#include "derivations-ast.cc" - namespace nix { -Hash hashTerm(ATerm t) -{ - return hashString(htSHA256, atPrint(t)); -} - - Path writeDerivation(const Derivation & drv, const string & name) { PathSet references; @@ -27,137 +17,150 @@ Path writeDerivation(const Derivation & drv, const string & name) (that can be missing (of course) and should not necessarily be held during a garbage collection). */ string suffix = name + drvExtension; - string contents = atPrint(unparseDerivation(drv)); + string contents = unparseDerivation(drv); return readOnlyMode ? computeStorePathForText(suffix, contents, references) : store->addTextToStore(suffix, contents, references); } -static void checkPath(const string & s) +static Path parsePath(std::istream & str) { + string s = parseString(str); if (s.size() == 0 || s[0] != '/') throw Error(format("bad path `%1%' in derivation") % s); + return s; } -static void parseStrings(ATermList paths, StringSet & out, bool arePaths) +static StringSet parseStrings(std::istream & str, bool arePaths) { - for (ATermIterator i(paths); i; ++i) { - if (ATgetType(*i) != AT_APPL) - throw badTerm("not a path", *i); - string s = aterm2String(*i); - if (arePaths) checkPath(s); - out.insert(s); - } + StringSet res; + while (!endOfList(str)) + res.insert(arePaths ? parsePath(str) : parseString(str)); + return res; } + - -/* Shut up warnings. */ -void throwBadDrv(ATerm t) __attribute__ ((noreturn)); - -void throwBadDrv(ATerm t) -{ - throw badTerm("not a valid derivation", t); -} - - -Derivation parseDerivation(ATerm t) +Derivation parseDerivation(const string & s) { Derivation drv; - ATermList outs, inDrvs, inSrcs, args, bnds; - ATerm builder, platform; + std::istringstream str(s); + expect(str, "Derive(["); - if (!matchDerive(t, outs, inDrvs, inSrcs, platform, builder, args, bnds)) - throwBadDrv(t); - - for (ATermIterator i(outs); i; ++i) { - ATerm id, path, hashAlgo, hash; - if (!matchDerivationOutput(*i, id, path, hashAlgo, hash)) - throwBadDrv(t); + /* Parse the list of outputs. */ + while (!endOfList(str)) { DerivationOutput out; - out.path = aterm2String(path); - checkPath(out.path); - out.hashAlgo = aterm2String(hashAlgo); - out.hash = aterm2String(hash); - drv.outputs[aterm2String(id)] = out; + expect(str, "("); string id = parseString(str); + expect(str, ","); out.path = parsePath(str); + expect(str, ","); out.hashAlgo = parseString(str); + expect(str, ","); out.hash = parseString(str); + expect(str, ")"); + drv.outputs[id] = out; } - for (ATermIterator i(inDrvs); i; ++i) { - ATerm drvPath; - ATermList ids; - if (!matchDerivationInput(*i, drvPath, ids)) - throwBadDrv(t); - Path drvPath2 = aterm2String(drvPath); - checkPath(drvPath2); - StringSet ids2; - parseStrings(ids, ids2, false); - drv.inputDrvs[drvPath2] = ids2; + /* Parse the list of input derivations. */ + expect(str, ",["); + while (!endOfList(str)) { + expect(str, "("); + Path drvPath = parsePath(str); + expect(str, ",["); + drv.inputDrvs[drvPath] = parseStrings(str, false); + expect(str, ")"); + } + + expect(str, ",["); drv.inputSrcs = parseStrings(str, true); + expect(str, ","); drv.platform = parseString(str); + expect(str, ","); drv.builder = parseString(str); + + /* Parse the builder arguments. */ + expect(str, ",["); + while (!endOfList(str)) + drv.args.push_back(parseString(str)); + + /* Parse the environment variables. */ + expect(str, ",["); + while (!endOfList(str)) { + expect(str, "("); string name = parseString(str); + expect(str, ","); string value = parseString(str); + expect(str, ")"); + drv.env[name] = value; } - parseStrings(inSrcs, drv.inputSrcs, true); - - drv.builder = aterm2String(builder); - drv.platform = aterm2String(platform); - - for (ATermIterator i(args); i; ++i) { - if (ATgetType(*i) != AT_APPL) - throw badTerm("string expected", *i); - drv.args.push_back(aterm2String(*i)); - } - - for (ATermIterator i(bnds); i; ++i) { - ATerm s1, s2; - if (!matchEnvBinding(*i, s1, s2)) - throw badTerm("tuple of strings expected", *i); - drv.env[aterm2String(s1)] = aterm2String(s2); - } - + expect(str, ")"); return drv; } -ATerm unparseDerivation(const Derivation & drv) +void printString(std::ostream & str, const string & s) { - ATermList outputs = ATempty; - for (DerivationOutputs::const_reverse_iterator i = drv.outputs.rbegin(); - i != drv.outputs.rend(); ++i) - outputs = ATinsert(outputs, - makeDerivationOutput( - toATerm(i->first), - toATerm(i->second.path), - toATerm(i->second.hashAlgo), - toATerm(i->second.hash))); + str << "\""; + for (const char * i = s.c_str(); *i; i++) + if (*i == '\"' || *i == '\\') str << "\\" << *i; + else if (*i == '\n') str << "\\n"; + else if (*i == '\r') str << "\\r"; + else if (*i == '\t') str << "\\t"; + else str << *i; + str << "\""; +} - ATermList inDrvs = ATempty; - for (DerivationInputs::const_reverse_iterator i = drv.inputDrvs.rbegin(); - i != drv.inputDrvs.rend(); ++i) - inDrvs = ATinsert(inDrvs, - makeDerivationInput( - toATerm(i->first), - toATermList(i->second))); + +template +void printStrings(std::ostream & str, ForwardIterator i, ForwardIterator j) +{ + str << "["; + bool first = true; + for ( ; i != j; ++i) { + if (first) first = false; else str << ","; + printString(str, *i); + } + str << "]"; +} + + +string unparseDerivation(const Derivation & drv) +{ + std::ostringstream str; + str << "Derive(["; + + bool first = true; + foreach (DerivationOutputs::const_iterator, i, drv.outputs) { + if (first) first = false; else str << ","; + str << "("; printString(str, i->first); + str << ","; printString(str, i->second.path); + str << ","; printString(str, i->second.hashAlgo); + str << ","; printString(str, i->second.hash); + str << ")"; + } + + str << "],["; + first = true; + foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) { + if (first) first = false; else str << ","; + str << "("; printString(str, i->first); + str << ","; printStrings(str, i->second.begin(), i->second.end()); + str << ")"; + } + + str << "],"; + printStrings(str, drv.inputSrcs.begin(), drv.inputSrcs.end()); - ATermList args = ATempty; - for (Strings::const_reverse_iterator i = drv.args.rbegin(); - i != drv.args.rend(); ++i) - args = ATinsert(args, toATerm(*i)); + str << ","; printString(str, drv.platform); + str << ","; printString(str, drv.builder); + str << ","; printStrings(str, drv.args.begin(), drv.args.end()); - ATermList env = ATempty; - for (StringPairs::const_reverse_iterator i = drv.env.rbegin(); - i != drv.env.rend(); ++i) - env = ATinsert(env, - makeEnvBinding( - toATerm(i->first), - toATerm(i->second))); - - return makeDerive( - outputs, - inDrvs, - toATermList(drv.inputSrcs), - toATerm(drv.platform), - toATerm(drv.builder), - args, - env); + str << ",["; + first = true; + foreach (StringPairs::const_iterator, i, drv.env) { + if (first) first = false; else str << ","; + str << "("; printString(str, i->first); + str << ","; printString(str, i->second); + str << ")"; + } + + str << "])"; + + return str.str(); } diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index 042f4738d..95e49d42c 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -1,8 +1,6 @@ #ifndef __DERIVATIONS_H #define __DERIVATIONS_H -#include - #include "hash.hh" #include @@ -53,17 +51,14 @@ struct Derivation }; -/* Hash an aterm. */ -Hash hashTerm(ATerm t); - /* Write a derivation to the Nix store, and return its path. */ Path writeDerivation(const Derivation & drv, const string & name); /* Parse a derivation. */ -Derivation parseDerivation(ATerm t); +Derivation parseDerivation(const string & s); -/* Parse a derivation. */ -ATerm unparseDerivation(const Derivation & drv); +/* Print a derivation. */ +string unparseDerivation(const Derivation & drv); /* Check whether a file name ends with the extensions for derivations. */ diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index a83ba55e2..2c0aa3579 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -3,8 +3,6 @@ #include "globals.hh" #include "archive.hh" #include "pathlocks.hh" -#include "aterm.hh" -#include "derivations-ast.hh" #include "worker-protocol.hh" #include diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 2d7d13a0e..f2cc20626 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -2,8 +2,6 @@ #include "store-api.hh" #include "local-store.hh" -#include - namespace nix { @@ -12,9 +10,7 @@ Derivation derivationFromPath(const Path & drvPath) { assertStorePath(drvPath); store->ensurePath(drvPath); - ATerm t = ATreadFromNamedFile(drvPath.c_str()); - if (!t) throw Error(format("cannot read aterm from `%1%'") % drvPath); - return parseDerivation(t); + return parseDerivation(readFile(drvPath)); } diff --git a/src/libutil/Makefile.am b/src/libutil/Makefile.am index 55135e373..aa862208c 100644 --- a/src/libutil/Makefile.am +++ b/src/libutil/Makefile.am @@ -1,16 +1,16 @@ pkglib_LTLIBRARIES = libutil.la libutil_la_SOURCES = util.cc hash.cc serialise.cc \ - archive.cc aterm.cc xml-writer.cc + archive.cc xml-writer.cc libutil_la_LIBADD = ../boost/format/libformat.la pkginclude_HEADERS = util.hh hash.hh serialise.hh \ - archive.hh aterm.hh xml-writer.hh types.hh + archive.hh xml-writer.hh types.hh if !HAVE_OPENSSL libutil_la_SOURCES += \ md5.c md5.h sha1.c sha1.h sha256.c sha256.h md32_common.h endif -AM_CXXFLAGS = -Wall -I$(srcdir)/.. ${aterm_include} +AM_CXXFLAGS = -Wall -I$(srcdir)/.. diff --git a/src/libutil/aterm.cc b/src/libutil/aterm.cc deleted file mode 100644 index 25d704785..000000000 --- a/src/libutil/aterm.cc +++ /dev/null @@ -1,55 +0,0 @@ -#include "aterm.hh" - -#include - -using std::string; - - -string nix::atPrint(ATerm t) -{ - if (!t) throw Error("attempt to print null aterm"); - char * s = ATwriteToString(t); - if (!s) throw Error("cannot print term"); - return s; -} - - -std::ostream & operator << (std::ostream & stream, ATerm e) -{ - return stream << nix::atPrint(e); -} - - -nix::Error nix::badTerm(const format & f, ATerm t) -{ - char * s = ATwriteToString(t); - if (!s) throw Error("cannot print term"); - if (strlen(s) > 1000) { - int len; - s = ATwriteToSharedString(t, &len); - if (!s) throw Error("cannot print term"); - } - return Error(format("%1%, in `%2%'") % f.str() % (string) s); -} - - -ATerm nix::toATerm(const char * s) -{ - return (ATerm) ATmakeAppl0(ATmakeAFun((char *) s, 0, ATtrue)); -} - - -ATerm nix::toATerm(const string & s) -{ - return toATerm(s.c_str()); -} - - -ATermList nix::toATermList(const StringSet & ss) -{ - ATermList l = ATempty; - for (StringSet::const_reverse_iterator i = ss.rbegin(); - i != ss.rend(); ++i) - l = ATinsert(l, toATerm(*i)); - return l; -} diff --git a/src/libutil/aterm.hh b/src/libutil/aterm.hh deleted file mode 100644 index b1cbc3b6d..000000000 --- a/src/libutil/aterm.hh +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef __ATERM_H -#define __ATERM_H - -#include - -#include "types.hh" - - -namespace nix { - - -/* Print an ATerm. */ -string atPrint(ATerm t); - -class ATermIterator -{ - ATermList t; - -public: - ATermIterator(ATermList _t) : t(_t) { } - ATermIterator & operator ++ () - { - t = ATgetNext(t); - return *this; - } - ATerm operator * () - { - return ATgetFirst(t); - } - operator bool () - { - return t != ATempty; - } -}; - - -/* Throw an exception with an error message containing the given - aterm. */ -Error badTerm(const format & f, ATerm t); - - -/* Convert strings to ATerms. */ -ATerm toATerm(const char * s); -ATerm toATerm(const string & s); - -ATermList toATermList(const StringSet & ss); - -} - - -/* Write an ATerm to an output stream. */ -std::ostream & operator << (std::ostream & stream, ATerm e); - - -#endif /* !__ATERM_H */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index d28d0e823..98912e7a0 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1006,6 +1006,47 @@ bool hasSuffix(const string & s, const string & suffix) } +void expect(std::istream & str, const string & s) +{ + char s2[s.size()]; + str.read(s2, s.size()); + if (string(s2, s.size()) != s) + throw Error(format("expected string `%1%'") % s); +} + + +string parseString(std::istream & str) +{ + string res; + expect(str, "\""); + int c; + while ((c = str.get()) != '"') + if (c == '\\') { + c = str.get(); + if (c == 'n') res += '\n'; + else if (c == 'r') res += '\r'; + else if (c == 't') res += '\t'; + else res += c; + } + else res += c; + return res; +} + + +bool endOfList(std::istream & str) +{ + if (str.peek() == ',') { + str.get(); + return false; + } + if (str.peek() == ']') { + str.get(); + return true; + } + return false; +} + + void ignoreException() { try { diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 4de33d3ef..ff710077c 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -302,34 +302,23 @@ string int2String(int n); bool hasSuffix(const string & s, const string & suffix); +/* Read string `s' from stream `str'. */ +void expect(std::istream & str, const string & s); + + +/* Read a C-style string from stream `str'. */ +string parseString(std::istream & str); + + +/* Utility function used to parse legacy ATerms. */ +bool endOfList(std::istream & str); + + /* Exception handling in destructors: print an error message, then ignore the exception. */ void ignoreException(); -/* STL functions such as sort() pass a binary function object around - by value, so it gets cloned a lot. This is bad if the function - object has state or is simply large. This adapter wraps the - function object to simulate passing by reference. */ -template -struct binary_function_ref_adapter -{ - F * p; - - binary_function_ref_adapter(F * _p) - { - p = _p; - } - - typename F::result_type operator () ( - const typename F::first_argument_type & x, - const typename F::second_argument_type & y) - { - return (*p)(x, y); - } -}; - - } diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 8bb760094..f040f8c11 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -25,33 +25,6 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv) /* Code for parsing manifests in the old textual ATerm format. */ -static void expect(std::istream & str, const string & s) -{ - char s2[s.size()]; - str.read(s2, s.size()); - if (string(s2, s.size()) != s) - throw Error(format("expected string `%1%'") % s); -} - - -static string parseString(std::istream & str) -{ - string res; - expect(str, "\""); - int c; - while ((c = str.get()) != '"') - if (c == '\\') { - c = str.get(); - if (c == 'n') res += '\n'; - else if (c == 'r') res += '\r'; - else if (c == 't') res += '\t'; - else res += c; - } - else res += c; - return res; -} - - static string parseStr(std::istream & str) { expect(str, "Str("); @@ -70,20 +43,6 @@ static string parseWord(std::istream & str) } -static bool endOfList(std::istream & str) -{ - if (str.peek() == ',') { - str.get(); - return false; - } - if (str.peek() == ']') { - str.get(); - return true; - } - return false; -} - - static MetaInfo parseMeta(std::istream & str) { MetaInfo meta; From f3b8833a48472c3545ea8673d687ea9cadcedd61 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 19 Apr 2010 14:51:58 +0000 Subject: [PATCH 081/103] * Drop the dependency on the ATerm library. --- configure.ac | 17 --- doc/manual/installation.xml | 14 +-- externals/Makefile.am | 43 +------ externals/max-long.patch | 77 ------------- externals/sizeof.patch | 56 ---------- release.nix | 11 +- src/Makefile.am | 2 - src/aterm-helper.pl | 179 ------------------------------ src/libexpr/Makefile.am | 6 +- src/libmain/Makefile.am | 2 +- src/libmain/shared.cc | 2 - src/libstore/Makefile.am | 2 +- src/nix-env/Makefile.am | 4 +- src/nix-hash/Makefile.am | 2 +- src/nix-instantiate/Makefile.am | 3 +- src/nix-setuid-helper/Makefile.am | 4 +- src/nix-store/Makefile.am | 4 +- src/nix-worker/Makefile.am | 4 +- substitute.mk | 1 - tests/common.sh.in | 5 - tests/lang.sh | 7 +- 21 files changed, 32 insertions(+), 413 deletions(-) delete mode 100644 externals/max-long.patch delete mode 100644 externals/sizeof.patch delete mode 100755 src/aterm-helper.pl diff --git a/configure.ac b/configure.ac index 4c92bcb5c..8d3f308d7 100644 --- a/configure.ac +++ b/configure.ac @@ -203,23 +203,6 @@ AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH], storedir=$withval, storedir='/nix/store') AC_SUBST(storedir) -AC_ARG_WITH(aterm, AC_HELP_STRING([--with-aterm=PATH], - [prefix of CWI ATerm library]), - aterm=$withval, aterm=) -AM_CONDITIONAL(HAVE_ATERM, test -n "$aterm") -if test -z "$aterm"; then - aterm_lib='-L${top_builddir}/externals/inst-aterm/lib -lATerm' - aterm_include='-I${top_builddir}/externals/inst-aterm/include' - aterm_bin='${top_builddir}/externals/inst-aterm/bin' -else - aterm_lib="-L$aterm/lib -lATerm" - aterm_include="-I$aterm/include" - aterm_bin="$aterm/bin" -fi -AC_SUBST(aterm_lib) -AC_SUBST(aterm_include) -AC_SUBST(aterm_bin) - AC_ARG_WITH(openssl, AC_HELP_STRING([--with-openssl=PATH], [prefix of the OpenSSL library]), openssl=$withval, openssl=) diff --git a/doc/manual/installation.xml b/doc/manual/installation.xml index a1b99ca2e..bc5e21f0d 100644 --- a/doc/manual/installation.xml +++ b/doc/manual/installation.xml @@ -96,15 +96,13 @@ ubiquitous 2.5.4a won't. Note that these are only required if you modify the parser or when you are building from the Subversion repository. -Nix uses CWI's ATerm library and the bzip2 compressor (including -the bzip2 library). These are included in the Nix source -distribution. If you build from the Subversion repository, you must -download them yourself and place them in the -externals/ directory. See +Nix uses the bzip2 compressor (including the bzip2 library). It +is included in the Nix source distribution. If you build from the +Subversion repository, you must download it yourself and place it in +the externals/ directory. See externals/Makefile.am for the precise URLs of -these packages. Alternatively, if you already have them installed, -you can use configure's - and +this packages. Alternatively, if you already have it installed, you +can use configure's options to point to their respective locations. diff --git a/externals/Makefile.am b/externals/Makefile.am index c9bc05d71..838f5d704 100644 --- a/externals/Makefile.am +++ b/externals/Makefile.am @@ -1,38 +1,3 @@ -# CWI ATerm - -ATERM = aterm-2.5 - -$(ATERM).tar.gz: - @echo "Nix requires the CWI ATerm library to build." - @echo "Please download version 2.5 from" - @echo " http://nixos.org/tarballs/aterm-2.5.tar.gz" - @echo "and place it in the externals/ directory." - false - -$(ATERM): $(ATERM).tar.gz - gzip -d < $(srcdir)/$(ATERM).tar.gz | tar xvf - - patch -d $(ATERM) -p1 < ./max-long.patch - patch -d $(ATERM) -p1 < ./sizeof.patch - -have-aterm: - $(MAKE) $(ATERM) - touch have-aterm - -if HAVE_ATERM -build-aterm: -else -build-aterm: have-aterm - (pfx=`pwd` && \ - cd $(ATERM) && \ - CC="$(CC)" ./configure --prefix=$$pfx/inst-aterm \ - --disable-shared --enable-static && \ - $(MAKE) && \ - $(MAKE) check && \ - $(MAKE) install) - touch build-aterm -endif - - # bzip2 BZIP2 = bzip2-1.0.5 @@ -67,10 +32,10 @@ install: endif -all: build-aterm build-bzip2 +all: build-bzip2 -EXTRA_DIST = $(ATERM).tar.gz $(BZIP2).tar.gz max-long.patch sizeof.patch +EXTRA_DIST = $(BZIP2).tar.gz max-long.patch sizeof.patch ext-clean: - $(RM) -f have-aterm build-aterm have-bzip2 build-bzip2 - $(RM) -rf $(ATERM) $(BZIP2) + $(RM) -f have-bzip2 build-bzip2 + $(RM) -rf $(BZIP2) diff --git a/externals/max-long.patch b/externals/max-long.patch deleted file mode 100644 index a2f260b97..000000000 --- a/externals/max-long.patch +++ /dev/null @@ -1,77 +0,0 @@ -diff -rc aterm-2.8-orig/aterm/hash.c aterm-2.8/aterm/hash.c -*** aterm-2.8-orig/aterm/hash.c 2008-11-10 13:54:22.000000000 +0100 ---- aterm-2.8/aterm/hash.c 2009-01-27 18:14:14.000000000 +0100 -*************** -*** 93,146 **** - } - - /*}}} */ -- /*{{{ static long calc_long_max() */ -- static long calc_long_max() -- { -- long try_long_max; -- long long_max; -- long delta; -- -- try_long_max = 1; -- do { -- long_max = try_long_max; -- try_long_max = long_max * 2; -- } while (try_long_max > 0); -- -- delta = long_max; -- while (delta > 1) { -- while (long_max + delta < 0) { -- delta /= 2; -- } -- long_max += delta; -- } -- -- return long_max; -- -- } -- /*}}} */ - /*{{{ static long calculateNewSize(sizeMinus1, nrdel, nrentries) */ - - static long calculateNewSize - (long sizeMinus1, long nr_deletions, long nr_entries) - { -- -- /* Hack: LONG_MAX (limits.h) is often unreliable, we need to find -- * out the maximum possible value of a signed long dynamically. -- */ -- static long st_long_max = 0; -- -- /* the resulting length has the form 2^k-1 */ -- - if (nr_deletions >= nr_entries/2) { - return sizeMinus1; - } - -! if (st_long_max == 0) { -! st_long_max = calc_long_max(); -! } -! -! if (sizeMinus1 > st_long_max / 2) { -! return st_long_max-1; - } - - return (2*sizeMinus1)+1; ---- 93,109 ---- - } - - /*}}} */ - /*{{{ static long calculateNewSize(sizeMinus1, nrdel, nrentries) */ - - static long calculateNewSize - (long sizeMinus1, long nr_deletions, long nr_entries) - { - if (nr_deletions >= nr_entries/2) { - return sizeMinus1; - } - -! if (sizeMinus1 > LONG_MAX / 2) { -! return LONG_MAX-1; - } - - return (2*sizeMinus1)+1; diff --git a/externals/sizeof.patch b/externals/sizeof.patch deleted file mode 100644 index 2649cc564..000000000 --- a/externals/sizeof.patch +++ /dev/null @@ -1,56 +0,0 @@ -diff -rc -x '*~' aterm-2.5-orig/aterm/aterm.c aterm-2.5/aterm/aterm.c -*** aterm-2.5-orig/aterm/aterm.c 2007-02-27 23:41:31.000000000 +0100 ---- aterm-2.5/aterm/aterm.c 2010-02-23 15:10:38.000000000 +0100 -*************** -*** 150,155 **** ---- 150,157 ---- - if (initialized) - return; - -+ assert(sizeof(long) == sizeof(void *)); -+ - /*{{{ Handle arguments */ - - for (lcv=1; lcv < argc; lcv++) { -diff -rc -x '*~' aterm-2.5-orig/aterm/encoding.h aterm-2.5/aterm/encoding.h -*** aterm-2.5-orig/aterm/encoding.h 2007-02-27 23:41:31.000000000 +0100 ---- aterm-2.5/aterm/encoding.h 2010-02-23 15:36:05.000000000 +0100 -*************** -*** 10,24 **** - { - #endif/* __cplusplus */ - -! #if SIZEOF_LONG > 4 -! #define AT_64BIT - #endif - -! #if SIZEOF_LONG != SIZEOF_VOID_P -! #error Size of long is not the same as the size of a pointer - #endif - -! #if SIZEOF_INT > 4 - #error Size of int is not 32 bits - #endif - ---- 10,30 ---- - { - #endif/* __cplusplus */ - -! #include -! -! #ifndef SIZEOF_LONG -! #if ULONG_MAX > 4294967295 -! #define SIZEOF_LONG 8 -! #else -! #define SIZEOF_LONG 4 -! #endif - #endif - -! #if SIZEOF_LONG > 4 -! #define AT_64BIT - #endif - -! #if UINT_MAX > 4294967295 - #error Size of int is not 32 bits - #endif - diff --git a/release.nix b/release.nix index e9afe9c9e..3343f8658 100644 --- a/release.nix +++ b/release.nix @@ -29,11 +29,8 @@ let --with-xml-flags=--nonet ''; - # Include the ATerm and Bzip2 tarballs in the distribution. + # Include the Bzip2 tarball in the distribution. preConfigure = '' - stripHash ${aterm.src} - cp -pv ${aterm.src} externals/$strippedName - stripHash ${bzip2.src} cp -pv ${bzip2.src} externals/$strippedName @@ -77,7 +74,7 @@ let configureFlags = '' --disable-init-state - --with-aterm=${aterm} --with-bzip2=${bzip2} + --with-bzip2=${bzip2} ''; }; @@ -98,7 +95,7 @@ let configureFlags = '' --disable-init-state - --with-aterm=${aterm} --with-bzip2=${bzip2} + --with-bzip2=${bzip2} --enable-static-nix ''; }; @@ -123,7 +120,7 @@ let configureFlags = '' --disable-init-state --disable-shared - --with-aterm=${aterm} --with-bzip2=${bzip2} + --with-bzip2=${bzip2} ''; lcovFilter = ["*/boost/*" "*-tab.*"]; diff --git a/src/Makefile.am b/src/Makefile.am index 971f7d9d1..ec3632773 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,3 @@ SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \ libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \ nix-log2xml bsdiff-4.3 - -EXTRA_DIST = aterm-helper.pl diff --git a/src/aterm-helper.pl b/src/aterm-helper.pl deleted file mode 100755 index f1eb77ee8..000000000 --- a/src/aterm-helper.pl +++ /dev/null @@ -1,179 +0,0 @@ -#! /usr/bin/perl -w - -# This program generates C/C++ code for efficiently manipulating -# ATerms. It generates functions to build and match ATerms according -# to a set of constructor definitions defined in a file read from -# standard input. A constructor is defined by a line with the -# following format: -# -# SYM | ARGS | TYPE | FUN? -# -# where SYM is the name of the constructor, ARGS is a -# whitespace-separated list of argument types, TYPE is the type of the -# resulting ATerm (which should be `ATerm' or a type synonym for -# `ATerm'), and the optional FUN is used to construct the names of the -# build and match functions (it defaults to SYM; overriding it is -# useful if there are overloaded constructors, e.g., with different -# arities). Note that SYM may be empty. -# -# A line of the form -# -# VAR = EXPR -# -# causes a ATerm variable to be generated that is initialised to the -# value EXPR. -# -# Finally, a line of the form -# -# init NAME -# -# causes the initialisation function to be called `NAME'. This -# function must be called before any of the build/match functions or -# the generated variables are used. - -die if scalar @ARGV != 2; - -my $syms = ""; -my $init = ""; -my $initFun = "init"; - -open HEADER, ">$ARGV[0]"; -open IMPL, ">$ARGV[1]"; - -print HEADER "#include \n"; -print HEADER "#ifdef __cplusplus\n"; -print HEADER "namespace nix {\n"; -print HEADER "#endif\n\n\n"; -print IMPL "namespace nix {\n"; - -while () { - s/\#.*//; - next if (/^\s*$/); - - if (/^\s*(\w*)\s*\|([^\|]*)\|\s*(\w+)\s*\|\s*(\w+)?/) { - my $const = $1; - my @types = split ' ', $2; - my $result = $3; - my $funname = $4; - $funname = $const unless defined $funname; - - my $formals = ""; - my $formals2 = ""; - my $args = ""; - my $unpack = ""; - my $n = 1; - foreach my $type (@types) { - my $realType = $type; - $args .= ", "; - if ($type eq "string") { -# $args .= "(ATerm) ATmakeAppl0(ATmakeAFun((char *) e$n, 0, ATtrue))"; -# $type = "const char *"; - $type = "ATerm"; - $args .= "e$n"; - # !!! in the matcher, we should check that the - # argument is a string (i.e., a nullary application). - } elsif ($type eq "int") { - $args .= "(ATerm) ATmakeInt(e$n)"; - } elsif ($type eq "ATermList" || $type eq "ATermBlob") { - $args .= "(ATerm) e$n"; - } else { - $args .= "e$n"; - } - $formals .= ", " if $formals ne ""; - $formals .= "$type e$n"; - $formals2 .= ", "; - $formals2 .= "$type & e$n"; - my $m = $n - 1; - # !!! more checks here - if ($type eq "int") { - $unpack .= " e$n = ATgetInt((ATermInt) ATgetArgument(e, $m));\n"; - } elsif ($type eq "ATermList") { - $unpack .= " e$n = (ATermList) ATgetArgument(e, $m);\n"; - } elsif ($type eq "ATermBlob") { - $unpack .= " e$n = (ATermBlob) ATgetArgument(e, $m);\n"; - } elsif ($realType eq "string") { - $unpack .= " e$n = ATgetArgument(e, $m);\n"; - $unpack .= " if (ATgetType(e$n) != AT_APPL) return false;\n"; - } else { - $unpack .= " e$n = ATgetArgument(e, $m);\n"; - } - $n++; - } - - my $arity = scalar @types; - - print HEADER "extern AFun sym$funname;\n\n"; - - print IMPL "AFun sym$funname = 0;\n"; - - if ($arity == 0) { - print HEADER "extern ATerm const$funname;\n\n"; - print IMPL "ATerm const$funname = 0;\n"; - } - - print HEADER "static inline $result make$funname($formals) __attribute__ ((pure, nothrow));\n"; - print HEADER "static inline $result make$funname($formals) {\n"; - if ($arity == 0) { - print HEADER " return const$funname;\n"; - } - elsif ($arity <= 6) { - print HEADER " return (ATerm) ATmakeAppl$arity(sym$funname$args);\n"; - } else { - $args =~ s/^,//; - print HEADER " ATerm array[$arity] = {$args};\n"; - print HEADER " return (ATerm) ATmakeApplArray(sym$funname, array);\n"; - } - print HEADER "}\n\n"; - - print HEADER "#ifdef __cplusplus\n"; - print HEADER "static inline bool match$funname(ATerm e$formals2) {\n"; - print HEADER " if (ATgetType(e) != AT_APPL || (AFun) ATgetAFun(e) != sym$funname) return false;\n"; - print HEADER "$unpack"; - print HEADER " return true;\n"; - print HEADER "}\n"; - print HEADER "#endif\n\n\n"; - - $init .= " sym$funname = ATmakeAFun(\"$const\", $arity, ATfalse);\n"; - $init .= " ATprotectAFun(sym$funname);\n"; - if ($arity == 0) { - $init .= " const$funname = (ATerm) ATmakeAppl0(sym$funname);\n"; - $init .= " ATprotect(&const$funname);\n"; - } - } - - elsif (/^\s*(\w+)\s*=\s*(.*)$/) { - my $name = $1; - my $value = $2; - print HEADER "extern ATerm $name;\n"; - print IMPL "ATerm $name = 0;\n"; - $init .= " $name = $value;\n"; - $init .= " ATprotect(&$name);\n"; - } - - elsif (/^\s*init\s+(\w+)\s*$/) { - $initFun = $1; - } - - else { - die "bad line: `$_'"; - } -} - -print HEADER "void $initFun();\n\n"; - -print HEADER "static inline const char * aterm2String(ATerm t) {\n"; -print HEADER " return (const char *) ATgetName(ATgetAFun(t));\n"; -print HEADER "}\n\n"; - -print IMPL "\n"; -print IMPL "void $initFun() {\n"; -print IMPL "$init"; -print IMPL "}\n"; - -print HEADER "#ifdef __cplusplus\n"; -print HEADER "}\n"; -print HEADER "#endif\n\n\n"; -print IMPL "}\n"; - -close HEADER; -close IMPL; diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am index 39423394a..7562631cd 100644 --- a/src/libexpr/Makefile.am +++ b/src/libexpr/Makefile.am @@ -19,10 +19,8 @@ BUILT_SOURCES = \ EXTRA_DIST = lexer.l parser.y AM_CXXFLAGS = \ - -I$(srcdir)/.. ${aterm_include} \ + -I$(srcdir)/.. \ -I$(srcdir)/../libutil -I$(srcdir)/../libstore -AM_CFLAGS = \ - ${aterm_include} # Parser generation. @@ -47,4 +45,4 @@ bin_PROGRAMS = eval-test eval_test_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \ ../libstore/libstore.la ../libutil/libutil.la \ - ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@ + ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@ diff --git a/src/libmain/Makefile.am b/src/libmain/Makefile.am index c2946febc..a9ee66042 100644 --- a/src/libmain/Makefile.am +++ b/src/libmain/Makefile.am @@ -15,5 +15,5 @@ AM_CXXFLAGS = \ -DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \ -DNIX_BIN_DIR=\"$(bindir)\" \ -DNIX_VERSION=\"$(VERSION)\" \ - -I$(srcdir)/.. ${aterm_include} -I$(srcdir)/../libutil \ + -I$(srcdir)/.. -I$(srcdir)/../libutil \ -I$(srcdir)/../libstore diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 825e87621..3fbec4b52 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -13,8 +13,6 @@ #include #include -#include - namespace nix { diff --git a/src/libstore/Makefile.am b/src/libstore/Makefile.am index 47681242f..9accc3005 100644 --- a/src/libstore/Makefile.am +++ b/src/libstore/Makefile.am @@ -13,4 +13,4 @@ pkginclude_HEADERS = \ libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@ AM_CXXFLAGS = -Wall \ - -I$(srcdir)/.. ${aterm_include} -I$(srcdir)/../libutil + -I$(srcdir)/.. -I$(srcdir)/../libutil diff --git a/src/nix-env/Makefile.am b/src/nix-env/Makefile.am index 9b0d99513..0819a721c 100644 --- a/src/nix-env/Makefile.am +++ b/src/nix-env/Makefile.am @@ -4,7 +4,7 @@ nix_env_SOURCES = nix-env.cc profiles.cc user-env.cc profiles.hh help.txt nix_env_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \ ../libstore/libstore.la ../libutil/libutil.la \ - ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@ + ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@ nix-env.o: help.txt.hh @@ -12,6 +12,6 @@ nix-env.o: help.txt.hh ../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1) AM_CXXFLAGS = \ - -I$(srcdir)/.. ${aterm_include} \ + -I$(srcdir)/.. \ -I$(srcdir)/../libutil -I$(srcdir)/../libstore \ -I$(srcdir)/../libexpr -I$(srcdir)/../libmain -I../libexpr diff --git a/src/nix-hash/Makefile.am b/src/nix-hash/Makefile.am index 350aa8ebd..5f84eb34d 100644 --- a/src/nix-hash/Makefile.am +++ b/src/nix-hash/Makefile.am @@ -2,7 +2,7 @@ bin_PROGRAMS = nix-hash nix_hash_SOURCES = nix-hash.cc help.txt nix_hash_LDADD = ../libmain/libmain.la ../libstore/libstore.la ../libutil/libutil.la \ - ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@ + ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@ nix-hash.o: help.txt.hh diff --git a/src/nix-instantiate/Makefile.am b/src/nix-instantiate/Makefile.am index 3f6671719..a65907a8d 100644 --- a/src/nix-instantiate/Makefile.am +++ b/src/nix-instantiate/Makefile.am @@ -3,7 +3,7 @@ bin_PROGRAMS = nix-instantiate nix_instantiate_SOURCES = nix-instantiate.cc help.txt nix_instantiate_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \ ../libstore/libstore.la ../libutil/libutil.la \ - ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@ + ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@ nix-instantiate.o: help.txt.hh @@ -11,6 +11,5 @@ nix-instantiate.o: help.txt.hh ../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1) AM_CXXFLAGS = \ - ${aterm_include} \ -I$(srcdir)/.. -I$(srcdir)/../libutil -I$(srcdir)/../libstore \ -I$(srcdir)/../libexpr -I$(srcdir)/../libmain -I../libexpr diff --git a/src/nix-setuid-helper/Makefile.am b/src/nix-setuid-helper/Makefile.am index a0fbdf39d..35528458c 100644 --- a/src/nix-setuid-helper/Makefile.am +++ b/src/nix-setuid-helper/Makefile.am @@ -2,7 +2,7 @@ libexec_PROGRAMS = nix-setuid-helper nix_setuid_helper_SOURCES = nix-setuid-helper.cc nix_setuid_helper_LDADD = ../libutil/libutil.la \ - ../boost/format/libformat.la ${aterm_lib} + ../boost/format/libformat.la AM_CXXFLAGS = \ - -I$(srcdir)/.. $(aterm_include) -I$(srcdir)/../libutil + -I$(srcdir)/.. -I$(srcdir)/../libutil diff --git a/src/nix-store/Makefile.am b/src/nix-store/Makefile.am index ca0fec570..9a439dd92 100644 --- a/src/nix-store/Makefile.am +++ b/src/nix-store/Makefile.am @@ -2,7 +2,7 @@ bin_PROGRAMS = nix-store nix_store_SOURCES = nix-store.cc dotgraph.cc dotgraph.hh help.txt nix_store_LDADD = ../libmain/libmain.la ../libstore/libstore.la ../libutil/libutil.la \ - ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@ + ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@ nix-store.o: help.txt.hh @@ -10,5 +10,5 @@ nix-store.o: help.txt.hh ../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1) AM_CXXFLAGS = \ - -I$(srcdir)/.. $(aterm_include) -I$(srcdir)/../libutil \ + -I$(srcdir)/.. -I$(srcdir)/../libutil \ -I$(srcdir)/../libstore -I$(srcdir)/../libmain diff --git a/src/nix-worker/Makefile.am b/src/nix-worker/Makefile.am index d1163ce37..50c8ae36d 100644 --- a/src/nix-worker/Makefile.am +++ b/src/nix-worker/Makefile.am @@ -2,7 +2,7 @@ bin_PROGRAMS = nix-worker nix_worker_SOURCES = nix-worker.cc help.txt nix_worker_LDADD = ../libmain/libmain.la ../libstore/libstore.la ../libutil/libutil.la \ - ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@ + ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@ nix-worker.o: help.txt.hh @@ -10,5 +10,5 @@ nix-worker.o: help.txt.hh ../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1) AM_CXXFLAGS = \ - -I$(srcdir)/.. $(aterm_include) -I$(srcdir)/../libutil \ + -I$(srcdir)/.. -I$(srcdir)/../libutil \ -I$(srcdir)/../libstore -I$(srcdir)/../libmain diff --git a/substitute.mk b/substitute.mk index 729116477..50d63ef5c 100644 --- a/substitute.mk +++ b/substitute.mk @@ -24,7 +24,6 @@ -e "s^@xmllint\@^$(xmllint)^g" \ -e "s^@xmlflags\@^$(xmlflags)^g" \ -e "s^@xsltproc\@^$(xsltproc)^g" \ - -e "s^@aterm_bin\@^$(aterm_bin)^g" \ -e "s^@version\@^$(VERSION)^g" \ -e "s^@testPath\@^$(coreutils):$$(dirname $$(type -P expr))^g" \ < $< > $@ || rm $@ diff --git a/tests/common.sh.in b/tests/common.sh.in index 85dd3a389..b30bc44b5 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -30,7 +30,6 @@ export REAL_STORE_DIR=@storedir@ export NIX_BUILD_HOOK= export PERL=perl export TOP=$(pwd)/.. -export aterm_bin=@aterm_bin@ export bzip2_bin_test="@bzip2_bin_test@" if test "${bzip2_bin_test:0:1}" != "/"; then bzip2_bin_test=`pwd`/${bzip2_bin_test} @@ -41,10 +40,6 @@ export xmlflags="@xmlflags@" export xsltproc="@xsltproc@" export SHELL="@shell@" -# Hack to get "atdiff" to run on Cygwin (Windows looks for -# DLLs in $PATH). -export PATH=$aterm_bin/../lib:$PATH - export version=@version@ export system=@system@ diff --git a/tests/lang.sh b/tests/lang.sh index 18eb0278c..c0d436907 100644 --- a/tests/lang.sh +++ b/tests/lang.sh @@ -47,10 +47,11 @@ for i in lang/eval-okay-*.nix; do if ! $nixinstantiate $flags --eval-only lang/$i.nix > lang/$i.out; then echo "FAIL: $i should evaluate" fail=1 - elif ! $aterm_bin/atdiff lang/$i.out lang/$i.exp; then - echo "FAIL: evaluation result of $i not as expected" - fail=1 fi + #elif ! $aterm_bin/atdiff lang/$i.out lang/$i.exp; then + # echo "FAIL: evaluation result of $i not as expected" + # fail=1 + #fi fi if test -e lang/$i.exp.xml; then From fe2d869e04372de69719c3989a75247ff44b8fd4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Apr 2010 15:08:58 +0000 Subject: [PATCH 082/103] * Store user environment manifests as a Nix expression in $out/manifest.nix rather than as an ATerm. (Hm, I thought I committed this two days ago...) --- corepkgs/buildenv/builder.pl.in | 2 +- src/libexpr/eval.cc | 11 ++- src/libexpr/eval.hh | 11 ++- src/libexpr/get-drvs.cc | 23 +----- src/libexpr/get-drvs.hh | 2 +- src/libexpr/primops.cc | 31 ++++---- src/nix-env/nix-env.cc | 136 ------------------------------- src/nix-env/profiles.cc | 16 +++- src/nix-env/profiles.hh | 15 ++++ src/nix-env/user-env.cc | 137 ++++++++++++++++++++++++++++++-- src/nix-env/user-env.hh | 4 + 11 files changed, 205 insertions(+), 183 deletions(-) diff --git a/corepkgs/buildenv/builder.pl.in b/corepkgs/buildenv/builder.pl.in index 9932ea577..9eb9f7bb0 100755 --- a/corepkgs/buildenv/builder.pl.in +++ b/corepkgs/buildenv/builder.pl.in @@ -160,4 +160,4 @@ while (scalar(keys %postponed) > 0) { print STDERR "created $symlinks symlinks in user environment\n"; -symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest"; +symlink($ENV{"manifest"}, "$out/manifest.nix") or die "cannot create manifest"; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d259d58a3..f59ea99e5 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -98,6 +98,7 @@ EvalState::EvalState() , sType(symbols.create("type")) , sMeta(symbols.create("meta")) , sName(symbols.create("name")) + , sSystem(symbols.create("system")) , baseEnv(allocEnv(128)) , baseEnvDispl(0) , staticBaseEnv(false, 0) @@ -131,12 +132,13 @@ void EvalState::addPrimOp(const string & name, unsigned int arity, PrimOp primOp) { Value v; + string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; v.type = tPrimOp; v.primOp.arity = arity; v.primOp.fun = primOp; + v.primOp.name = strdup(name2.c_str()); staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; baseEnv.values[baseEnvDispl++] = v; - string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; (*baseEnv.values[0].attrs)[symbols.create(name2)] = v; } @@ -550,7 +552,12 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) vArgs[n--] = arg->primOpApp.right; /* And call the primop. */ - primOp->primOp.fun(*this, vArgs, v); + try { + primOp->primOp.fun(*this, vArgs, v); + } catch (Error & e) { + addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp.name); + throw; + } } else { Value * v2 = allocValues(2); v2[0] = fun; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 6cdc171f5..a730dc297 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -92,6 +92,7 @@ struct Value Value * val; struct { PrimOp fun; + char * name; unsigned int arity; } primOp; struct { @@ -138,6 +139,14 @@ static inline void mkCopy(Value & v, Value & src) } +static inline void mkApp(Value & v, Value & left, Value & right) +{ + v.type = tApp; + v.app.left = &left; + v.app.right = &right; +} + + void mkString(Value & v, const char * s); void mkString(Value & v, const string & s, const PathSet & context = PathSet()); void mkPath(Value & v, const char * s); @@ -162,7 +171,7 @@ public: SymbolTable symbols; - const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName; + const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sSystem; private: SrcToStore srcToStore; diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index e9f1063d9..e0ad91d8a 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -70,27 +70,6 @@ void DrvInfo::setMetaInfo(const MetaInfo & meta) { metaInfoRead = true; this->meta = meta; - -#if 0 - Value * metaAttrs = state.allocValues(1); - foreach (MetaInfo::const_iterator, i, meta) { - Expr e; - switch (i->second.type) { - case MetaValue::tpInt: e = makeInt(i->second.intValue); break; - case MetaValue::tpString: e = makeStr(i->second.stringValue); break; - case MetaValue::tpStrings: { - ATermList es = ATempty; - foreach (Strings::const_iterator, j, i->second.stringValues) - es = ATinsert(es, makeStr(*j)); - e = makeList(ATreverse(es)); - break; - } - default: abort(); - } - metaAttrs.set(toATerm(i->first), makeAttrRHS(e, makeNoPos())); - } - attrs->set(toATerm("meta"), makeAttrs(metaAttrs)); -#endif } @@ -122,7 +101,7 @@ static bool getDerivation(EvalState & state, Value & v, if (i == v.attrs->end()) throw TypeError("derivation name missing"); drv.name = state.forceStringNoCtx(i->second); - i = v.attrs->find(state.symbols.create("system")); + i = v.attrs->find(state.sSystem); if (i == v.attrs->end()) drv.system = "unknown"; else diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 6f3c381f8..ca7d98002 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -41,7 +41,7 @@ public: /* !!! make this private */ Bindings * attrs; - DrvInfo() : metaInfoRead(false) { }; + DrvInfo() : metaInfoRead(false), attrs(0) { }; string queryDrvPath(EvalState & state) const; string queryOutPath(EvalState & state) const; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b28201593..a228398e0 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -89,24 +89,29 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v) { startNest(nest, lvlDebug, "finding dependencies"); - Expr attrs = evalExpr(state, args[0]); + state.forceAttrs(*args[0]); /* Get the start set. */ - Expr startSet = queryAttr(attrs, "startSet"); - if (!startSet) throw EvalError("attribute `startSet' required"); - ATermList startSet2 = evalList(state, startSet); + Bindings::iterator startSet = + args[0]->attrs->find(state.symbols.create("startSet")); + if (startSet == args[0]->attrs->end()) + throw EvalError("attribute `startSet' required"); + state.forceList(startSet->second); - set workSet; // !!! gc roots - for (ATermIterator i(startSet2); i; ++i) workSet.insert(*i); + list workSet; + for (unsigned int n = 0; n < startSet->second.list.length; ++n) + workSet.push_back(*startSet->second.list.elems[n]); /* Get the operator. */ - Expr op = queryAttr(attrs, "operator"); - if (!op) throw EvalError("attribute `operator' required"); + Bindings::iterator op = + args[0]->attrs->find(state.symbols.create("operator")); + if (op == args[0]->attrs->end()) + throw EvalError("attribute `operator' required"); /* Construct the closure by applying the operator to element of `workSet', adding the result to `workSet', continuing until no new elements are found. */ - ATermList res = ATempty; + list res; set doneKeys; // !!! gc roots while (!workSet.empty()) { Expr e = *(workSet.begin()); @@ -322,8 +327,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) string s = state.coerceToString(i->second, context, true); drv.env[key] = s; if (key == "builder") drv.builder = s; - else if (key == "system") drv.platform = s; - else if (key == "name") drvName = s; + else if (i->first == state.sSystem) drv.platform = s; + else if (i->first == state.sName) drvName = s; else if (key == "outputHash") outputHash = s; else if (key == "outputHashAlgo") outputHashAlgo = s; else if (key == "outputHashMode") { @@ -830,9 +835,7 @@ static void prim_map(EvalState & state, Value * * args, Value & v) for (unsigned int n = 0; n < v.list.length; ++n) { v.list.elems[n] = &vs[n]; - vs[n].type = tApp; - vs[n].app.left = args[0]; - vs[n].app.right = args[1]->list.elems[n]; + mkApp(vs[n], *args[0], *args[1]->list.elems[n]); } } diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index e298c4003..4a9df454d 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -8,7 +8,6 @@ #include "help.txt.hh" #include "get-drvs.hh" #include "attr-path.hh" -#include "pathlocks.hh" #include "common-opts.hh" #include "xml-writer.hh" #include "store-api.hh" @@ -193,141 +192,6 @@ static Path getDefNixExprPath() } -/* Ensure exclusive access to a profile. Any command that modifies - the profile first acquires this lock. */ -static void lockProfile(PathLocks & lock, const Path & profile) -{ - lock.lockPaths(singleton(profile), - (format("waiting for lock on profile `%1%'") % profile).str()); - lock.setDeletion(true); -} - - -/* Optimistic locking is used by long-running operations like `nix-env - -i'. Instead of acquiring the exclusive lock for the entire - duration of the operation, we just perform the operation - optimistically (without an exclusive lock), and check at the end - whether the profile changed while we were busy (i.e., the symlink - target changed). If so, the operation is restarted. Restarting is - generally cheap, since the build results are still in the Nix - store. Most of the time, only the user environment has to be - rebuilt. */ -static string optimisticLockProfile(const Path & profile) -{ - return pathExists(profile) ? readLink(profile) : ""; -} - - -static bool createUserEnv(EvalState & state, DrvInfos & elems, - const Path & profile, bool keepDerivations, - const string & lockToken) -{ - throw Error("not implemented"); -#if 0 - /* Build the components in the user environment, if they don't - exist already. */ - PathSet drvsToBuild; - foreach (DrvInfos::const_iterator, i, elems) - /* Call to `isDerivation' is for compatibility with Nix <= 0.7 - user environments. */ - if (i->queryDrvPath(state) != "" && - isDerivation(i->queryDrvPath(state))) - drvsToBuild.insert(i->queryDrvPath(state)); - - debug(format("building user environment dependencies")); - store->buildDerivations(drvsToBuild); - - /* Get the environment builder expression. */ - Expr envBuilder = parseExprFromFile(state, - nixDataDir + "/nix/corepkgs/buildenv"); /* !!! */ - - /* Construct the whole top level derivation. */ - PathSet references; - ATermList manifest = ATempty; - ATermList inputs = ATempty; - foreach (DrvInfos::iterator, i, elems) { - /* Create a pseudo-derivation containing the name, system, - output path, and optionally the derivation path, as well as - the meta attributes. */ - Path drvPath = keepDerivations ? i->queryDrvPath(state) : ""; - - /* Round trip to get rid of "bad" meta values (like - functions). */ - MetaInfo meta = i->queryMetaInfo(state); - i->setMetaInfo(meta); - - ATermList as = ATmakeList5( - makeBind(toATerm("type"), - makeStr("derivation"), makeNoPos()), - makeBind(toATerm("name"), - makeStr(i->name), makeNoPos()), - makeBind(toATerm("system"), - makeStr(i->system), makeNoPos()), - makeBind(toATerm("outPath"), - makeStr(i->queryOutPath(state)), makeNoPos()), - makeBind(toATerm("meta"), - i->attrs->get(toATerm("meta")), makeNoPos())); - - if (drvPath != "") as = ATinsert(as, - makeBind(toATerm("drvPath"), - makeStr(drvPath), makeNoPos())); - - manifest = ATinsert(manifest, makeAttrs(as)); - - inputs = ATinsert(inputs, makeStr(i->queryOutPath(state))); - - /* This is only necessary when installing store paths, e.g., - `nix-env -i /nix/store/abcd...-foo'. */ - store->addTempRoot(i->queryOutPath(state)); - store->ensurePath(i->queryOutPath(state)); - - references.insert(i->queryOutPath(state)); - if (drvPath != "") references.insert(drvPath); - } - - /* Also write a copy of the list of inputs to the store; we need - it for future modifications of the environment. */ - Path manifestFile = store->addTextToStore("env-manifest", - atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references); - - Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3( - makeBind(toATerm("system"), - makeStr(thisSystem), makeNoPos()), - makeBind(toATerm("derivations"), - makeList(ATreverse(manifest)), makeNoPos()), - makeBind(toATerm("manifest"), - makeStr(manifestFile, singleton(manifestFile)), makeNoPos()) - ))); - - /* Instantiate it. */ - debug(format("evaluating builder expression `%1%'") % topLevel); - DrvInfo topLevelDrv; - if (!getDerivation(state, topLevel, topLevelDrv)) - abort(); - - /* Realise the resulting store expression. */ - debug(format("building user environment")); - store->buildDerivations(singleton(topLevelDrv.queryDrvPath(state))); - - /* Switch the current user environment to the output path. */ - PathLocks lock; - lockProfile(lock, profile); - - Path lockTokenCur = optimisticLockProfile(profile); - if (lockToken != lockTokenCur) { - printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile); - return false; - } - - debug(format("switching to new user environment")); - Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state)); - switchLink(profile, generation); - - return true; -#endif -} - - static int getPriority(EvalState & state, const DrvInfo & drv) { MetaValue value = drv.queryMetaInfo(state, "priority"); diff --git a/src/nix-env/profiles.cc b/src/nix-env/profiles.cc index 75585b1b2..60576f1ae 100644 --- a/src/nix-env/profiles.cc +++ b/src/nix-env/profiles.cc @@ -130,6 +130,20 @@ void switchLink(Path link, Path target) throw SysError(format("renaming `%1%' to `%2%'") % tmp % link); } - + +void lockProfile(PathLocks & lock, const Path & profile) +{ + lock.lockPaths(singleton(profile), + (format("waiting for lock on profile `%1%'") % profile).str()); + lock.setDeletion(true); +} + + +string optimisticLockProfile(const Path & profile) +{ + return pathExists(profile) ? readLink(profile) : ""; +} + + } diff --git a/src/nix-env/profiles.hh b/src/nix-env/profiles.hh index 99c20f42d..a64258dae 100644 --- a/src/nix-env/profiles.hh +++ b/src/nix-env/profiles.hh @@ -2,6 +2,7 @@ #define __PROFILES_H #include "types.hh" +#include "pathlocks.hh" #include @@ -37,6 +38,20 @@ void deleteGeneration(const Path & profile, unsigned int gen); void switchLink(Path link, Path target); +/* Ensure exclusive access to a profile. Any command that modifies + the profile first acquires this lock. */ +void lockProfile(PathLocks & lock, const Path & profile); + +/* Optimistic locking is used by long-running operations like `nix-env + -i'. Instead of acquiring the exclusive lock for the entire + duration of the operation, we just perform the operation + optimistically (without an exclusive lock), and check at the end + whether the profile changed while we were busy (i.e., the symlink + target changed). If so, the operation is restarted. Restarting is + generally cheap, since the build results are still in the Nix + store. Most of the time, only the user environment has to be + rebuilt. */ +string optimisticLockProfile(const Path & profile); } diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index f040f8c11..a0e51cae1 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -1,5 +1,12 @@ #include "util.hh" #include "get-drvs.hh" +#include "derivations.hh" +#include "store-api.hh" +#include "globals.hh" +#include "shared.hh" +#include "eval.hh" +#include "parser.hh" +#include "profiles.hh" namespace nix { @@ -11,18 +18,138 @@ static void readLegacyManifest(const Path & path, DrvInfos & elems); DrvInfos queryInstalled(EvalState & state, const Path & userEnv) { DrvInfos elems; - - Path path = userEnv + "/manifest"; - if (!pathExists(path)) - return DrvInfos(); /* not an error, assume nothing installed */ + Path manifestFile = userEnv + "/manifest.nix"; + Path oldManifestFile = userEnv + "/manifest"; - readLegacyManifest(path, elems); + if (pathExists(manifestFile)) { + Value v; + state.eval(parseExprFromFile(state, manifestFile), v); + getDerivations(state, v, "", Bindings(), elems); + } else if (pathExists(oldManifestFile)) + readLegacyManifest(oldManifestFile, elems); return elems; } +bool createUserEnv(EvalState & state, DrvInfos & elems, + const Path & profile, bool keepDerivations, + const string & lockToken) +{ + /* Build the components in the user environment, if they don't + exist already. */ + PathSet drvsToBuild; + foreach (DrvInfos::const_iterator, i, elems) + if (i->queryDrvPath(state) != "") + drvsToBuild.insert(i->queryDrvPath(state)); + + debug(format("building user environment dependencies")); + store->buildDerivations(drvsToBuild); + + /* Construct the whole top level derivation. */ + PathSet references; + Value manifest; + state.mkList(manifest, elems.size()); + unsigned int n = 0; + foreach (DrvInfos::iterator, i, elems) { + /* Create a pseudo-derivation containing the name, system, + output path, and optionally the derivation path, as well as + the meta attributes. */ + Path drvPath = keepDerivations ? i->queryDrvPath(state) : ""; + + Value & v(*state.allocValues(1)); + manifest.list.elems[n++] = &v; + state.mkAttrs(v); + + mkString((*v.attrs)[state.sType], "derivation"); + mkString((*v.attrs)[state.sName], i->name); + mkString((*v.attrs)[state.sSystem], i->system); + mkString((*v.attrs)[state.sOutPath], i->queryOutPath(state)); + if (drvPath != "") + mkString((*v.attrs)[state.sDrvPath], i->queryDrvPath(state)); + + state.mkAttrs((*v.attrs)[state.sMeta]); + + MetaInfo meta = i->queryMetaInfo(state); + + foreach (MetaInfo::const_iterator, j, meta) { + Value & v2((*(*v.attrs)[state.sMeta].attrs)[state.symbols.create(j->first)]); + switch (j->second.type) { + case MetaValue::tpInt: mkInt(v2, j->second.intValue); break; + case MetaValue::tpString: mkString(v2, j->second.stringValue); break; + case MetaValue::tpStrings: { + state.mkList(v2, j->second.stringValues.size()); + unsigned int m = 0; + foreach (Strings::const_iterator, k, j->second.stringValues) { + v2.list.elems[m] = state.allocValues(1); + mkString(*v2.list.elems[m++], *k); + } + break; + } + default: abort(); + } + } + + /* This is only necessary when installing store paths, e.g., + `nix-env -i /nix/store/abcd...-foo'. */ + store->addTempRoot(i->queryOutPath(state)); + store->ensurePath(i->queryOutPath(state)); + + references.insert(i->queryOutPath(state)); + if (drvPath != "") references.insert(drvPath); + } + + /* Also write a copy of the list of user environment elements to + the store; we need it for future modifications of the + environment. */ + Path manifestFile = store->addTextToStore("env-manifest.nix", + (format("%1%") % manifest).str(), references); + + printMsg(lvlError, manifestFile); + + /* Get the environment builder expression. */ + Value envBuilder; + state.eval(parseExprFromFile(state, nixDataDir + "/nix/corepkgs/buildenv"), envBuilder); + + /* Construct a Nix expression that calls the user environment + builder with the manifest as argument. */ + Value args, topLevel; + state.mkAttrs(args); + mkString((*args.attrs)[state.sSystem], thisSystem); + mkString((*args.attrs)[state.symbols.create("manifest")], + manifestFile, singleton(manifestFile)); + (*args.attrs)[state.symbols.create("derivations")] = manifest; + mkApp(topLevel, envBuilder, args); + + /* Evaluate it. */ + debug("evaluating user environment builder"); + DrvInfo topLevelDrv; + if (!getDerivation(state, topLevel, topLevelDrv)) + abort(); + + /* Realise the resulting store expression. */ + debug("building user environment"); + store->buildDerivations(singleton(topLevelDrv.queryDrvPath(state))); + + /* Switch the current user environment to the output path. */ + PathLocks lock; + lockProfile(lock, profile); + + Path lockTokenCur = optimisticLockProfile(profile); + if (lockToken != lockTokenCur) { + printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile); + return false; + } + + debug(format("switching to new user environment")); + Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state)); + switchLink(profile, generation); + + return true; +} + + /* Code for parsing manifests in the old textual ATerm format. */ static string parseStr(std::istream & str) diff --git a/src/nix-env/user-env.hh b/src/nix-env/user-env.hh index 6675014f1..4125d8217 100644 --- a/src/nix-env/user-env.hh +++ b/src/nix-env/user-env.hh @@ -7,6 +7,10 @@ namespace nix { DrvInfos queryInstalled(EvalState & state, const Path & userEnv); +bool createUserEnv(EvalState & state, DrvInfos & elems, + const Path & profile, bool keepDerivations, + const string & lockToken); + } #endif /* !__USER_ENV_H */ From 0777448ca68175b6daf3d8f5374cf43523828c47 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Apr 2010 15:57:11 +0000 Subject: [PATCH 083/103] * Fixed builtins.genericClosure. --- src/libexpr/eval.cc | 10 ++++-- src/libexpr/eval.hh | 2 +- src/libexpr/primops.cc | 74 ++++++++++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 25 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f59ea99e5..0e4f2519a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -73,21 +73,25 @@ std::ostream & operator << (std::ostream & str, Value & v) } -string showType(Value & v) +string showType(const Value & v) { switch (v.type) { case tInt: return "an integer"; case tBool: return "a boolean"; case tString: return "a string"; case tPath: return "a path"; + case tNull: return "null"; case tAttrs: return "an attribute set"; case tList: return "a list"; - case tNull: return "null"; + case tThunk: return "a thunk"; + case tApp: return "a function application"; case tLambda: return "a function"; + case tCopy: return "a copy"; + case tBlackhole: return "a black hole"; case tPrimOp: return "a built-in function"; case tPrimOpApp: return "a partially applied built-in function"; - default: throw Error(format("unknown type: %1%") % v.type); } + abort(); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index a730dc297..aff68ea2a 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -304,7 +304,7 @@ private: /* Return a string representing the type of the value `v'. */ -string showType(Value & v); +string showType(const Value & v); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a228398e0..9023d2b1b 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -84,7 +84,26 @@ static void prim_isBool(EvalState & state, Value * * args, Value & v) } -#if 0 +struct CompareValues +{ + bool operator () (const Value & v1, const Value & v2) const + { + if (v1.type != v2.type) + throw EvalError("cannot compare values of different types"); + switch (v1.type) { + case tInt: + return v1.integer < v2.integer; + case tString: + return strcmp(v1.string.s, v2.string.s) < 0; + case tPath: + return strcmp(v1.path, v2.path) < 0; + default: + throw EvalError(format("cannot compare %1% with %2%") % showType(v1) % showType(v2)); + } + } +}; + + static void prim_genericClosure(EvalState & state, Value * * args, Value & v) { startNest(nest, lvlDebug, "finding dependencies"); @@ -98,45 +117,60 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v) throw EvalError("attribute `startSet' required"); state.forceList(startSet->second); - list workSet; + list workSet; for (unsigned int n = 0; n < startSet->second.list.length; ++n) - workSet.push_back(*startSet->second.list.elems[n]); + workSet.push_back(startSet->second.list.elems[n]); /* Get the operator. */ Bindings::iterator op = args[0]->attrs->find(state.symbols.create("operator")); if (op == args[0]->attrs->end()) throw EvalError("attribute `operator' required"); - + state.forceValue(op->second); + /* Construct the closure by applying the operator to element of `workSet', adding the result to `workSet', continuing until no new elements are found. */ list res; - set doneKeys; // !!! gc roots + set doneKeys; while (!workSet.empty()) { - Expr e = *(workSet.begin()); - workSet.erase(e); + Value * e = *(workSet.begin()); + workSet.pop_front(); - e = strictEvalExpr(state, e); + state.forceAttrs(*e); - Expr key = queryAttr(e, "key"); - if (!key) throw EvalError("attribute `key' required"); + Bindings::iterator key = + e->attrs->find(state.symbols.create("key")); + if (key == e->attrs->end()) + throw EvalError("attribute `key' required"); + state.forceValue(key->second); - if (doneKeys.find(key) != doneKeys.end()) continue; - doneKeys.insert(key); - res = ATinsert(res, e); + if (doneKeys.find(key->second) != doneKeys.end()) continue; + doneKeys.insert(key->second); + res.push_back(*e); /* Call the `operator' function with `e' as argument. */ - ATermList res = evalList(state, makeCall(op, e)); + Value call; + mkApp(call, op->second, *e); + state.forceList(call); - /* Try to find the dependencies relative to the `path'. */ - for (ATermIterator i(res); i; ++i) - workSet.insert(evalExpr(state, *i)); + /* Add the values returned by the operator to the work set. */ + for (unsigned int n = 0; n < call.list.length; ++n) { + state.forceValue(*call.list.elems[n]); + workSet.push_back(call.list.elems[n]); + } } - return makeList(res); + /* Create the result list. */ + state.mkList(v, res.size()); + Value * vs = state.allocValues(res.size()); + + unsigned int n = 0; + foreach (list::iterator, i, res) { + v.list.elems[n] = &vs[n]; + vs[n++] = *i; + } } -#endif static void prim_abort(EvalState & state, Value * * args, Value & v) @@ -1017,9 +1051,7 @@ void EvalState::createBaseEnv() addPrimOp("__isString", 1, prim_isString); addPrimOp("__isInt", 1, prim_isInt); addPrimOp("__isBool", 1, prim_isBool); -#if 0 addPrimOp("__genericClosure", 1, prim_genericClosure); -#endif addPrimOp("abort", 1, prim_abort); addPrimOp("throw", 1, prim_throw); #if 0 From cae4efdca3a207b97244aef96161ad2974ec4e50 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Apr 2010 16:02:12 +0000 Subject: [PATCH 084/103] * Because --parse-only no longer produces an ATerm, don't check the output. Whether it parses at all should be enough. --- tests/lang.sh | 6 +----- tests/lang/parse-okay-1.exp | 1 - tests/lang/parse-okay-crlf.exp | 1 - tests/lang/parse-okay-regression-20041027.exp | 1 - tests/lang/parse-okay-subversion.exp | 1 - tests/lang/parse-okay-url.exp | 1 - 6 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 tests/lang/parse-okay-1.exp delete mode 100644 tests/lang/parse-okay-crlf.exp delete mode 100644 tests/lang/parse-okay-regression-20041027.exp delete mode 100644 tests/lang/parse-okay-subversion.exp delete mode 100644 tests/lang/parse-okay-url.exp diff --git a/tests/lang.sh b/tests/lang.sh index c0d436907..ab61b7b9d 100644 --- a/tests/lang.sh +++ b/tests/lang.sh @@ -16,14 +16,10 @@ done for i in lang/parse-okay-*.nix; do echo "parsing $i (should succeed)"; i=$(basename $i .nix) - if ! $nixinstantiate --parse-only - < lang/$i.nix > lang/$i.ast; then + if ! $nixinstantiate --parse-only - < lang/$i.nix > lang/$i.out; then echo "FAIL: $i should parse" fail=1 fi - if ! $aterm_bin/atdiff lang/$i.ast lang/$i.exp; then - echo "FAIL: parse tree of $i not as expected" - fail=1 - fi done for i in lang/eval-fail-*.nix; do diff --git a/tests/lang/parse-okay-1.exp b/tests/lang/parse-okay-1.exp deleted file mode 100644 index 2a6f05dd5..000000000 --- a/tests/lang/parse-okay-1.exp +++ /dev/null @@ -1 +0,0 @@ -Function(AttrsPat([Formal("x",NoDefaultValue),Formal("y",NoDefaultValue),Formal("z",NoDefaultValue)],Bool(False),""),OpPlus(OpPlus(Var("x"),Var("y")),Var("z")),NoPos) diff --git a/tests/lang/parse-okay-crlf.exp b/tests/lang/parse-okay-crlf.exp deleted file mode 100644 index 7d4d9e387..000000000 --- a/tests/lang/parse-okay-crlf.exp +++ /dev/null @@ -1 +0,0 @@ -Rec([Bind("x",Var("y"),NoPos),Bind("y",Int(123),NoPos),Bind("foo",Str("multi\nline\n string\n test\r",[]),NoPos),Bind("z",Int(456),NoPos)],[]) diff --git a/tests/lang/parse-okay-regression-20041027.exp b/tests/lang/parse-okay-regression-20041027.exp deleted file mode 100644 index 48251516d..000000000 --- a/tests/lang/parse-okay-regression-20041027.exp +++ /dev/null @@ -1 +0,0 @@ -Function(AttrsPat([Formal("stdenv",NoDefaultValue),Formal("fetchurl",NoDefaultValue)],Bool(False),""),Call(Select(Var("stdenv"),"mkDerivation"),Attrs([Bind("name",Str("libXi-6.0.1",[]),NoPos),Bind("src",Call(Var("fetchurl"),Attrs([Bind("md5",Str("7e935a42428d63a387b3c048be0f2756",[]),NoPos),Bind("url",Str("http://freedesktop.org/~xlibs/release/libXi-6.0.1.tar.bz2",[]),NoPos)])),NoPos)])),NoPos) diff --git a/tests/lang/parse-okay-subversion.exp b/tests/lang/parse-okay-subversion.exp deleted file mode 100644 index b54e3af78..000000000 --- a/tests/lang/parse-okay-subversion.exp +++ /dev/null @@ -1 +0,0 @@ -Function(AttrsPat([Formal("localServer",DefaultValue(Var("false"))),Formal("httpServer",DefaultValue(Var("false"))),Formal("sslSupport",DefaultValue(Var("false"))),Formal("pythonBindings",DefaultValue(Var("false"))),Formal("javaSwigBindings",DefaultValue(Var("false"))),Formal("javahlBindings",DefaultValue(Var("false"))),Formal("stdenv",NoDefaultValue),Formal("fetchurl",NoDefaultValue),Formal("openssl",DefaultValue(Var("null"))),Formal("httpd",DefaultValue(Var("null"))),Formal("db4",DefaultValue(Var("null"))),Formal("expat",NoDefaultValue),Formal("swig",DefaultValue(Var("null"))),Formal("j2sdk",DefaultValue(Var("null")))],Bool(False),""),Assert(OpNEq(Var("expat"),Var("null")),Assert(OpImpl(Var("localServer"),OpNEq(Var("db4"),Var("null"))),Assert(OpImpl(Var("httpServer"),OpAnd(OpNEq(Var("httpd"),Var("null")),OpEq(Select(Var("httpd"),"expat"),Var("expat")))),Assert(OpImpl(Var("sslSupport"),OpAnd(OpNEq(Var("openssl"),Var("null")),OpImpl(Var("httpServer"),OpEq(Select(Var("httpd"),"openssl"),Var("openssl"))))),Assert(OpImpl(Var("pythonBindings"),OpAnd(OpNEq(Var("swig"),Var("null")),Select(Var("swig"),"pythonSupport"))),Assert(OpImpl(Var("javaSwigBindings"),OpAnd(OpNEq(Var("swig"),Var("null")),Select(Var("swig"),"javaSupport"))),Assert(OpImpl(Var("javahlBindings"),OpNEq(Var("j2sdk"),Var("null"))),Call(Select(Var("stdenv"),"mkDerivation"),Attrs([Bind("builder",Path("/foo/bar"),NoPos),Bind("db4",If(Var("localServer"),Var("db4"),Var("null")),NoPos),Bind("expat",Var("expat"),NoPos),Bind("httpServer",Var("httpServer"),NoPos),Bind("httpd",If(Var("httpServer"),Var("httpd"),Var("null")),NoPos),Bind("j2sdk",If(Var("javaSwigBindings"),Select(Var("swig"),"j2sdk"),If(Var("javahlBindings"),Var("j2sdk"),Var("null"))),NoPos),Bind("javaSwigBindings",Var("javaSwigBindings"),NoPos),Bind("javahlBindings",Var("javahlBindings"),NoPos),Bind("localServer",Var("localServer"),NoPos),Bind("name",Str("subversion-1.1.1",[]),NoPos),Bind("openssl",If(Var("sslSupport"),Var("openssl"),Var("null")),NoPos),Bind("patches",If(Var("javahlBindings"),List([Path("/javahl.patch")]),List([])),NoPos),Bind("python",If(Var("pythonBindings"),Select(Var("swig"),"python"),Var("null")),NoPos),Bind("pythonBindings",Var("pythonBindings"),NoPos),Bind("src",Call(Var("fetchurl"),Attrs([Bind("md5",Str("a180c3fe91680389c210c99def54d9e0",[]),NoPos),Bind("url",Str("http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2",[]),NoPos)])),NoPos),Bind("sslSupport",Var("sslSupport"),NoPos),Bind("swig",If(OpOr(Var("pythonBindings"),Var("javaSwigBindings")),Var("swig"),Var("null")),NoPos)])),NoPos),NoPos),NoPos),NoPos),NoPos),NoPos),NoPos),NoPos) diff --git a/tests/lang/parse-okay-url.exp b/tests/lang/parse-okay-url.exp deleted file mode 100644 index 2aa22c379..000000000 --- a/tests/lang/parse-okay-url.exp +++ /dev/null @@ -1 +0,0 @@ -List([Str("x:x",[]),Str("https://svn.cs.uu.nl:12443/repos/trace/trunk",[]),Str("http://www2.mplayerhq.hu/MPlayer/releases/fonts/font-arial-iso-8859-1.tar.bz2",[]),Str("http://losser.st-lab.cs.uu.nl/~armijn/.nix/gcc-3.3.4-static-nix.tar.gz",[]),Str("http://fpdownload.macromedia.com/get/shockwave/flash/english/linux/7.0r25/install_flash_player_7_linux.tar.gz",[]),Str("ftp://ftp.gtk.org/pub/gtk/v1.2/gtk+-1.2.10.tar.gz",[])]) From 6f0f16497a8ce671c2aa9ff3cd9a27eb1f931cf2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Apr 2010 16:18:27 +0000 Subject: [PATCH 085/103] * Fix the interpretation of ''\ in indented strings. --- src/libexpr/lexer.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 7aa05db8c..00de57a7b 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -137,7 +137,7 @@ inherit { return INHERIT; } return IND_STR; } \'\'\\. { - yylval->e = new ExprIndStr(yytext + 2); + yylval->e = unescapeStr(yytext + 2); return IND_STR; } \$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; } From 7148df797174ff742fdd8cee3c2d92e6340ee930 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Apr 2010 16:22:03 +0000 Subject: [PATCH 086/103] * Update the expected test output (no longer an ATerm). --- tests/lang.sh | 9 ++++----- tests/lang/eval-okay-arithmetic.exp | 2 +- tests/lang/eval-okay-attrnames.exp | 2 +- tests/lang/eval-okay-attrs.exp | 2 +- tests/lang/eval-okay-attrs2.exp | 2 +- tests/lang/eval-okay-attrs3.exp | 2 +- tests/lang/eval-okay-autoargs.exp | 2 +- tests/lang/eval-okay-builtins.exp | 2 +- tests/lang/eval-okay-concat.exp | 2 +- tests/lang/eval-okay-empty-args.exp | 2 +- tests/lang/eval-okay-flatten.exp | 2 +- tests/lang/eval-okay-getenv.exp | 2 +- tests/lang/eval-okay-if.exp | 2 +- tests/lang/eval-okay-ind-string.exp | 2 +- tests/lang/eval-okay-let.exp | 2 +- tests/lang/eval-okay-list.exp | 2 +- tests/lang/eval-okay-listtoattrs.exp | 2 +- tests/lang/eval-okay-logic.exp | 2 +- tests/lang/eval-okay-map.exp | 2 +- tests/lang/eval-okay-new-let.exp | 2 +- tests/lang/eval-okay-pathexists.exp | 2 +- tests/lang/eval-okay-patterns.exp | 2 +- tests/lang/eval-okay-readfile.exp | 2 +- tests/lang/eval-okay-remove.exp | 2 +- tests/lang/eval-okay-scope-1.exp | 2 +- tests/lang/eval-okay-scope-2.exp | 2 +- tests/lang/eval-okay-scope-3.exp | 2 +- tests/lang/eval-okay-scope-4.exp | 2 +- tests/lang/eval-okay-scope-6.exp | 2 +- tests/lang/eval-okay-scope-7.exp | 2 +- tests/lang/eval-okay-string.exp | 2 +- tests/lang/eval-okay-substring.exp | 2 +- tests/lang/eval-okay-to-xml.exp | 2 +- tests/lang/eval-okay-versions.exp | 2 +- tests/lang/eval-okay-with.exp | 2 +- 35 files changed, 38 insertions(+), 39 deletions(-) diff --git a/tests/lang.sh b/tests/lang.sh index ab61b7b9d..eec33734c 100644 --- a/tests/lang.sh +++ b/tests/lang.sh @@ -40,14 +40,13 @@ for i in lang/eval-okay-*.nix; do if test -e lang/$i.flags; then flags=$(cat lang/$i.flags) fi - if ! $nixinstantiate $flags --eval-only lang/$i.nix > lang/$i.out; then + if ! $nixinstantiate $flags --eval-only --strict lang/$i.nix > lang/$i.out; then echo "FAIL: $i should evaluate" fail=1 + elif ! diff lang/$i.out lang/$i.exp; then + echo "FAIL: evaluation result of $i not as expected" + fail=1 fi - #elif ! $aterm_bin/atdiff lang/$i.out lang/$i.exp; then - # echo "FAIL: evaluation result of $i not as expected" - # fail=1 - #fi fi if test -e lang/$i.exp.xml; then diff --git a/tests/lang/eval-okay-arithmetic.exp b/tests/lang/eval-okay-arithmetic.exp index 433cb1c9b..9c113c6f7 100644 --- a/tests/lang/eval-okay-arithmetic.exp +++ b/tests/lang/eval-okay-arithmetic.exp @@ -1 +1 @@ -Int(1275) +1275 diff --git a/tests/lang/eval-okay-attrnames.exp b/tests/lang/eval-okay-attrnames.exp index 98af99a0c..b4aa387e0 100644 --- a/tests/lang/eval-okay-attrnames.exp +++ b/tests/lang/eval-okay-attrnames.exp @@ -1 +1 @@ -Str("newxfoonewxy",[]) +"newxfoonewxy" diff --git a/tests/lang/eval-okay-attrs.exp b/tests/lang/eval-okay-attrs.exp index add36384d..45b0f829e 100644 --- a/tests/lang/eval-okay-attrs.exp +++ b/tests/lang/eval-okay-attrs.exp @@ -1 +1 @@ -Int(987) \ No newline at end of file +987 diff --git a/tests/lang/eval-okay-attrs2.exp b/tests/lang/eval-okay-attrs2.exp index add36384d..45b0f829e 100644 --- a/tests/lang/eval-okay-attrs2.exp +++ b/tests/lang/eval-okay-attrs2.exp @@ -1 +1 @@ -Int(987) \ No newline at end of file +987 diff --git a/tests/lang/eval-okay-attrs3.exp b/tests/lang/eval-okay-attrs3.exp index d2c7555c1..19de4fdf7 100644 --- a/tests/lang/eval-okay-attrs3.exp +++ b/tests/lang/eval-okay-attrs3.exp @@ -1 +1 @@ -Str("foo 22 80 itchyxac",[]) +"foo 22 80 itchyxac" diff --git a/tests/lang/eval-okay-autoargs.exp b/tests/lang/eval-okay-autoargs.exp index 3b12bdd5e..7a8391786 100644 --- a/tests/lang/eval-okay-autoargs.exp +++ b/tests/lang/eval-okay-autoargs.exp @@ -1 +1 @@ -Str("xyzzy!xyzzy!foobar",[]) +"xyzzy!xyzzy!foobar" diff --git a/tests/lang/eval-okay-builtins.exp b/tests/lang/eval-okay-builtins.exp index f4f3ba81a..0661686d6 100644 --- a/tests/lang/eval-okay-builtins.exp +++ b/tests/lang/eval-okay-builtins.exp @@ -1 +1 @@ -Path("/foo") +/foo diff --git a/tests/lang/eval-okay-concat.exp b/tests/lang/eval-okay-concat.exp index 359ccef86..bb4bbd577 100644 --- a/tests/lang/eval-okay-concat.exp +++ b/tests/lang/eval-okay-concat.exp @@ -1 +1 @@ -List([Int(1),Int(2),Int(3),Int(4),Int(5),Int(6),Int(7),Int(8),Int(9)]) +[ 1 2 3 4 5 6 7 8 9 ] diff --git a/tests/lang/eval-okay-empty-args.exp b/tests/lang/eval-okay-empty-args.exp index c386bbc80..cb5537d5d 100644 --- a/tests/lang/eval-okay-empty-args.exp +++ b/tests/lang/eval-okay-empty-args.exp @@ -1 +1 @@ -Str("ab",[]) +"ab" diff --git a/tests/lang/eval-okay-flatten.exp b/tests/lang/eval-okay-flatten.exp index 50c91bee5..b979b2b8b 100644 --- a/tests/lang/eval-okay-flatten.exp +++ b/tests/lang/eval-okay-flatten.exp @@ -1 +1 @@ -Str("1234567",[]) +"1234567" diff --git a/tests/lang/eval-okay-getenv.exp b/tests/lang/eval-okay-getenv.exp index 7c3659b97..14e24d419 100644 --- a/tests/lang/eval-okay-getenv.exp +++ b/tests/lang/eval-okay-getenv.exp @@ -1 +1 @@ -Str("foobar",[]) +"foobar" diff --git a/tests/lang/eval-okay-if.exp b/tests/lang/eval-okay-if.exp index 8b99801d3..00750edc0 100644 --- a/tests/lang/eval-okay-if.exp +++ b/tests/lang/eval-okay-if.exp @@ -1 +1 @@ -Int(3) \ No newline at end of file +3 diff --git a/tests/lang/eval-okay-ind-string.exp b/tests/lang/eval-okay-ind-string.exp index b8d2f5b39..886219dcf 100644 --- a/tests/lang/eval-okay-ind-string.exp +++ b/tests/lang/eval-okay-ind-string.exp @@ -1 +1 @@ -Str("This is an indented multi-line string\nliteral. An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed. Thus,\nin this case four spaces will be\nstripped from each line, even though\n THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n followed by a newline, it's stripped, but\n that's not the case here. Two spaces are\n stripped because of the \" \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n Tabs are not interpreted as whitespace (since we can't guess\n what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored. But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n Similarly you can force an indentation level,\n in this case to 2 spaces. This works because the anti-quote\n is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n rm -f /var/run/opengl-driver\n ln -sf 123 /var/run/opengl-driver\n\n rm -f /var/log/slim.log\n \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\n",[]) +"This is an indented multi-line string\nliteral. An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed. Thus,\nin this case four spaces will be\nstripped from each line, even though\n THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n followed by a newline, it's stripped, but\n that's not the case here. Two spaces are\n stripped because of the \" \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n Tabs are not interpreted as whitespace (since we can't guess\n what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored. But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n Similarly you can force an indentation level,\n in this case to 2 spaces. This works because the anti-quote\n is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n rm -f /var/run/opengl-driver\n ln -sf 123 /var/run/opengl-driver\n\n rm -f /var/log/slim.log\n \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\n" diff --git a/tests/lang/eval-okay-let.exp b/tests/lang/eval-okay-let.exp index 7c3659b97..14e24d419 100644 --- a/tests/lang/eval-okay-let.exp +++ b/tests/lang/eval-okay-let.exp @@ -1 +1 @@ -Str("foobar",[]) +"foobar" diff --git a/tests/lang/eval-okay-list.exp b/tests/lang/eval-okay-list.exp index b9cad67dd..f784f26d8 100644 --- a/tests/lang/eval-okay-list.exp +++ b/tests/lang/eval-okay-list.exp @@ -1 +1 @@ -Str("foobarblatest",[]) +"foobarblatest" diff --git a/tests/lang/eval-okay-listtoattrs.exp b/tests/lang/eval-okay-listtoattrs.exp index 49adcc16f..11d29b588 100644 --- a/tests/lang/eval-okay-listtoattrs.exp +++ b/tests/lang/eval-okay-listtoattrs.exp @@ -1 +1 @@ -Str("AA",[]) +"AA" diff --git a/tests/lang/eval-okay-logic.exp b/tests/lang/eval-okay-logic.exp index f1161b8d5..d00491fd7 100644 --- a/tests/lang/eval-okay-logic.exp +++ b/tests/lang/eval-okay-logic.exp @@ -1 +1 @@ -Int(1) \ No newline at end of file +1 diff --git a/tests/lang/eval-okay-map.exp b/tests/lang/eval-okay-map.exp index 2d9456dc0..dbb64f717 100644 --- a/tests/lang/eval-okay-map.exp +++ b/tests/lang/eval-okay-map.exp @@ -1 +1 @@ -Str("foobarblabarxyzzybar",[]) +"foobarblabarxyzzybar" diff --git a/tests/lang/eval-okay-new-let.exp b/tests/lang/eval-okay-new-let.exp index f10a34e71..f98b38807 100644 --- a/tests/lang/eval-okay-new-let.exp +++ b/tests/lang/eval-okay-new-let.exp @@ -1 +1 @@ -Str("xyzzyfoobar",[]) +"xyzzyfoobar" diff --git a/tests/lang/eval-okay-pathexists.exp b/tests/lang/eval-okay-pathexists.exp index 2015847b6..27ba77dda 100644 --- a/tests/lang/eval-okay-pathexists.exp +++ b/tests/lang/eval-okay-pathexists.exp @@ -1 +1 @@ -Bool(True) +true diff --git a/tests/lang/eval-okay-patterns.exp b/tests/lang/eval-okay-patterns.exp index d497e9f2b..a4304010f 100644 --- a/tests/lang/eval-okay-patterns.exp +++ b/tests/lang/eval-okay-patterns.exp @@ -1 +1 @@ -Str("abcxyzDDDDEFijk",[]) +"abcxyzDDDDEFijk" diff --git a/tests/lang/eval-okay-readfile.exp b/tests/lang/eval-okay-readfile.exp index 979771c7a..a2c87d0c4 100644 --- a/tests/lang/eval-okay-readfile.exp +++ b/tests/lang/eval-okay-readfile.exp @@ -1 +1 @@ -Str("builtins.readFile ./eval-okay-readfile.nix\n",[]) +"builtins.readFile ./eval-okay-readfile.nix\n" diff --git a/tests/lang/eval-okay-remove.exp b/tests/lang/eval-okay-remove.exp index e22e6b1b2..8d38505c1 100644 --- a/tests/lang/eval-okay-remove.exp +++ b/tests/lang/eval-okay-remove.exp @@ -1 +1 @@ -Int(456) +456 diff --git a/tests/lang/eval-okay-scope-1.exp b/tests/lang/eval-okay-scope-1.exp index 7da0e4219..00750edc0 100644 --- a/tests/lang/eval-okay-scope-1.exp +++ b/tests/lang/eval-okay-scope-1.exp @@ -1 +1 @@ -Int(3) +3 diff --git a/tests/lang/eval-okay-scope-2.exp b/tests/lang/eval-okay-scope-2.exp index 067d2b746..d00491fd7 100644 --- a/tests/lang/eval-okay-scope-2.exp +++ b/tests/lang/eval-okay-scope-2.exp @@ -1 +1 @@ -Int(1) +1 diff --git a/tests/lang/eval-okay-scope-3.exp b/tests/lang/eval-okay-scope-3.exp index 65c1faee2..b8626c4cf 100644 --- a/tests/lang/eval-okay-scope-3.exp +++ b/tests/lang/eval-okay-scope-3.exp @@ -1 +1 @@ -Int(4) +4 diff --git a/tests/lang/eval-okay-scope-4.exp b/tests/lang/eval-okay-scope-4.exp index ea9580603..00ff03a46 100644 --- a/tests/lang/eval-okay-scope-4.exp +++ b/tests/lang/eval-okay-scope-4.exp @@ -1 +1 @@ -Str("ccdd",[]) +"ccdd" diff --git a/tests/lang/eval-okay-scope-6.exp b/tests/lang/eval-okay-scope-6.exp index ea9580603..00ff03a46 100644 --- a/tests/lang/eval-okay-scope-6.exp +++ b/tests/lang/eval-okay-scope-6.exp @@ -1 +1 @@ -Str("ccdd",[]) +"ccdd" diff --git a/tests/lang/eval-okay-scope-7.exp b/tests/lang/eval-okay-scope-7.exp index 067d2b746..d00491fd7 100644 --- a/tests/lang/eval-okay-scope-7.exp +++ b/tests/lang/eval-okay-scope-7.exp @@ -1 +1 @@ -Int(1) +1 diff --git a/tests/lang/eval-okay-string.exp b/tests/lang/eval-okay-string.exp index 8249c7c8d..607920247 100644 --- a/tests/lang/eval-okay-string.exp +++ b/tests/lang/eval-okay-string.exp @@ -1 +1 @@ -Str("foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/yescape: \"quote\" \n \\end\nof\nlinefoobarblaatfoo$bar",[]) +"foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/yescape: \"quote\" \n \\end\nof\nlinefoobarblaatfoo$bar" diff --git a/tests/lang/eval-okay-substring.exp b/tests/lang/eval-okay-substring.exp index d6098476b..d936b7e96 100644 --- a/tests/lang/eval-okay-substring.exp +++ b/tests/lang/eval-okay-substring.exp @@ -1 +1 @@ -Str("ooxfoobarybarzobaabb",[]) +"ooxfoobarybarzobaabb" diff --git a/tests/lang/eval-okay-to-xml.exp b/tests/lang/eval-okay-to-xml.exp index 0a4d1884e..634a841eb 100644 --- a/tests/lang/eval-okay-to-xml.exp +++ b/tests/lang/eval-okay-to-xml.exp @@ -1 +1 @@ -Str("\n\n \n \n \n \n \n \n \n \n \n \n \n \n\n",[]) +"\n\n \n \n \n \n \n \n \n \n \n \n \n \n\n" diff --git a/tests/lang/eval-okay-versions.exp b/tests/lang/eval-okay-versions.exp index 2015847b6..27ba77dda 100644 --- a/tests/lang/eval-okay-versions.exp +++ b/tests/lang/eval-okay-versions.exp @@ -1 +1 @@ -Bool(True) +true diff --git a/tests/lang/eval-okay-with.exp b/tests/lang/eval-okay-with.exp index 2c9bd8ba0..378c8dc80 100644 --- a/tests/lang/eval-okay-with.exp +++ b/tests/lang/eval-okay-with.exp @@ -1 +1 @@ -Str("xyzzybarxyzzybar",[]) +"xyzzybarxyzzybar" From 6bbfe95e3012effa0df79066ae129ce9828a8ff2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Apr 2010 19:25:50 +0000 Subject: [PATCH 087/103] * Don't use an ostringstream in unparseDerivation(), because it's slow. A `nix-env -qa --drv-path \*' on Nixpkgs was spending 40% of its time in unparseDerivation() because of this (now 11%). --- src/libstore/derivations.cc | 77 +++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 3d0f1eb42..e321ae8aa 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -92,75 +92,76 @@ Derivation parseDerivation(const string & s) } -void printString(std::ostream & str, const string & s) +static void printString(string & res, const string & s) { - str << "\""; + res += '"'; for (const char * i = s.c_str(); *i; i++) - if (*i == '\"' || *i == '\\') str << "\\" << *i; - else if (*i == '\n') str << "\\n"; - else if (*i == '\r') str << "\\r"; - else if (*i == '\t') str << "\\t"; - else str << *i; - str << "\""; + if (*i == '\"' || *i == '\\') { res += "\\"; res += *i; } + else if (*i == '\n') res += "\\n"; + else if (*i == '\r') res += "\\r"; + else if (*i == '\t') res += "\\t"; + else res += *i; + res += '"'; } template -void printStrings(std::ostream & str, ForwardIterator i, ForwardIterator j) +static void printStrings(string & res, ForwardIterator i, ForwardIterator j) { - str << "["; + res += '['; bool first = true; for ( ; i != j; ++i) { - if (first) first = false; else str << ","; - printString(str, *i); + if (first) first = false; else res += ','; + printString(res, *i); } - str << "]"; + res += ']'; } string unparseDerivation(const Derivation & drv) { - std::ostringstream str; - str << "Derive(["; + string s; + s.reserve(65536); + s += "Derive(["; bool first = true; foreach (DerivationOutputs::const_iterator, i, drv.outputs) { - if (first) first = false; else str << ","; - str << "("; printString(str, i->first); - str << ","; printString(str, i->second.path); - str << ","; printString(str, i->second.hashAlgo); - str << ","; printString(str, i->second.hash); - str << ")"; + if (first) first = false; else s += ','; + s += '('; printString(s, i->first); + s += ','; printString(s, i->second.path); + s += ','; printString(s, i->second.hashAlgo); + s += ','; printString(s, i->second.hash); + s += ')'; } - str << "],["; + s += "],["; first = true; foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) { - if (first) first = false; else str << ","; - str << "("; printString(str, i->first); - str << ","; printStrings(str, i->second.begin(), i->second.end()); - str << ")"; + if (first) first = false; else s += ','; + s += '('; printString(s, i->first); + s += ','; printStrings(s, i->second.begin(), i->second.end()); + s += ')'; } - str << "],"; - printStrings(str, drv.inputSrcs.begin(), drv.inputSrcs.end()); + s += "],"; + printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end()); - str << ","; printString(str, drv.platform); - str << ","; printString(str, drv.builder); - str << ","; printStrings(str, drv.args.begin(), drv.args.end()); + s += ','; printString(s, drv.platform); + s += ','; printString(s, drv.builder); + s += ','; printStrings(s, drv.args.begin(), drv.args.end()); - str << ",["; + s += ",["; first = true; foreach (StringPairs::const_iterator, i, drv.env) { - if (first) first = false; else str << ","; - str << "("; printString(str, i->first); - str << ","; printString(str, i->second); - str << ")"; + if (first) first = false; else s += ','; + s += '('; printString(s, i->first); + s += ','; printString(s, i->second); + s += ')'; } - str << "])"; + s += "])"; - return str.str(); + return s; } From 2d7636529f782b552b634497fd8ac876aae72fcc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Apr 2010 09:54:11 +0000 Subject: [PATCH 088/103] * String equality tests should take the context into account. All the evaluation test cases now succeed. --- src/libexpr/eval.cc | 14 +++++++++++--- src/libexpr/eval.hh | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0e4f2519a..9acd42310 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1019,9 +1019,17 @@ bool EvalState::eqValues(Value & v1, Value & v2) case tBool: return v1.boolean == v2.boolean; - case tString: - /* !!! contexts */ - return strcmp(v1.string.s, v2.string.s) == 0; + case tString: { + /* Compare both the string and its context. */ + if (strcmp(v1.string.s, v2.string.s) != 0) return false; + const char * * p = v1.string.context, * * q = v2.string.context; + if (!p && !q) return true; + if (!p || !q) return false; + for ( ; *p && *q; ++p, ++q) + if (strcmp(*p, *q) != 0) return false; + if (*p || *q) return false; + return true; + } case tPath: return strcmp(v1.path, v2.path) == 0; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index aff68ea2a..2726fd971 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -69,7 +69,7 @@ struct Value For canonicity, the store paths should be in sorted order. */ struct { const char * s; - const char * * context; + const char * * context; // must be in sorted order } string; const char * path; From ebade9ff8b8557bdae7cdaf9f70c12ceeb3dc02c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Apr 2010 11:02:24 +0000 Subject: [PATCH 089/103] * Check for duplicate attribute names / function arguments. `make check' now succeeds :-) * An attribute set such as `{ foo = { enable = true; }; foo.port = 23; }' now parses. It was previously rejected, but I'm too lazy to implement the check. (The only reason to reject it is that the reverse, `{ foo.port = 23; foo = { enable = true; }; }', is rejected, which is kind of ugly.) --- src/libexpr/nixexpr.hh | 9 +- src/libexpr/parser.y | 83 +++++++++---------- tests/lang/eval-okay-context.exp | 2 +- tests/lang/parse-fail-dup-attrs-7.nix | 9 ++ ...attrs-5.nix => parse-okay-dup-attrs-5.nix} | 0 5 files changed, 58 insertions(+), 45 deletions(-) create mode 100644 tests/lang/parse-fail-dup-attrs-7.nix rename tests/lang/{parse-fail-dup-attrs-5.nix => parse-okay-dup-attrs-5.nix} (100%) diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 9e5a262d7..4da77ee58 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -128,6 +128,7 @@ struct ExprAttrs : Expr typedef std::map Attrs; Attrs attrs; list inherited; + set attrNames; // used during parsing ExprAttrs() : recursive(false) { }; COMMON_METHODS }; @@ -150,6 +151,7 @@ struct Formals { typedef std::list Formals_; Formals_ formals; + std::set argNames; // used during parsing bool ellipsis; }; @@ -161,7 +163,12 @@ struct ExprLambda : Expr Formals * formals; Expr * body; ExprLambda(const Pos & pos, const Symbol & arg, bool matchAttrs, Formals * formals, Expr * body) - : pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body) { }; + : pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body) + { + if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end()) + throw ParseError(format("duplicate formal function argument `%1%' at %2%") + % arg % pos); + }; COMMON_METHODS }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 06fcc72fc..66da76940 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -59,6 +59,21 @@ static string showAttrPath(const vector & attrPath) } return s; } + + +static void dupAttr(const vector & attrPath, const Pos & pos) +{ + throw ParseError(format("attribute `%1%' at %2% already defined at ") + % showAttrPath(attrPath) % pos); +} + + +static void dupAttr(Symbol attr, const Pos & pos) +{ + vector attrPath; attrPath.push_back(attr); + throw ParseError(format("attribute `%1%' at %2% already defined at ") + % showAttrPath(attrPath) % pos); +} static void addAttr(ExprAttrs * attrs, const vector & attrPath, @@ -69,11 +84,12 @@ static void addAttr(ExprAttrs * attrs, const vector & attrPath, n++; if (attrs->attrs[*i]) { ExprAttrs * attrs2 = dynamic_cast(attrs->attrs[*i]); - if (!attrs2) - throw ParseError(format("attribute `%1%' at %2% already defined at ") - % showAttrPath(attrPath) % pos); + if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos); attrs = attrs2; } else { + if (attrs->attrNames.find(*i) != attrs->attrNames.end()) + dupAttr(attrPath, pos); + attrs->attrNames.insert(*i); if (n == attrPath.size()) attrs->attrs[*i] = e; else { @@ -86,43 +102,16 @@ static void addAttr(ExprAttrs * attrs, const vector & attrPath, } -#if 0 -static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat) +static void addFormal(const Pos & pos, Formals * formals, const Formal & formal) { - ATerm name = sNoAlias; - ATermList formals; - ATermBool ellipsis; - - if (matchAttrsPat(pat, formals, ellipsis, name)) { - for (ATermIterator i(formals); i; ++i) { - ATerm d1, name2; - if (!matchFormal(*i, name2, d1)) abort(); - if (map.get(name2)) - throw ParseError(format("duplicate formal function argument `%1%' at %2%") - % aterm2String(name2) % showPos(pos)); - map.set(name2, name2); - } - } - - else matchVarPat(pat, name); - - if (name != sNoAlias) { - if (map.get(name)) - throw ParseError(format("duplicate formal function argument `%1%' at %2%") - % aterm2String(name) % showPos(pos)); - map.set(name, name); - } + if (formals->argNames.find(formal.name) != formals->argNames.end()) + throw ParseError(format("duplicate formal function argument `%1%' at %2%") + % formal.name % pos); + formals->formals.push_front(formal); + formals->argNames.insert(formal.name); } -static void checkPatternVars(ATerm pos, Pattern pat) -{ - ATermMap map; - checkPatternVars(pos, map, pat); -} -#endif - - static Expr * stripIndentation(vector & es) { if (es.empty()) return new ExprString(""); @@ -294,7 +283,7 @@ expr: expr_function; expr_function : ID ':' expr_function - { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), false, 0, $3); /* checkPatternVars(CUR_POS, $1); */ } + { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), false, 0, $3); } | '{' formals '}' ':' expr_function { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), true, $2, $5); } | '{' formals '}' '@' ID ':' expr_function @@ -388,14 +377,22 @@ binds : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, CUR_POS); } | binds INHERIT ids ';' { $$ = $1; - foreach (vector::iterator, i, *$3) - $$->inherited.push_back(*i); + foreach (vector::iterator, i, *$3) { + if ($$->attrNames.find(*i) != $$->attrNames.end()) + dupAttr(*i, CUR_POS); + $$->inherited.push_back(*i); + $$->attrNames.insert(*i); + } } | binds INHERIT '(' expr ')' ids ';' { $$ = $1; /* !!! Should ensure sharing of the expression in $4. */ - foreach (vector::iterator, i, *$6) - $$->attrs[*i] = new ExprSelect($4, *i); + foreach (vector::iterator, i, *$6) { + if ($$->attrNames.find(*i) != $$->attrNames.end()) + dupAttr(*i, CUR_POS); + $$->attrs[*i] = new ExprSelect($4, *i); + $$->attrNames.insert(*i); + } } | { $$ = new ExprAttrs; } ; @@ -417,9 +414,9 @@ expr_list formals : formal ',' formals - { $$ = $3; $$->formals.push_front(*$1); /* !!! dangerous */ } + { $$ = $3; addFormal(CUR_POS, $$, *$1); } | formal - { $$ = new Formals; $$->formals.push_back(*$1); $$->ellipsis = false; } + { $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; } | { $$ = new Formals; $$->ellipsis = false; } | ELLIPSIS diff --git a/tests/lang/eval-okay-context.exp b/tests/lang/eval-okay-context.exp index 95a993654..2f535bdbc 100644 --- a/tests/lang/eval-okay-context.exp +++ b/tests/lang/eval-okay-context.exp @@ -1 +1 @@ -Str("foo eval-okay-context.nix bar",[]) +"foo eval-okay-context.nix bar" diff --git a/tests/lang/parse-fail-dup-attrs-7.nix b/tests/lang/parse-fail-dup-attrs-7.nix new file mode 100644 index 000000000..bbc3eb08c --- /dev/null +++ b/tests/lang/parse-fail-dup-attrs-7.nix @@ -0,0 +1,9 @@ +rec { + + x = 1; + + as = { + inherit x; + inherit x; + }; +} \ No newline at end of file diff --git a/tests/lang/parse-fail-dup-attrs-5.nix b/tests/lang/parse-okay-dup-attrs-5.nix similarity index 100% rename from tests/lang/parse-fail-dup-attrs-5.nix rename to tests/lang/parse-okay-dup-attrs-5.nix From ee0384fb966b7a9bd202fa6fb447788be30c4ce4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Apr 2010 11:34:15 +0000 Subject: [PATCH 090/103] --- externals/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/Makefile.am b/externals/Makefile.am index 838f5d704..63150b1f5 100644 --- a/externals/Makefile.am +++ b/externals/Makefile.am @@ -34,7 +34,7 @@ endif all: build-bzip2 -EXTRA_DIST = $(BZIP2).tar.gz max-long.patch sizeof.patch +EXTRA_DIST = $(BZIP2).tar.gz ext-clean: $(RM) -f have-bzip2 build-bzip2 From 0bc468f195e37a8a5f4f8b36ae6c92459e8ca652 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Apr 2010 15:08:09 +0000 Subject: [PATCH 091/103] * Simplify the implementation of `with'. This gives a 7% speedup in evaluating the NixOS system configuration. --- src/libexpr/eval.cc | 34 +++++++++------------------------- src/libexpr/eval.hh | 1 + src/libexpr/nixexpr.cc | 8 ++++---- src/libexpr/nixexpr.hh | 2 +- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 9acd42310..b4f12c8a9 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -246,10 +246,14 @@ Value * EvalState::lookupVar(Env * env, const VarRef & var) for (unsigned int l = var.level; l; --l, env = env->up) ; if (var.fromWith) { - Bindings::iterator j = env->values[0].attrs->find(var.name); - if (j == env->values[0].attrs->end()) - throwEvalError("undefined variable `%1%'", var.name); - return &j->second; + while (1) { + Bindings::iterator j = env->values[0].attrs->find(var.name); + if (j != env->values[0].attrs->end()) + return &j->second; + if (env->prevWith == 0) + throwEvalError("undefined variable `%1%'", var.name); + for (unsigned int l = env->prevWith; l; --l, env = env->up) ; + } } else return &env->values[var.displ]; } @@ -656,30 +660,10 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) { Env & env2(state.allocEnv(1)); env2.up = &env; + env2.prevWith = prevWith; state.evalAttrs(env, attrs, 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) ; - - /* Because the first `with' may be a shallow copy of another - attribute set (through a tCopy node), we need to clone its - `attrs' before modifying them. */ - Bindings * old(env2.values[0].attrs); - state.mkAttrs(env2.values[0]); - foreach (Bindings::iterator, i, *old) - mkCopy((*env2.values[0].attrs)[i->first], i->second); - - 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()) - mkCopy((*env2.values[0].attrs)[i->first], i->second); - } - } - state.eval(env2, body, v); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 2726fd971..6912e2288 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -106,6 +106,7 @@ struct Value struct Env { Env * up; + unsigned int prevWith; // nr of levels up to next `with' environment Value values[0]; }; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 3dfbddf5b..a9c83108e 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -278,12 +278,12 @@ 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'. */ + level so that `lookupVar' can look up variables in the previous + `with' if this one doesn't contain the desired attribute. */ const StaticEnv * curEnv; unsigned int level; - prevWith = -1; - for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++) + prevWith = 0; + for (curEnv = &env, level = 1; curEnv; curEnv = curEnv->up, level++) if (curEnv->isWith) { prevWith = level; break; diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 4da77ee58..2d328d382 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -184,7 +184,7 @@ struct ExprWith : Expr { Pos pos; Expr * attrs, * body; - int prevWith; + unsigned int prevWith; ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { }; COMMON_METHODS }; From 2be6118f4c7e909f36d5563ad0ea70780f30cc10 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 23 Apr 2010 09:09:32 +0000 Subject: [PATCH 092/103] * Don't need the test program anymore. --- src/libexpr/Makefile.am | 8 --- src/libexpr/eval-test.cc | 127 --------------------------------------- 2 files changed, 135 deletions(-) delete mode 100644 src/libexpr/eval-test.cc diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am index 7562631cd..e7228e183 100644 --- a/src/libexpr/Makefile.am +++ b/src/libexpr/Makefile.am @@ -38,11 +38,3 @@ nix.tbl: nix.sdf test.ast: test.nix nix.tbl sglri -p nix.tbl -i test.nix -o test.ast - - -# Testing. -bin_PROGRAMS = eval-test - -eval_test_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \ - ../libstore/libstore.la ../libutil/libutil.la \ - ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@ diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc deleted file mode 100644 index f7f91f503..000000000 --- a/src/libexpr/eval-test.cc +++ /dev/null @@ -1,127 +0,0 @@ -#include "nixexpr.hh" -#include "parser.hh" -#include "hash.hh" -#include "util.hh" - -#include -#include -#include - -using namespace nix; - - -void doTest(EvalState & state, string s) -{ - Expr * e = parseExprFromString(state, s, absPath(".")); - std::cerr << ">>>>> " << *e << std::endl; - Value v; - state.eval(e, v); - state.strictForceValue(v); - printMsg(lvlError, format("result: %1%") % v); -} - - -void run(Strings args) -{ - SymbolTable t; - - printMsg(lvlError, format("size of symbol: %1% bytes") % sizeof(Symbol)); - - Symbol s1 = t.create("foo"); - Symbol s2 = t.create("foo"); - Symbol s3 = t.create("bar"); - Symbol s4 = t.create("foo"); - - assert(s1 == s2); - assert(s1 == s4); - assert(s1 != s3); - - std::map m; - - m[s1] = 123; - m[s3] = 456; - - std::cout << m[s1] << std::endl; - std::cout << m[s2] << std::endl; - std::cout << m[s3] << std::endl; - std::cout << m[s4] << std::endl; - - EvalState state; - - printMsg(lvlError, format("size of value: %1% bytes") % sizeof(Value)); - printMsg(lvlError, format("size of int AST node: %1% bytes") % sizeof(ExprInt)); - printMsg(lvlError, format("size of attrset AST node: %1% bytes") % sizeof(ExprAttrs)); - - doTest(state, "123"); - doTest(state, "{ x = 1; y = 2; }"); - doTest(state, "{ x = 1; y = 2; }.y"); - doTest(state, "let x = 1; y = 2; z = 3; in let a = 4; in y"); - doTest(state, "rec { x = 1; y = x; }.x"); - doTest(state, "(x: x) 1"); - doTest(state, "(x: y: y) 1 2"); - doTest(state, "x: x"); - doTest(state, "({x, y}: x) { x = 1; y = 2; }"); - doTest(state, "({x, y}@args: args.x) { x = 1; y = 2; }"); - doTest(state, "(args@{x, y}: args.x) { x = 1; y = 2; }"); - doTest(state, "({x ? 1}: x) { }"); - doTest(state, "({x ? 1, y ? x}: y) { x = 2; }"); - doTest(state, "({x, y, ...}: x) { x = 1; y = 2; z = 3; }"); - doTest(state, "({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); - //doTest(state, "({x ? y, y ? x}: y) { }"); - doTest(state, "let x = 1; in x"); - 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"); // => 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"); - doTest(state, "123 == 456"); - doTest(state, "let id = x: x; in [1 2] == [(id 1) (id 2)]"); - doTest(state, "let id = x: x; in [1 2] == [(id 1) (id 3)]"); - doTest(state, "[1 2] == [3 (let x = x; in x)]"); - doTest(state, "{ x = 1; y.z = 2; } == { y = { z = 2; }; x = 1; }"); - doTest(state, "{ x = 1; y = 2; } == { x = 2; }"); - doTest(state, "{ x = [ 1 2 ]; } == { x = [ 1 ] ++ [ 2 ]; }"); - doTest(state, "1 != 1"); - doTest(state, "true"); - doTest(state, "builtins.true"); - doTest(state, "true == false"); - doTest(state, "__head [ 1 2 3 ]"); - doTest(state, "__add 1 2"); - doTest(state, "null"); - doTest(state, "\"foo\""); - doTest(state, "''\n foo\n bar\n ''"); - doTest(state, "let s = \"bar\"; in \"foo${s}\""); - doTest(state, "if true then 1 else 2"); - doTest(state, "if false then 1 else 2"); - doTest(state, "if false || true then 1 else 2"); - doTest(state, "!(true || false)"); - doTest(state, "let x = x; in if true || x then 1 else 2"); - doTest(state, "http://nixos.org/"); - doTest(state, "/etc/passwd"); - //doTest(state, "import ./foo.nix"); - doTest(state, "map (x: __add 1 x) [ 1 2 3 ]"); - doTest(state, "map (builtins.add 1) [ 1 2 3 ]"); - doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); - doTest(state, "let x = 1; as = { inherit x; y = as.x; }; in as.y"); - doTest(state, "let x = 1; as = rec { inherit x; y = x; }; in as.y"); - doTest(state, "let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); - doTest(state, "let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); - doTest(state, "let x = 1; in let inherit x; in x"); - doTest(state, "with { x = 1; }; let inherit x; y = x; in y"); - doTest(state, "builtins.toXML 123"); - doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); - doTest(state, "builtins.attrNames { x = 1; y = 2; }"); - - state.printStats(); -} - - -void printHelp() -{ -} - - -string programId = "eval-test"; From d77331d32f33cc17398d3e1422d0114309ef62de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Batlle=20i=20Rossell?= Date: Sun, 25 Apr 2010 20:52:49 +0000 Subject: [PATCH 093/103] Fixing a typo in the nix-store manual, that could confuse a bit too much (--delete/--gc) --- doc/manual/nix-store.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/nix-store.xml b/doc/manual/nix-store.xml index 566c75bf3..10bb3eda5 100644 --- a/doc/manual/nix-store.xml +++ b/doc/manual/nix-store.xml @@ -342,7 +342,7 @@ $ nix-store --gc --max-freed $((100 * 1024 * 1024)) nix-store - + paths From c82782f9a5190c7489fb8e9bd6876b247b0de0bf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 27 Apr 2010 09:05:11 +0000 Subject: [PATCH 094/103] --- src/nix-env/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix-env/Makefile.am b/src/nix-env/Makefile.am index 0819a721c..7dfa7425a 100644 --- a/src/nix-env/Makefile.am +++ b/src/nix-env/Makefile.am @@ -1,6 +1,6 @@ bin_PROGRAMS = nix-env -nix_env_SOURCES = nix-env.cc profiles.cc user-env.cc profiles.hh help.txt +nix_env_SOURCES = nix-env.cc profiles.cc profiles.hh user-env.cc user-env.hh help.txt nix_env_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \ ../libstore/libstore.la ../libutil/libutil.la \ From 4bab25a28d32f0551ac20b8b9b33e79af5decf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= Date: Mon, 3 May 2010 13:13:38 +0000 Subject: [PATCH 095/103] buildenv: Special-case Python's `easy-install.pth' files. * corepkgs/buildenv/builder.pl.in (createLinks): Skip `easy-install.pth' files. Comment the hack. --- corepkgs/buildenv/builder.pl.in | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/corepkgs/buildenv/builder.pl.in b/corepkgs/buildenv/builder.pl.in index 9932ea577..4101f66a4 100755 --- a/corepkgs/buildenv/builder.pl.in +++ b/corepkgs/buildenv/builder.pl.in @@ -29,10 +29,16 @@ sub createLinks { $baseName =~ s/^.*\///g; # strip directory my $dstFile = "$dstDir/$baseName"; + # The files below are special-cased so that they don't show up + # in user profiles, either because they are useless, or + # because they would cause pointless collisions (e.g., each + # Python package brings its own + # `$out/lib/pythonX.Y/site-packages/easy-install.pth'.) # Urgh, hacky... - if ($srcFile =~ /\/propagated-build-inputs$/ || + if ($srcFile =~ /\/propagated-build-inputs$/ || $srcFile =~ /\/nix-support$/ || $srcFile =~ /\/perllocal.pod$/ || + $srcFile =~ /\/easy-install.pth$/ || $srcFile =~ /\/info\/dir$/ || $srcFile =~ /\/log$/) { From 4750065ada362bd46e85879975a3148e18df5b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= Date: Wed, 5 May 2010 20:46:41 +0000 Subject: [PATCH 096/103] buildenv: Special-case Python's `site.py' and `site.pyc'. * corepkgs/buildenv/builder.pl.in (createLinks): Skip `site.py' and `site.pyc' files. --- corepkgs/buildenv/builder.pl.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/corepkgs/buildenv/builder.pl.in b/corepkgs/buildenv/builder.pl.in index 4101f66a4..4a52df65e 100755 --- a/corepkgs/buildenv/builder.pl.in +++ b/corepkgs/buildenv/builder.pl.in @@ -39,6 +39,8 @@ sub createLinks { $srcFile =~ /\/nix-support$/ || $srcFile =~ /\/perllocal.pod$/ || $srcFile =~ /\/easy-install.pth$/ || + $srcFile =~ /\/site.py$/ || + $srcFile =~ /\/site.pyc$/ || $srcFile =~ /\/info\/dir$/ || $srcFile =~ /\/log$/) { From 84ce7ac76feab6e9a5c074bd1b9550ae543d1db8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 6 May 2010 16:46:48 +0000 Subject: [PATCH 097/103] * Store attribute positions in the AST and report duplicate attribute errors with position info. * For all positions, use the position of the first character of the first token, rather than the last character of the first token plus one. --- src/libexpr/eval.cc | 16 +++--------- src/libexpr/lexer.l | 13 ++++++---- src/libexpr/nixexpr.cc | 13 ++++++---- src/libexpr/nixexpr.hh | 10 ++++++-- src/libexpr/parser.y | 55 ++++++++++++++++++++---------------------- src/nix-env/nix-env.cc | 5 ++-- 6 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b4f12c8a9..cb124ab8b 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -192,11 +192,6 @@ LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos)) throw AssertionError(format(s) % pos); } -LocalNoInline(void addErrorPrefix(Error & e, const char * s)) -{ - e.addPrefix(s); -} - LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2)) { e.addPrefix(format(s) % s2); @@ -207,11 +202,6 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const Pos & pos)) e.addPrefix(format(s) % pos); } -LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const string & s3)) -{ - e.addPrefix(format(s) % s2 % s3); -} - void mkString(Value & v, const char * s) { @@ -426,7 +416,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) foreach (Attrs::iterator, i, attrs) { Value & v2 = (*v.attrs)[i->first]; mkCopy(v2, env2.values[displ]); - mkThunk(env2.values[displ++], env2, i->second); + mkThunk(env2.values[displ++], env2, i->second.first); } /* The inherited attributes, on the other hand, are @@ -443,7 +433,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) else { foreach (Attrs::iterator, i, attrs) { Value & v2 = (*v.attrs)[i->first]; - mkThunk(v2, env, i->second); + mkThunk(v2, env, i->second.first); } foreach (list::iterator, i, inherited) { @@ -466,7 +456,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) /* The recursive attributes are evaluated in the new environment. */ foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) - mkThunk(env2.values[displ++], env2, i->second); + mkThunk(env2.values[displ++], env2, i->second.first); /* The inherited attributes, on the other hand, are evaluated in the original environment. */ diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 00de57a7b..f29f9b684 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -19,13 +19,16 @@ namespace nix { static void initLoc(YYLTYPE * loc) { - loc->first_line = 1; - loc->first_column = 1; + loc->first_line = loc->last_line = 1; + loc->first_column = loc->last_column = 1; } static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) { + loc->first_line = loc->last_line; + loc->first_column = loc->last_column; + while (len--) { switch (*s++) { case '\r': @@ -33,11 +36,11 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) s++; /* fall through */ case '\n': - ++loc->first_line; - loc->first_column = 1; + ++loc->last_line; + loc->last_column = 1; break; default: - ++loc->first_column; + ++loc->last_column; } } } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index a9c83108e..af0632a94 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -58,7 +58,7 @@ void ExprAttrs::show(std::ostream & str) foreach (list::iterator, i, inherited) str << "inherit " << i->name << "; "; foreach (Attrs::iterator, i, attrs) - str << i->first << " = " << *i->second << "; "; + str << i->first << " = " << *i->second.first << "; "; str << "}"; } @@ -94,7 +94,7 @@ void ExprLet::show(std::ostream & str) foreach (list::iterator, i, attrs->inherited) str << "inherit " << i->name << "; "; foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) - str << i->first << " = " << *i->second << "; "; + str << i->first << " = " << *i->second.first << "; "; str << "in " << *body; } @@ -138,6 +138,9 @@ std::ostream & operator << (std::ostream & str, const Pos & pos) } +Pos noPos; + + /* Computing levels/displacements for variables. */ void Expr::bindVars(const StaticEnv & env) @@ -218,12 +221,12 @@ void ExprAttrs::bindVars(const StaticEnv & env) } foreach (ExprAttrs::Attrs::iterator, i, attrs) - i->second->bindVars(newEnv); + i->second.first->bindVars(newEnv); } else { foreach (ExprAttrs::Attrs::iterator, i, attrs) - i->second->bindVars(env); + i->second.first->bindVars(env); foreach (list::iterator, i, inherited) i->bind(env); @@ -270,7 +273,7 @@ void ExprLet::bindVars(const StaticEnv & env) } foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) - i->second->bindVars(newEnv); + i->second.first->bindVars(newEnv); body->bindVars(newEnv); } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 2d328d382..36cb4e53c 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -23,8 +23,13 @@ struct Pos { string file; unsigned int line, column; + Pos() : line(0), column(0) { }; + Pos(const string & file, unsigned int line, unsigned int column) + : file(file), line(line), column(column) { }; }; +extern Pos noPos; + std::ostream & operator << (std::ostream & str, const Pos & pos); @@ -125,10 +130,11 @@ struct ExprOpHasAttr : Expr struct ExprAttrs : Expr { bool recursive; - typedef std::map Attrs; + typedef std::pair Attr; + typedef std::map Attrs; Attrs attrs; list inherited; - set attrNames; // used during parsing + std::map attrNames; // used during parsing ExprAttrs() : recursive(false) { }; COMMON_METHODS }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 66da76940..99980240f 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -61,18 +61,18 @@ static string showAttrPath(const vector & attrPath) } -static void dupAttr(const vector & attrPath, const Pos & pos) +static void dupAttr(const vector & attrPath, const Pos & pos, const Pos & prevPos) { - throw ParseError(format("attribute `%1%' at %2% already defined at ") - % showAttrPath(attrPath) % pos); + throw ParseError(format("attribute `%1%' at %2% already defined at %3%") + % showAttrPath(attrPath) % pos % prevPos); } -static void dupAttr(Symbol attr, const Pos & pos) +static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos) { vector attrPath; attrPath.push_back(attr); - throw ParseError(format("attribute `%1%' at %2% already defined at ") - % showAttrPath(attrPath) % pos); + throw ParseError(format("attribute `%1%' at %2% already defined at %3%") + % showAttrPath(attrPath) % pos % prevPos); } @@ -82,19 +82,20 @@ static void addAttr(ExprAttrs * attrs, const vector & attrPath, unsigned int n = 0; foreach (vector::const_iterator, i, attrPath) { n++; - if (attrs->attrs[*i]) { - ExprAttrs * attrs2 = dynamic_cast(attrs->attrs[*i]); - if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos); + ExprAttrs::Attrs::iterator j = attrs->attrs.find(*i); + if (j != attrs->attrs.end()) { + ExprAttrs * attrs2 = dynamic_cast(j->second.first); + if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.second); attrs = attrs2; } else { if (attrs->attrNames.find(*i) != attrs->attrNames.end()) - dupAttr(attrPath, pos); - attrs->attrNames.insert(*i); + dupAttr(attrPath, pos, attrs->attrNames[*i]); + attrs->attrNames[*i] = pos; if (n == attrPath.size()) - attrs->attrs[*i] = e; + attrs->attrs[*i] = ExprAttrs::Attr(e, pos); else { ExprAttrs * nested = new ExprAttrs; - attrs->attrs[*i] = nested; + attrs->attrs[*i] = ExprAttrs::Attr(nested, pos); attrs = nested; } } @@ -205,16 +206,12 @@ void backToString(yyscan_t scanner); void backToIndString(yyscan_t scanner); -static Pos makeCurPos(YYLTYPE * loc, ParseData * data) +static Pos makeCurPos(const YYLTYPE & loc, ParseData * data) { - Pos pos; - pos.file = data->path; - pos.line = loc->first_line; - pos.column = loc->first_column; - return pos; + return Pos(data->path, loc.first_line, loc.first_column); } -#define CUR_POS makeCurPos(yylocp, data) +#define CUR_POS makeCurPos(*yylocp, data) } @@ -223,7 +220,7 @@ static Pos makeCurPos(YYLTYPE * loc, ParseData * data) void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error) { data->error = (format("%1%, at %2%") - % error % makeCurPos(loc, data)).str(); + % error % makeCurPos(*loc, data)).str(); } @@ -374,14 +371,14 @@ ind_string_parts ; binds - : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, CUR_POS); } + : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); } | binds INHERIT ids ';' { $$ = $1; foreach (vector::iterator, i, *$3) { if ($$->attrNames.find(*i) != $$->attrNames.end()) - dupAttr(*i, CUR_POS); + dupAttr(*i, makeCurPos(@3, data), $$->attrNames[*i]); $$->inherited.push_back(*i); - $$->attrNames.insert(*i); + $$->attrNames[*i] = makeCurPos(@3, data); } } | binds INHERIT '(' expr ')' ids ';' @@ -389,11 +386,11 @@ binds /* !!! Should ensure sharing of the expression in $4. */ foreach (vector::iterator, i, *$6) { if ($$->attrNames.find(*i) != $$->attrNames.end()) - dupAttr(*i, CUR_POS); - $$->attrs[*i] = new ExprSelect($4, *i); - $$->attrNames.insert(*i); - } - } + dupAttr(*i, makeCurPos(@6, data), $$->attrNames[*i]); + $$->attrs[*i] = ExprAttrs::Attr(new ExprSelect($4, *i), makeCurPos(@6, data)); + $$->attrNames[*i] = makeCurPos(@6, data); + }} + | { $$ = new ExprAttrs; } ; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 4a9df454d..12a256a09 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -132,7 +132,7 @@ static void getAllExprs(EvalState & state, if (hasSuffix(attrName, ".nix")) attrName = string(attrName, 0, attrName.size() - 4); attrs.attrs[state.symbols.create(attrName)] = - parseExprFromFile(state, absPath(path2)); + ExprAttrs::Attr(parseExprFromFile(state, absPath(path2)), noPos); } else /* `path2' is a directory (with no default.nix in it); @@ -153,7 +153,8 @@ static Expr * loadSourceExpr(EvalState & state, const Path & path) for a user to have a ~/.nix-defexpr directory that includes some system-wide directory). */ ExprAttrs * attrs = new ExprAttrs; - attrs->attrs[state.symbols.create("_combineChannels")] = new ExprList(); + attrs->attrs[state.symbols.create("_combineChannels")] = + ExprAttrs::Attr(new ExprList(), noPos); getAllExprs(state, path, *attrs); return attrs; } From e2d5e40f4fdd98e5d2ad7b77c00fb97caa3aa259 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 7 May 2010 12:11:05 +0000 Subject: [PATCH 098/103] * Keep track of the source positions of attributes. --- src/libexpr/attr-path.cc | 2 +- src/libexpr/common-opts.cc | 2 +- src/libexpr/eval.cc | 59 ++++++++++++++++++++------------- src/libexpr/eval.hh | 11 ++++++- src/libexpr/get-drvs.cc | 32 +++++++++--------- src/libexpr/primops.cc | 66 ++++++++++++++++++++----------------- src/libexpr/value-to-xml.cc | 11 ++++--- src/nix-env/user-env.cc | 20 +++++------ 8 files changed, 117 insertions(+), 86 deletions(-) diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 769acb6b8..0660ba1c1 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -48,7 +48,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath, Bindings::iterator a = v.attrs->find(state.symbols.create(attr)); if (a == v.attrs->end()) throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath); - v = a->second; + v = a->second.value; } else if (apType == apIndex) { diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc index a25317de1..5a4856568 100644 --- a/src/libexpr/common-opts.cc +++ b/src/libexpr/common-opts.cc @@ -20,7 +20,7 @@ bool parseOptionArg(const string & arg, Strings::iterator & i, if (i == argsEnd) throw error; string value = *i++; - Value & v(autoArgs[state.symbols.create(name)]); + Value & v(autoArgs[state.symbols.create(name)].value); if (arg == "--arg") state.mkThunk_( v, parseExprFromString(state, value, absPath("."))); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index cb124ab8b..b8ec410f3 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -44,7 +44,7 @@ std::ostream & operator << (std::ostream & str, Value & v) case tAttrs: str << "{ "; foreach (Bindings::iterator, i, *v.attrs) - str << (string) i->first << " = " << i->second << "; "; + str << (string) i->first << " = " << i->second.value << "; "; str << "}"; break; case tList: @@ -128,7 +128,7 @@ void EvalState::addConstant(const string & name, Value & v) staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; baseEnv.values[baseEnvDispl++] = v; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; - (*baseEnv.values[0].attrs)[symbols.create(name2)] = v; + (*baseEnv.values[0].attrs)[symbols.create(name2)].value = v; } @@ -143,7 +143,7 @@ void EvalState::addPrimOp(const string & name, v.primOp.name = strdup(name2.c_str()); staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; baseEnv.values[baseEnvDispl++] = v; - (*baseEnv.values[0].attrs)[symbols.create(name2)] = v; + (*baseEnv.values[0].attrs)[symbols.create(name2)].value = v; } @@ -202,6 +202,11 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const Pos & pos)) e.addPrefix(format(s) % pos); } +LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos)) +{ + e.addPrefix(format(s) % s2 % pos); +} + void mkString(Value & v, const char * s) { @@ -239,7 +244,7 @@ Value * EvalState::lookupVar(Env * env, const VarRef & var) while (1) { Bindings::iterator j = env->values[0].attrs->find(var.name); if (j != env->values[0].attrs->end()) - return &j->second; + return &j->second.value; if (env->prevWith == 0) throwEvalError("undefined variable `%1%'", var.name); for (unsigned int l = env->prevWith; l; --l, env = env->up) ; @@ -290,8 +295,11 @@ void EvalState::mkThunk_(Value & v, Expr * expr) void EvalState::cloneAttrs(Value & src, Value & dst) { mkAttrs(dst); - foreach (Bindings::iterator, i, *src.attrs) - mkCopy((*dst.attrs)[i->first], i->second); + foreach (Bindings::iterator, i, *src.attrs) { + Attr & a = (*dst.attrs)[i->first]; + mkCopy(a.value, i->second.value); + a.pos = i->second.pos; + } } @@ -414,15 +422,16 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) /* The recursive attributes are evaluated in the new environment. */ foreach (Attrs::iterator, i, attrs) { - Value & v2 = (*v.attrs)[i->first]; - mkCopy(v2, env2.values[displ]); + nix::Attr & a = (*v.attrs)[i->first]; + mkCopy(a.value, env2.values[displ]); mkThunk(env2.values[displ++], env2, i->second.first); + a.pos = &i->second.second; } /* The inherited attributes, on the other hand, are evaluated in the original environment. */ foreach (list::iterator, i, inherited) { - Value & v2 = (*v.attrs)[i->name]; + Value & v2 = (*v.attrs)[i->name].value; Value * v3 = state.lookupVar(&env, *i); mkCopy(v2, *v3); mkCopy(env2.values[displ++], *v3); @@ -432,12 +441,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) else { foreach (Attrs::iterator, i, attrs) { - Value & v2 = (*v.attrs)[i->first]; - mkThunk(v2, env, i->second.first); + nix::Attr & a = (*v.attrs)[i->first]; + mkThunk(a.value, env, i->second.first); + a.pos = &i->second.second; } foreach (list::iterator, i, inherited) { - Value & v2 = (*v.attrs)[i->name]; + Value & v2 = (*v.attrs)[i->name].value; mkCopy(v2, *state.lookupVar(&env, *i)); } } @@ -494,12 +504,13 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) if (i == v2.attrs->end()) throwEvalError("attribute `%1%' missing", name); try { - state.forceValue(i->second); + state.forceValue(i->second.value); } catch (Error & e) { - addErrorPrefix(e, "while evaluating the attribute `%1%':\n", name); + addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n", + name, *i->second.pos); throw; } - v = i->second; + v = i->second.value; } @@ -601,7 +612,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) mkThunk(env2.values[displ++], env2, i->def); } else { attrsUsed++; - mkCopy(env2.values[displ++], j->second); + mkCopy(env2.values[displ++], j->second.value); } } @@ -721,8 +732,11 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) state.evalAttrs(env, e2, v2); - foreach (Bindings::iterator, i, *v2.attrs) - mkCopy((*v.attrs)[i->first], i->second); + foreach (Bindings::iterator, i, *v2.attrs) { + Attr & a = (*v.attrs)[i->first]; + mkCopy(a.value, i->second.value); + a.pos = i->second.pos; + } } @@ -802,7 +816,7 @@ void EvalState::strictForceValue(Value & v) if (v.type == tAttrs) { foreach (Bindings::iterator, i, *v.attrs) - strictForceValue(i->second); + strictForceValue(i->second.value); } else if (v.type == tList) { @@ -887,7 +901,7 @@ bool EvalState::isDerivation(Value & v) { if (v.type != tAttrs) return false; Bindings::iterator i = v.attrs->find(sType); - return i != v.attrs->end() && forceStringNoCtx(i->second) == "derivation"; + return i != v.attrs->end() && forceStringNoCtx(i->second.value) == "derivation"; } @@ -933,7 +947,7 @@ string EvalState::coerceToString(Value & v, PathSet & context, Bindings::iterator i = v.attrs->find(sOutPath); if (i == v.attrs->end()) throwTypeError("cannot coerce an attribute set (except a derivation) to a string"); - return coerceToString(i->second, context, coerceMore, copyToStore); + return coerceToString(i->second.value, context, coerceMore, copyToStore); } if (coerceMore) { @@ -1021,7 +1035,8 @@ bool EvalState::eqValues(Value & v1, Value & v2) if (v1.attrs->size() != v2.attrs->size()) return false; Bindings::iterator i, j; for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) - if (i->first != j->first || !eqValues(i->second, j->second)) return false; + if (i->first != j->first || !eqValues(i->second.value, j->second.value)) + return false; return true; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 6912e2288..1a9862c29 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -14,8 +14,9 @@ class Hash; class EvalState; struct Env; struct Value; +struct Attr; -typedef std::map Bindings; +typedef std::map Bindings; typedef enum { @@ -111,6 +112,14 @@ struct Env }; +struct Attr +{ + Value value; + Pos * pos; + Attr() : pos(&noPos) { }; +}; + + static inline void mkInt(Value & v, int n) { v.type = tInt; diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index e0ad91d8a..82a92416b 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -10,7 +10,7 @@ string DrvInfo::queryDrvPath(EvalState & state) const if (drvPath == "" && attrs) { Bindings::iterator i = attrs->find(state.sDrvPath); PathSet context; - (string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; + (string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : ""; } return drvPath; } @@ -21,7 +21,7 @@ string DrvInfo::queryOutPath(EvalState & state) const if (outPath == "" && attrs) { Bindings::iterator i = attrs->find(state.sOutPath); PathSet context; - (string &) outPath = i != attrs->end() ? state.coerceToPath(i->second, context) : ""; + (string &) outPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : ""; } return outPath; } @@ -36,21 +36,21 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const Bindings::iterator a = attrs->find(state.sMeta); if (a == attrs->end()) return meta; /* fine, empty meta information */ - state.forceAttrs(a->second); + state.forceAttrs(a->second.value); - foreach (Bindings::iterator, i, *a->second.attrs) { + foreach (Bindings::iterator, i, *a->second.value.attrs) { MetaValue value; - state.forceValue(i->second); - if (i->second.type == tString) { + state.forceValue(i->second.value); + if (i->second.value.type == tString) { value.type = MetaValue::tpString; - value.stringValue = i->second.string.s; - } else if (i->second.type == tInt) { + value.stringValue = i->second.value.string.s; + } else if (i->second.value.type == tInt) { value.type = MetaValue::tpInt; - value.intValue = i->second.integer; - } else if (i->second.type == tList) { + value.intValue = i->second.value.integer; + } else if (i->second.value.type == tList) { value.type = MetaValue::tpStrings; - for (unsigned int j = 0; j < i->second.list.length; ++j) - value.stringValues.push_back(state.forceStringNoCtx(*i->second.list.elems[j])); + for (unsigned int j = 0; j < i->second.value.list.length; ++j) + value.stringValues.push_back(state.forceStringNoCtx(*i->second.value.list.elems[j])); } else continue; ((MetaInfo &) meta)[i->first] = value; } @@ -99,13 +99,13 @@ static bool getDerivation(EvalState & state, Value & v, Bindings::iterator i = v.attrs->find(state.sName); /* !!! We really would like to have a decent back trace here. */ if (i == v.attrs->end()) throw TypeError("derivation name missing"); - drv.name = state.forceStringNoCtx(i->second); + drv.name = state.forceStringNoCtx(i->second.value); i = v.attrs->find(state.sSystem); if (i == v.attrs->end()) drv.system = "unknown"; else - drv.system = state.forceStringNoCtx(i->second); + drv.system = state.forceStringNoCtx(i->second.value); drv.attrs = v.attrs; @@ -168,7 +168,7 @@ static void getDerivations(EvalState & state, Value & vIn, foreach (SortedSymbols::iterator, i, attrs) { startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first); string pathPrefix2 = addToPath(pathPrefix, i->first); - Value & v2((*v.attrs)[i->second]); + Value & v2((*v.attrs)[i->second].value); if (combineChannels) getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); else if (getDerivation(state, v2, pathPrefix2, drvs, done)) { @@ -178,7 +178,7 @@ static void getDerivations(EvalState & state, Value & vIn, attribute. */ if (v2.type == tAttrs) { Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations")); - if (j != v2.attrs->end() && state.forceBool(j->second)) + if (j != v2.attrs->end() && state.forceBool(j->second.value)) getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); } } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9023d2b1b..981f87ce8 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -115,18 +115,18 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v) args[0]->attrs->find(state.symbols.create("startSet")); if (startSet == args[0]->attrs->end()) throw EvalError("attribute `startSet' required"); - state.forceList(startSet->second); + state.forceList(startSet->second.value); list workSet; - for (unsigned int n = 0; n < startSet->second.list.length; ++n) - workSet.push_back(startSet->second.list.elems[n]); + for (unsigned int n = 0; n < startSet->second.value.list.length; ++n) + workSet.push_back(startSet->second.value.list.elems[n]); /* Get the operator. */ Bindings::iterator op = args[0]->attrs->find(state.symbols.create("operator")); if (op == args[0]->attrs->end()) throw EvalError("attribute `operator' required"); - state.forceValue(op->second); + state.forceValue(op->second.value); /* Construct the closure by applying the operator to element of `workSet', adding the result to `workSet', continuing until @@ -143,15 +143,15 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v) e->attrs->find(state.symbols.create("key")); if (key == e->attrs->end()) throw EvalError("attribute `key' required"); - state.forceValue(key->second); + state.forceValue(key->second.value); - if (doneKeys.find(key->second) != doneKeys.end()) continue; - doneKeys.insert(key->second); + if (doneKeys.find(key->second.value) != doneKeys.end()) continue; + doneKeys.insert(key->second.value); res.push_back(*e); /* Call the `operator' function with `e' as argument. */ Value call; - mkApp(call, op->second, *e); + mkApp(call, op->second.value, *e); state.forceList(call); /* Add the values returned by the operator to the work set. */ @@ -323,11 +323,11 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) if (attr == args[0]->attrs->end()) throw EvalError("required attribute `name' missing"); string drvName; + Pos & posDrvName(*attr->second.pos); try { - drvName = state.forceStringNoCtx(attr->second); + drvName = state.forceStringNoCtx(attr->second.value); } catch (Error & e) { - e.addPrefix(format("while evaluating the derivation attribute `name' at :\n")); - // !!! % showPos(posDrvName)); + e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n") % posDrvName); throw; } @@ -348,9 +348,9 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) /* The `args' attribute is special: it supplies the command-line arguments to the builder. */ if (key == "args") { - state.forceList(i->second); - for (unsigned int n = 0; n < i->second.list.length; ++n) { - string s = state.coerceToString(*i->second.list.elems[n], context, true); + state.forceList(i->second.value); + for (unsigned int n = 0; n < i->second.value.list.length; ++n) { + string s = state.coerceToString(*i->second.value.list.elems[n], context, true); drv.args.push_back(s); } } @@ -358,7 +358,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) /* All other attributes are passed to the builder through the environment. */ else { - string s = state.coerceToString(i->second, context, true); + string s = state.coerceToString(i->second.value, context, true); drv.env[key] = s; if (key == "builder") drv.builder = s; else if (i->first == state.sSystem) drv.platform = s; @@ -373,10 +373,10 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) } } catch (Error & e) { - e.addPrefix(format("while evaluating the derivation attribute `%1%' at :\n") - % key /* !!! % showPos(pos) */); - e.addPrefix(format("while instantiating the derivation named `%1%' at :\n") - % drvName /* !!! % showPos(posDrvName) */); + e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n") + % key % *i->second.pos); + e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n") + % drvName % posDrvName); throw; } } @@ -487,8 +487,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) /* !!! assumes a single output */ state.mkAttrs(v); - mkString((*v.attrs)[state.sOutPath], outPath, singleton(drvPath)); - mkString((*v.attrs)[state.sDrvPath], drvPath, singleton("=" + drvPath)); + mkString((*v.attrs)[state.sOutPath].value, outPath, singleton(drvPath)); + mkString((*v.attrs)[state.sDrvPath].value, drvPath, singleton("=" + drvPath)); } @@ -711,8 +711,9 @@ static void prim_getAttr(EvalState & state, Value * * args, Value & v) Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); if (i == args[1]->attrs->end()) throw EvalError(format("attribute `%1%' missing") % attr); - state.forceValue(i->second); - v = i->second; + // !!! add to stack trace? + state.forceValue(i->second.value); + v = i->second.value; } @@ -764,13 +765,15 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v) Bindings::iterator j = v2.attrs->find(state.sName); if (j == v2.attrs->end()) throw TypeError("`name' attribute missing in a call to `listToAttrs'"); - string name = state.forceStringNoCtx(j->second); + string name = state.forceStringNoCtx(j->second.value); j = v2.attrs->find(state.symbols.create("value")); if (j == v2.attrs->end()) throw TypeError("`value' attribute missing in a call to `listToAttrs'"); - mkCopy((*v.attrs)[state.symbols.create(name)], j->second); + Attr & a = (*v.attrs)[state.symbols.create(name)]; + mkCopy(a.value, j->second.value); + a.pos = j->second.pos; } } @@ -787,8 +790,11 @@ static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v) foreach (Bindings::iterator, i, *args[1]->attrs) { Bindings::iterator j = args[0]->attrs->find(i->first); - if (j != args[0]->attrs->end()) - mkCopy((*v.attrs)[i->first], i->second); + if (j != args[0]->attrs->end()) { + Attr & a = (*v.attrs)[i->first]; + mkCopy(a.value, i->second.value); + a.pos = i->second.pos; + } } } @@ -817,7 +823,7 @@ static void prim_functionArgs(EvalState & state, Value * * args, Value & v) if (!args[0]->lambda.fun->matchAttrs) return; foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals) - mkBool((*v.attrs)[i->name], i->def); + mkBool((*v.attrs)[i->name].value, i->def); } @@ -1000,8 +1006,8 @@ static void prim_parseDrvName(EvalState & state, Value * * args, Value & v) string name = state.forceStringNoCtx(*args[0]); DrvName parsed(name); state.mkAttrs(v); - mkString((*v.attrs)[state.sName], parsed.name); - mkString((*v.attrs)[state.symbols.create("version")], parsed.version); + mkString((*v.attrs)[state.sName].value, parsed.name); + mkString((*v.attrs)[state.symbols.create("version")].value, parsed.version); } diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 58e89925c..821f715e2 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -28,7 +28,8 @@ static void showAttrs(EvalState & state, bool strict, Bindings & attrs, names.insert(i->first); foreach (StringSet::iterator, i, names) { XMLOpenElement _(doc, "attr", singletonAttrs("name", *i)); - printValueAsXML(state, strict, attrs[state.symbols.create(*i)], doc, context, drvsSeen); + printValueAsXML(state, strict, attrs[state.symbols.create(*i)].value, + doc, context, drvsSeen); } } @@ -71,12 +72,12 @@ static void printValueAsXML(EvalState & state, bool strict, Value & v, Path drvPath; a = v.attrs->find(state.sDrvPath); - if (a != v.attrs->end() && a->second.type == tString) - xmlAttrs["drvPath"] = drvPath = a->second.string.s; + if (a != v.attrs->end() && a->second.value.type == tString) + xmlAttrs["drvPath"] = drvPath = a->second.value.string.s; a = v.attrs->find(state.sOutPath); - if (a != v.attrs->end() && a->second.type == tString) - xmlAttrs["outPath"] = a->second.string.s; + if (a != v.attrs->end() && a->second.value.type == tString) + xmlAttrs["outPath"] = a->second.value.string.s; XMLOpenElement _(doc, "derivation", xmlAttrs); diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index a0e51cae1..72e13fceb 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -62,19 +62,19 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, manifest.list.elems[n++] = &v; state.mkAttrs(v); - mkString((*v.attrs)[state.sType], "derivation"); - mkString((*v.attrs)[state.sName], i->name); - mkString((*v.attrs)[state.sSystem], i->system); - mkString((*v.attrs)[state.sOutPath], i->queryOutPath(state)); + mkString((*v.attrs)[state.sType].value, "derivation"); + mkString((*v.attrs)[state.sName].value, i->name); + mkString((*v.attrs)[state.sSystem].value, i->system); + mkString((*v.attrs)[state.sOutPath].value, i->queryOutPath(state)); if (drvPath != "") - mkString((*v.attrs)[state.sDrvPath], i->queryDrvPath(state)); + mkString((*v.attrs)[state.sDrvPath].value, i->queryDrvPath(state)); - state.mkAttrs((*v.attrs)[state.sMeta]); + state.mkAttrs((*v.attrs)[state.sMeta].value); MetaInfo meta = i->queryMetaInfo(state); foreach (MetaInfo::const_iterator, j, meta) { - Value & v2((*(*v.attrs)[state.sMeta].attrs)[state.symbols.create(j->first)]); + Value & v2((*(*v.attrs)[state.sMeta].value.attrs)[state.symbols.create(j->first)].value); switch (j->second.type) { case MetaValue::tpInt: mkInt(v2, j->second.intValue); break; case MetaValue::tpString: mkString(v2, j->second.stringValue); break; @@ -116,10 +116,10 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, builder with the manifest as argument. */ Value args, topLevel; state.mkAttrs(args); - mkString((*args.attrs)[state.sSystem], thisSystem); - mkString((*args.attrs)[state.symbols.create("manifest")], + mkString((*args.attrs)[state.sSystem].value, thisSystem); + mkString((*args.attrs)[state.symbols.create("manifest")].value, manifestFile, singleton(manifestFile)); - (*args.attrs)[state.symbols.create("derivations")] = manifest; + (*args.attrs)[state.symbols.create("derivations")].value = manifest; mkApp(topLevel, envBuilder, args); /* Evaluate it. */ From 83d7b89660ebb942ad844b9be260492b2ea9fb24 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 7 May 2010 12:33:14 +0000 Subject: [PATCH 099/103] * Updated addErrorContext. --- src/libexpr/primops.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 981f87ce8..d33b0030f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -189,19 +189,20 @@ static void prim_throw(EvalState & state, Value * * args, Value & v) } -#if 0 static void prim_addErrorContext(EvalState & state, Value * * args, Value & v) { - PathSet context; try { - return evalExpr(state, args[1]); + state.forceValue(*args[1]); + v = *args[1]; } catch (Error & e) { - e.addPrefix(format("%1%\n") % - evalString(state, args[0], context)); + PathSet context; + e.addPrefix(format("%1%\n") % state.coerceToString(*args[0], context)); throw; } } + +#if 0 /* Try evaluating the argument. Success => {success=true; value=something;}, * else => {success=false; value=false;} */ static void prim_tryEval(EvalState & state, Value * * args, Value & v) @@ -1060,8 +1061,8 @@ void EvalState::createBaseEnv() addPrimOp("__genericClosure", 1, prim_genericClosure); addPrimOp("abort", 1, prim_abort); addPrimOp("throw", 1, prim_throw); -#if 0 addPrimOp("__addErrorContext", 2, prim_addErrorContext); +#if 0 addPrimOp("__tryEval", 1, prim_tryEval); #endif addPrimOp("__getEnv", 1, prim_getEnv); From 01e58adce0767f1a484d80fcbcf67c7945cbc146 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 7 May 2010 12:43:57 +0000 Subject: [PATCH 100/103] * Store position info for inherited attributes. --- src/libexpr/eval.cc | 22 ++++++++++++---------- src/libexpr/nixexpr.cc | 24 ++++++++++++------------ src/libexpr/nixexpr.hh | 3 ++- src/libexpr/parser.y | 5 +++-- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b8ec410f3..69632eb37 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -430,11 +430,12 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) /* The inherited attributes, on the other hand, are evaluated in the original environment. */ - foreach (list::iterator, i, inherited) { - Value & v2 = (*v.attrs)[i->name].value; - Value * v3 = state.lookupVar(&env, *i); - mkCopy(v2, *v3); - mkCopy(env2.values[displ++], *v3); + foreach (list::iterator, i, inherited) { + nix::Attr & a = (*v.attrs)[i->first.name]; + Value * v2 = state.lookupVar(&env, i->first); + mkCopy(a.value, *v2); + mkCopy(env2.values[displ++], *v2); + a.pos = &i->second; } } @@ -446,9 +447,10 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) a.pos = &i->second.second; } - foreach (list::iterator, i, inherited) { - Value & v2 = (*v.attrs)[i->name].value; - mkCopy(v2, *state.lookupVar(&env, *i)); + foreach (list::iterator, i, inherited) { + nix::Attr & a = (*v.attrs)[i->first.name]; + mkCopy(a.value, *state.lookupVar(&env, i->first)); + a.pos = &i->second; } } } @@ -470,8 +472,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) /* The inherited attributes, on the other hand, are evaluated in the original environment. */ - foreach (list::iterator, i, attrs->inherited) - mkCopy(env2.values[displ++], *state.lookupVar(&env, *i)); + foreach (list::iterator, i, attrs->inherited) + mkCopy(env2.values[displ++], *state.lookupVar(&env, i->first)); state.eval(env2, body, v); } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index af0632a94..898fdb609 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -55,8 +55,8 @@ void ExprAttrs::show(std::ostream & str) { if (recursive) str << "rec "; str << "{ "; - foreach (list::iterator, i, inherited) - str << "inherit " << i->name << "; "; + foreach (list::iterator, i, inherited) + str << "inherit " << i->first.name << "; "; foreach (Attrs::iterator, i, attrs) str << i->first << " = " << *i->second.first << "; "; str << "}"; @@ -91,8 +91,8 @@ void ExprLambda::show(std::ostream & str) void ExprLet::show(std::ostream & str) { str << "let "; - foreach (list::iterator, i, attrs->inherited) - str << "inherit " << i->name << "; "; + foreach (list::iterator, i, attrs->inherited) + str << "inherit " << i->first.name << "; "; foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) str << i->first << " = " << *i->second.first << "; "; str << "in " << *body; @@ -215,9 +215,9 @@ void ExprAttrs::bindVars(const StaticEnv & env) foreach (ExprAttrs::Attrs::iterator, i, attrs) newEnv.vars[i->first] = displ++; - foreach (list::iterator, i, inherited) { - newEnv.vars[i->name] = displ++; - i->bind(env); + foreach (list::iterator, i, inherited) { + newEnv.vars[i->first.name] = displ++; + i->first.bind(env); } foreach (ExprAttrs::Attrs::iterator, i, attrs) @@ -228,8 +228,8 @@ void ExprAttrs::bindVars(const StaticEnv & env) foreach (ExprAttrs::Attrs::iterator, i, attrs) i->second.first->bindVars(env); - foreach (list::iterator, i, inherited) - i->bind(env); + foreach (list::iterator, i, inherited) + i->first.bind(env); } } @@ -267,9 +267,9 @@ void ExprLet::bindVars(const StaticEnv & env) foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) newEnv.vars[i->first] = displ++; - foreach (list::iterator, i, attrs->inherited) { - newEnv.vars[i->name] = displ++; - i->bind(env); + foreach (list::iterator, i, attrs->inherited) { + newEnv.vars[i->first.name] = displ++; + i->first.bind(env); } foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 36cb4e53c..1c72441b2 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -131,9 +131,10 @@ struct ExprAttrs : Expr { bool recursive; typedef std::pair Attr; + typedef std::pair Inherited; typedef std::map Attrs; Attrs attrs; - list inherited; + list inherited; std::map attrNames; // used during parsing ExprAttrs() : recursive(false) { }; COMMON_METHODS diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 99980240f..7236bab19 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -377,8 +377,9 @@ binds foreach (vector::iterator, i, *$3) { if ($$->attrNames.find(*i) != $$->attrNames.end()) dupAttr(*i, makeCurPos(@3, data), $$->attrNames[*i]); - $$->inherited.push_back(*i); - $$->attrNames[*i] = makeCurPos(@3, data); + Pos pos = makeCurPos(@3, data); + $$->inherited.push_back(ExprAttrs::Inherited(*i, pos)); + $$->attrNames[*i] = pos; } } | binds INHERIT '(' expr ')' ids ';' From 1a8eb6e3ec9329ee7b61ac2345c6e1d994905813 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 7 May 2010 15:26:33 +0000 Subject: [PATCH 101/103] --- src/libexpr/value-to-xml.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index c3f352cfb..e751fd300 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -32,8 +32,10 @@ static void showAttrs(EvalState & state, bool strict, bool location, Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) { StringSet names; + foreach (Bindings::iterator, i, attrs) names.insert(i->first); + foreach (StringSet::iterator, i, names) { Attr & a(attrs[state.symbols.create(*i)]); From 81a4b4e49bf82f17eef20d78c4f505874cf5532e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 12 May 2010 11:23:44 +0000 Subject: [PATCH 102/103] * Implemented tryEval, the last missing primop in the fast-eval branch. Also added a test for tryEval. --- src/libexpr/primops.cc | 18 ++++++------------ tests/lang/eval-okay-tryeval.exp | 1 + tests/lang/eval-okay-tryeval.nix | 5 +++++ 3 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 tests/lang/eval-okay-tryeval.exp create mode 100644 tests/lang/eval-okay-tryeval.nix diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a7e7bcdc8..9d36fb6a0 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -202,24 +202,20 @@ static void prim_addErrorContext(EvalState & state, Value * * args, Value & v) } -#if 0 /* Try evaluating the argument. Success => {success=true; value=something;}, * else => {success=false; value=false;} */ static void prim_tryEval(EvalState & state, Value * * args, Value & v) { - ATermMap res = ATermMap(); + state.mkAttrs(v); try { - Expr val = evalExpr(state, args[0]); - res.set(toATerm("value"), makeAttrRHS(val, makeNoPos())); - res.set(toATerm("success"), makeAttrRHS(eTrue, makeNoPos())); + state.forceValue(*args[0]); + (*v.attrs)[state.symbols.create("value")].value = *args[0]; + mkBool((*v.attrs)[state.symbols.create("success")].value, true); } catch (AssertionError & e) { - printMsg(lvlDebug, format("tryEval caught an error: %1%: %2%") % e.prefix() % e.msg()); - res.set(toATerm("value"), makeAttrRHS(eFalse, makeNoPos())); - res.set(toATerm("success"), makeAttrRHS(eFalse, makeNoPos())); + mkBool((*v.attrs)[state.symbols.create("value")].value, false); + mkBool((*v.attrs)[state.symbols.create("success")].value, false); } - return makeAttrs(res); } -#endif /* Return an environment variable. Use with care. */ @@ -1062,9 +1058,7 @@ void EvalState::createBaseEnv() addPrimOp("abort", 1, prim_abort); addPrimOp("throw", 1, prim_throw); addPrimOp("__addErrorContext", 2, prim_addErrorContext); -#if 0 addPrimOp("__tryEval", 1, prim_tryEval); -#endif addPrimOp("__getEnv", 1, prim_getEnv); addPrimOp("__trace", 2, prim_trace); diff --git a/tests/lang/eval-okay-tryeval.exp b/tests/lang/eval-okay-tryeval.exp new file mode 100644 index 000000000..c2788b412 --- /dev/null +++ b/tests/lang/eval-okay-tryeval.exp @@ -0,0 +1 @@ +{ x = { value = "x"; success = true; }; y = { value = false; success = false; }; z = { value = false; success = false; }; } diff --git a/tests/lang/eval-okay-tryeval.nix b/tests/lang/eval-okay-tryeval.nix new file mode 100644 index 000000000..629bc440a --- /dev/null +++ b/tests/lang/eval-okay-tryeval.nix @@ -0,0 +1,5 @@ +{ + x = builtins.tryEval "x"; + y = builtins.tryEval (assert false; "y"); + z = builtins.tryEval (throw "bla"); +} From bd25ac2260267abd2181324e1650820da70e5e60 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 12 May 2010 12:15:49 +0000 Subject: [PATCH 103/103] * Print attributes in sorted order. --- src/libexpr/eval.cc | 9 +++++++-- tests/lang/eval-okay-tryeval.exp | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 69632eb37..26739faf6 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -41,12 +41,17 @@ std::ostream & operator << (std::ostream & str, Value & v) case tNull: str << "true"; break; - case tAttrs: + case tAttrs: { str << "{ "; + typedef std::map Sorted; + Sorted sorted; foreach (Bindings::iterator, i, *v.attrs) - str << (string) i->first << " = " << i->second.value << "; "; + sorted[i->first] = &i->second.value; + foreach (Sorted::iterator, i, sorted) + str << i->first << " = " << *i->second << "; "; str << "}"; break; + } case tList: str << "[ "; for (unsigned int n = 0; n < v.list.length; ++n) diff --git a/tests/lang/eval-okay-tryeval.exp b/tests/lang/eval-okay-tryeval.exp index c2788b412..2b2e6fa71 100644 --- a/tests/lang/eval-okay-tryeval.exp +++ b/tests/lang/eval-okay-tryeval.exp @@ -1 +1 @@ -{ x = { value = "x"; success = true; }; y = { value = false; success = false; }; z = { value = false; success = false; }; } +{ x = { success = true; value = "x"; }; y = { success = false; value = false; }; z = { success = false; value = false; }; }