* 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("."));
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 ]; }");
}

View file

@ -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);

View file

@ -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);

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
from the stack, therefore can't be garbage-collected. */
typedef set<Expr> 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);
else if (matchPath(e, s2))
doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s2)));
switch (v.type) {
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");
break;
else if (matchInt(e, i))
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % i).str()));
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") {
case tAttrs:
if (state.isDerivation(v)) {
XMLAttrs xmlAttrs;
Path outPath, drvPath;
a = attrs.get(toATerm("drvPath"));
if (matchStr(a, drvPath, context))
xmlAttrs["drvPath"] = drvPath;
Bindings::iterator a = v.attrs->find(toATerm("derivation"));
a = attrs.get(toATerm("outPath"));
if (matchStr(a, outPath, context))
xmlAttrs["outPath"] = outPath;
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 (drvsSeen.find(e) == drvsSeen.end()) {
drvsSeen.insert(e);
showAttrs(attrs, doc, context, drvsSeen);
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(attrs, doc, context, drvsSeen);
}
showAttrs(state, strict, *v.attrs, doc, context, drvsSeen);
}
else if (matchList(e, es)) {
break;
case tList: {
XMLOpenElement _(doc, "list");
for (ATermIterator i(es); i; ++i)
printTermAsXML(*i, doc, context, drvsSeen);
for (unsigned int n = 0; n < v.list.length; ++n)
printValueAsXML(state, strict, v.list.elems[n], doc, context, drvsSeen);
break;
}
else if (matchFunction(e, pat, body, pos)) {
case tLambda: {
XMLOpenElement _(doc, "function");
printPatternAsXML(pat, doc);
printPatternAsXML(v.lambda.pat, doc);
break;
}
else
default:
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);
}

View file

@ -5,11 +5,12 @@
#include <map>
#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);
}

View file

@ -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);

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
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(
/* 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! */
));
"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)
{
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<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)
{
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

View file

@ -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 {