From fc92244ba81d884e099d467a3b82fbdcbff7fc40 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 7 Apr 2010 13:55:46 +0000 Subject: [PATCH] * 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 3399aedc4c..eac95094fd 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 5f6ab2655a..0d18c55227 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 ae6b2106f4..a42b9ebeba 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 9b3062804e..ad68739c39 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 36b8e40424..8f094185c4 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 f02392bed5..6e651d77f0 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 98a31dc3a5..5fa2418b5f 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 a71998de24..2f41fa81b5 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 {