* Implemented the primops necessary for generating the NixOS manual.

This commit is contained in:
Eelco Dolstra 2010-04-07 13:55:46 +00:00
parent a353aef0b1
commit fc92244ba8
8 changed files with 160 additions and 146 deletions

View file

@ -16,7 +16,8 @@ void doTest(string s)
Expr e = parseExprFromString(state, s, absPath(".")); Expr e = parseExprFromString(state, s, absPath("."));
printMsg(lvlError, format(">>>>> %1%") % e); printMsg(lvlError, format(">>>>> %1%") % e);
Value v; Value v;
state.strictEval(e, v); state.eval(e, v);
state.strictForceValue(v);
printMsg(lvlError, format("result: %1%") % 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 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 = { 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("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 ]; }");
} }

View file

@ -302,6 +302,8 @@ void EvalState::eval(Env & env, Expr e, Value & v)
//debug(format("eval: %1%") % e); //debug(format("eval: %1%") % e);
checkInterrupt();
nrEvaluated++; nrEvaluated++;
Sym name; 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) void EvalState::forceValue(Value & v)
{ {
if (v.type == tThunk) { 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) int EvalState::forceInt(Value & v)
{ {
forceValue(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, string EvalState::coerceToString(Value & v, PathSet & context,
bool coerceMore, bool copyToStore) bool coerceMore, bool copyToStore)
{ {
@ -769,7 +773,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
if (!copyToStore) return path; if (!copyToStore) return path;
if (isDerivation(path)) if (nix::isDerivation(path))
throw EvalError(format("file names are not allowed to end in `%1%'") throw EvalError(format("file names are not allowed to end in `%1%'")
% drvExtension); % drvExtension);

View file

@ -169,17 +169,16 @@ public:
type. */ type. */
bool evalBool(Env & env, Expr e); 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 /* 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 of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */ result. Otherwise, this is a no-op. */
void forceValue(Value & v); 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. */ /* Force `v', and then verify that it has the expected type. */
int forceInt(Value & v); int forceInt(Value & v);
bool forceBool(Value & v); bool forceBool(Value & v);
@ -190,6 +189,10 @@ public:
string forceString(Value & v, PathSet & context); string forceString(Value & v, PathSet & context);
string forceStringNoCtx(Value & v); 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 coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers, string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set, booleans and lists to a string. If `copyToStore' is set,
@ -219,10 +222,10 @@ private:
elements and attributes are compared recursively. */ elements and attributes are compared recursively. */
bool eqValues(Value & v1, Value & v2); bool eqValues(Value & v1, Value & v2);
void callFunction(Value & fun, Value & arg, Value & v);
public: public:
void callFunction(Value & fun, Value & arg, Value & v);
/* Allocation primitives. */ /* Allocation primitives. */
Value * allocValues(unsigned int count); Value * allocValues(unsigned int count);
Env & allocEnv(); Env & allocEnv();
@ -237,6 +240,10 @@ public:
}; };
/* Return a string representing the type of the value `v'. */
string showType(Value & v);
#if 0 #if 0
/* Evaluate an expression to normal form. */ /* Evaluate an expression to normal form. */
Expr evalExpr(EvalState & state, Expr e); Expr evalExpr(EvalState & state, Expr e);

View file

@ -18,24 +18,19 @@ static XMLAttrs singletonAttrs(const string & name, const string & value)
} }
/* set<Expr> is safe because all the expressions are also reachable static void printValueAsXML(EvalState & state, bool strict, Value & v,
from the stack, therefore can't be garbage-collected. */ XMLWriter & doc, PathSet & context, PathSet & drvsSeen);
typedef set<Expr> ExprSet;
static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context, static void showAttrs(EvalState & state, bool strict, Bindings & attrs,
ExprSet & drvsSeen); XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
static void showAttrs(const ATermMap & attrs, XMLWriter & doc,
PathSet & context, ExprSet & drvsSeen)
{ {
StringSet names; StringSet names;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) foreach (Bindings::iterator, i, attrs)
names.insert(aterm2String(i->key)); names.insert(aterm2String(i->first));
for (StringSet::iterator i = names.begin(); i != names.end(); ++i) { foreach (StringSet::iterator, i, names) {
XMLOpenElement _(doc, "attr", singletonAttrs("name", *i)); 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, static void printValueAsXML(EvalState & state, bool strict, Value & v,
ExprSet & drvsSeen) XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
{ {
XMLAttrs attrs;
string s;
ATerm s2;
int i;
ATermList as, es;
ATerm pat, body, pos;
checkInterrupt(); checkInterrupt();
if (matchStr(e, s, context)) /* !!! show the context? */ if (strict) state.forceValue(v);
doc.writeEmptyElement("string", singletonAttrs("value", s));
else if (matchPath(e, s2)) switch (v.type) {
doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s2)));
else if (matchNull(e)) case tInt:
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str()));
break;
case tBool:
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
break;
case tString:
/* !!! show the context? */
doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
break;
case tPath:
doc.writeEmptyElement("path", singletonAttrs("value", v.path));
break;
case tNull:
doc.writeEmptyElement("null"); doc.writeEmptyElement("null");
break;
else if (matchInt(e, i)) case tAttrs:
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % i).str())); if (state.isDerivation(v)) {
else if (e == eTrue)
doc.writeEmptyElement("bool", singletonAttrs("value", "true"));
else if (e == eFalse)
doc.writeEmptyElement("bool", singletonAttrs("value", "false"));
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; XMLAttrs xmlAttrs;
Path outPath, drvPath;
a = attrs.get(toATerm("drvPath")); Bindings::iterator a = v.attrs->find(toATerm("derivation"));
if (matchStr(a, drvPath, context))
xmlAttrs["drvPath"] = drvPath;
a = attrs.get(toATerm("outPath")); Path drvPath;
if (matchStr(a, outPath, context)) a = v.attrs->find(toATerm("drvPath"));
xmlAttrs["outPath"] = outPath; 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); XMLOpenElement _(doc, "derivation", xmlAttrs);
if (drvsSeen.find(e) == drvsSeen.end()) { if (drvPath != "" && drvsSeen.find(drvPath) == drvsSeen.end()) {
drvsSeen.insert(e); drvsSeen.insert(drvPath);
showAttrs(attrs, doc, context, drvsSeen); showAttrs(state, strict, *v.attrs, doc, context, drvsSeen);
} else } else
doc.writeEmptyElement("repeated"); doc.writeEmptyElement("repeated");
} }
else { else {
XMLOpenElement _(doc, "attrs"); XMLOpenElement _(doc, "attrs");
showAttrs(attrs, doc, context, drvsSeen); showAttrs(state, strict, *v.attrs, doc, context, drvsSeen);
}
} }
else if (matchList(e, es)) { break;
case tList: {
XMLOpenElement _(doc, "list"); XMLOpenElement _(doc, "list");
for (ATermIterator i(es); i; ++i) for (unsigned int n = 0; n < v.list.length; ++n)
printTermAsXML(*i, doc, context, drvsSeen); printValueAsXML(state, strict, v.list.elems[n], doc, context, drvsSeen);
break;
} }
else if (matchFunction(e, pat, body, pos)) { case tLambda: {
XMLOpenElement _(doc, "function"); XMLOpenElement _(doc, "function");
printPatternAsXML(pat, doc); printPatternAsXML(v.lambda.pat, doc);
break;
} }
else default:
doc.writeEmptyElement("unevaluated"); 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); XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr"); XMLOpenElement root(doc, "expr");
ExprSet drvsSeen; PathSet drvsSeen;
printTermAsXML(e, doc, context, drvsSeen); printValueAsXML(state, strict, v, doc, context, drvsSeen);
} }

View file

@ -5,11 +5,12 @@
#include <map> #include <map>
#include "nixexpr.hh" #include "nixexpr.hh"
#include "aterm.hh" #include "eval.hh"
namespace nix { namespace nix {
void printTermAsXML(Expr e, std::ostream & out, PathSet & context); void printValueAsXML(EvalState & state, bool strict,
Value & v, std::ostream & out, PathSet & context);
} }

View file

@ -105,10 +105,7 @@ static bool getDerivation(EvalState & state, Value & v,
{ {
try { try {
state.forceValue(v); state.forceValue(v);
if (v.type != tAttrs) return true; if (!state.isDerivation(v)) 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 /* Remove spurious duplicates (e.g., an attribute set like
`rec { x = derivation {...}; y = x;}'. */ `rec { x = derivation {...}; y = x;}'. */
@ -117,7 +114,7 @@ static bool getDerivation(EvalState & state, Value & v,
DrvInfo drv; 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. */ /* !!! We really would like to have a decent back trace here. */
if (i == v.attrs->end()) throw TypeError("derivation name missing"); if (i == v.attrs->end()) throw TypeError("derivation name missing");
drv.name = state.forceStringNoCtx(i->second); drv.name = state.forceStringNoCtx(i->second);

View file

@ -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 /* Convert the argument (which can be any Nix expression) to an XML
representation returned in a string. Not all Nix expressions can representation returned in a string. Not all Nix expressions can
be sensibly or completely represented (e.g., functions). */ 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; std::ostringstream out;
PathSet context; PathSet context;
printTermAsXML(strictEvalExpr(state, args[0]), out, context); printValueAsXML(state, true, *args[0], out, context);
return makeStr(out.str(), context); mkString(v, out.str(), context);
} }
#endif
/* Store a string in the Nix store as a source file that can be used /* 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 struct FilterFromExpr : PathFilter
{ {
EvalState & state; EvalState & state;
Expr filter; Value & filter;
FilterFromExpr(EvalState & state, Expr filter) FilterFromExpr(EvalState & state, Value & filter)
: state(state), filter(filter) : state(state), filter(filter)
{ {
} }
@ -599,17 +596,25 @@ struct FilterFromExpr : PathFilter
if (lstat(path.c_str(), &st)) if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % path); throw SysError(format("getting attributes of path `%1%'") % path);
Expr call = /* Call the filter function. The first argument is the path,
makeCall( the second is a string indicating the type of the file. */
makeCall(filter, makeStr(path)), Value arg1;
makeStr( mkString(arg1, path);
Value fun2;
state.callFunction(filter, arg1, fun2);
Value arg2;
mkString(arg2,
S_ISREG(st.st_mode) ? "regular" : S_ISREG(st.st_mode) ? "regular" :
S_ISDIR(st.st_mode) ? "directory" : S_ISDIR(st.st_mode) ? "directory" :
S_ISLNK(st.st_mode) ? "symlink" : S_ISLNK(st.st_mode) ? "symlink" :
"unknown" /* not supported, will fail! */ "unknown" /* not supported, will fail! */);
));
return evalBool(state, call); 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) static void prim_filterSource(EvalState & state, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
Path path = coerceToPath(state, args[1], context); Path path = state.coerceToPath(*args[1], context);
if (!context.empty()) if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path); 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 Path dstPath = readOnlyMode
? computeStorePathForPath(path, true, htSHA256, filter).first ? computeStorePathForPath(path, true, htSHA256, filter).first
: store->addToStore(path, true, htSHA256, filter); : store->addToStore(path, true, htSHA256, filter);
return makeStr(dstPath, singleton<PathSet>(dstPath)); mkString(v, dstPath, singleton<PathSet>(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) static void prim_unsafeDiscardStringContext(EvalState & state, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = coerceToString(state, args[0], context); string s = state.coerceToString(*args[0], context);
return makeStr(s, PathSet()); mkString(v, s, PathSet());
} }
#if 0
/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a /* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
builder without causing the derivation to be built (for instance, builder without causing the derivation to be built (for instance,
in the derivation that builds NARs in nix-push, when doing in the derivation that builds NARs in nix-push, when doing
@ -1053,13 +1061,9 @@ void EvalState::createBaseEnv()
addPrimOp("__readFile", 1, prim_readFile); addPrimOp("__readFile", 1, prim_readFile);
// Creating files // Creating files
#if 0
addPrimOp("__toXML", 1, prim_toXML); addPrimOp("__toXML", 1, prim_toXML);
#endif
addPrimOp("__toFile", 2, prim_toFile); addPrimOp("__toFile", 2, prim_toFile);
#if 0
addPrimOp("__filterSource", 2, prim_filterSource); addPrimOp("__filterSource", 2, prim_filterSource);
#endif
// Attribute sets // Attribute sets
addPrimOp("__attrNames", 1, prim_attrNames); addPrimOp("__attrNames", 1, prim_attrNames);
@ -1091,8 +1095,8 @@ void EvalState::createBaseEnv()
addPrimOp("toString", 1, prim_toString); addPrimOp("toString", 1, prim_toString);
addPrimOp("__substring", 3, prim_substring); addPrimOp("__substring", 3, prim_substring);
addPrimOp("__stringLength", 1, prim_stringLength); addPrimOp("__stringLength", 1, prim_stringLength);
#if 0
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
#if 0
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
#endif #endif

View file

@ -75,7 +75,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
std::cout << format("%1%\n") % canonicaliseExpr(e); std::cout << format("%1%\n") % canonicaliseExpr(e);
else { else {
Value v; Value v;
if (strict) state.strictEval(e, v); else state.eval(e, v); state.eval(e, v);
if (strict) state.strictForceValue(v);
if (evalOnly) if (evalOnly)
std::cout << v << std::endl; std::cout << v << std::endl;
else { else {