diff options
-rw-r--r-- | src/libexpr/eval-test.cc | 4 | ||||
-rw-r--r-- | src/libexpr/eval.cc | 138 | ||||
-rw-r--r-- | src/libexpr/eval.hh | 38 | ||||
-rw-r--r-- | 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 d37014a731ca..8aade1298f3d 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 794e3966029f..86484031b1e5 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 8ca997f1404b..4706602d59fa 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<Path, PathSet> DrvRoots; typedef std::map<Path, Hash> 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 a24f40be67e0..2815567e5d15 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -14,6 +14,7 @@ #include <unistd.h> #include <algorithm> +#include <cstring> 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(<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<PathSet>(*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); |