From 47df476daa568af9f645b6a039c028e602a7e44b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Mar 2010 18:05:54 +0000 Subject: [PATCH] * 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) {