* Implemented the primops necessary for generating the NixOS manual.
This commit is contained in:
parent
a353aef0b1
commit
fc92244ba8
8 changed files with 160 additions and 146 deletions
|
@ -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 ]; }");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
doc.writeEmptyElement("null");
|
||||
case tInt:
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str()));
|
||||
break;
|
||||
|
||||
else if (matchInt(e, i))
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % i).str()));
|
||||
case tBool:
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
|
||||
break;
|
||||
|
||||
else if (e == eTrue)
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", "true"));
|
||||
case tString:
|
||||
/* !!! show the context? */
|
||||
doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
|
||||
break;
|
||||
|
||||
else if (e == eFalse)
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", "false"));
|
||||
case tPath:
|
||||
doc.writeEmptyElement("path", singletonAttrs("value", v.path));
|
||||
break;
|
||||
|
||||
else if (matchAttrs(e, as)) {
|
||||
ATermMap attrs;
|
||||
queryAllAttrs(e, attrs);
|
||||
case tNull:
|
||||
doc.writeEmptyElement("null");
|
||||
break;
|
||||
|
||||
Expr a = attrs.get(toATerm("type"));
|
||||
if (a && matchStr(a, s, context) && s == "derivation") {
|
||||
case tAttrs:
|
||||
if (state.isDerivation(v)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
|
||||
XMLAttrs xmlAttrs;
|
||||
Path outPath, drvPath;
|
||||
Bindings::iterator a = v.attrs->find(toATerm("derivation"));
|
||||
|
||||
a = attrs.get(toATerm("drvPath"));
|
||||
if (matchStr(a, drvPath, context))
|
||||
xmlAttrs["drvPath"] = drvPath;
|
||||
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 = attrs.get(toATerm("outPath"));
|
||||
if (matchStr(a, outPath, context))
|
||||
xmlAttrs["outPath"] = outPath;
|
||||
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()) {
|
||||
drvsSeen.insert(e);
|
||||
showAttrs(attrs, doc, context, drvsSeen);
|
||||
} else
|
||||
doc.writeEmptyElement("repeated");
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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! */
|
||||
));
|
||||
/* 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);
|
||||
|
||||
return evalBool(state, call);
|
||||
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<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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue