* Make `import' work.

This commit is contained in:
Eelco Dolstra 2010-03-30 09:22:33 +00:00
parent 31428c3a06
commit d78a05ab40
4 changed files with 141 additions and 62 deletions

View file

@ -13,7 +13,7 @@ using namespace nix;
void doTest(string s) void doTest(string s)
{ {
EvalState state; EvalState state;
Expr e = parseExprFromString(state, s, "/"); 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.strictEval(e, v);
@ -66,6 +66,8 @@ void run(Strings args)
doTest("if false then 1 else 2"); doTest("if false then 1 else 2");
doTest("if false || true 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("let x = x; in if true || x then 1 else 2");
doTest("/etc/passwd");
doTest("import ./foo.nix");
} }

View file

@ -29,6 +29,9 @@ std::ostream & operator << (std::ostream & str, Value & v)
case tString: case tString:
str << "\"" << v.string.s << "\""; // !!! escaping str << "\"" << v.string.s << "\""; // !!! escaping
break; break;
case tPath:
str << v.path; // !!! escaping?
break;
case tNull: case tNull:
str << "true"; str << "true";
break; 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 */ 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; ATerm s; ATermList context;
if (matchStr(e, s, context)) { if (matchStr(e, s, context)) {
assert(context == ATempty); 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; return;
} }
@ -282,8 +304,14 @@ void EvalState::eval(Env & env, Expr e, Value & v)
eval(env, e2, v); eval(env, e2, v);
forceAttrs(v); // !!! eval followed by force is slightly inefficient forceAttrs(v); // !!! eval followed by force is slightly inefficient
Bindings::iterator i = v.attrs->find(name); Bindings::iterator i = v.attrs->find(name);
if (i == v.attrs->end()) throw TypeError("attribute not found"); if (i == v.attrs->end())
forceValue(i->second); 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; v = i->second;
return; 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) bool EvalState::eqValues(Value & v1, Value & v2)
{ {
forceValue(v1); 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)) LocalNoInline(Expr evalAssert(EvalState & state, Expr cond, Expr body, ATerm pos))
{ {
if (!evalBool(state, cond)) 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); static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs);

View file

@ -58,6 +58,7 @@ struct Value
const char * s; const char * s;
const char * * context; const char * * context;
} string; } string;
const char * path;
Bindings * attrs; Bindings * attrs;
struct { struct {
unsigned int length; 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, PathSet> DrvRoots;
typedef std::map<Path, Hash> DrvHashes; typedef std::map<Path, Hash> DrvHashes;
@ -134,6 +142,10 @@ struct EvalState
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 /* Evaluate an expression to normal form, storing the result in
value `v'. */ value `v'. */
void eval(Expr e, Value & v); void eval(Expr e, Value & v);
@ -157,6 +169,18 @@ struct EvalState
void forceAttrs(Value & v); void forceAttrs(Value & v);
void forceList(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: private:
/* The base environment, containing the builtin functions and /* The base environment, containing the builtin functions and
@ -182,9 +206,6 @@ private:
/* Evaluate an expression to normal form. */ /* Evaluate an expression to normal form. */
Expr evalExpr(EvalState & state, Expr e); 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 /* Evaluate an expression, and recursively evaluate list elements and
attributes. If `canonicalise' is true, we remove things like attributes. If `canonicalise' is true, we remove things like
position information and make sure that attribute sets are in position information and make sure that attribute sets are in
@ -202,17 +223,6 @@ ATermList evalList(EvalState & state, Expr e);
a list). */ a list). */
ATermList flattenList(EvalState & state, Expr e); 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 /* 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, 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(). */ not a normal form; it should be evaluated by calling evalExpr(). */

View file

@ -14,6 +14,7 @@
#include <unistd.h> #include <unistd.h>
#include <algorithm> #include <algorithm>
#include <cstring>
namespace nix { 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) static Expr prim_currentTime(EvalState & state, const ATermVector & args)
{ {
return ATmake("Int(<int>)", time(0)); 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 /* Load and evaluate an expression from path specified by the
argument. */ argument. */
static Expr prim_import(EvalState & state, const ATermVector & args) static void prim_import(EvalState & state, Value * * args, Value & v)
{ {
PathSet context; 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) { for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
assert(isStorePath(*i)); assert(isStorePath(*i));
@ -106,10 +98,11 @@ static Expr prim_import(EvalState & state, const ATermVector & args)
store->buildDerivations(singleton<PathSet>(*i)); store->buildDerivations(singleton<PathSet>(*i));
} }
return evalFile(state, path); state.evalFile(path, v);
} }
#if 0
/* Determine whether the argument is the null value. */ /* Determine whether the argument is the null value. */
static Expr prim_isNull(EvalState & state, const ATermVector & args) static Expr prim_isNull(EvalState & state, const ATermVector & args)
{ {
@ -1134,7 +1127,7 @@ void EvalState::createBaseEnv()
v.type = tNull; v.type = tNull;
} }
{ Value & v = (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm("currentSystem")]; { Value & v = (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm("currentSystem")];
mkString(v, thisSystem.c_str()); // !!! copy string mkString(v, strdup(thisSystem.c_str()));
} }
#if 0 #if 0
@ -1143,7 +1136,9 @@ void EvalState::createBaseEnv()
addPrimOp("__currentTime", 0, prim_currentTime); addPrimOp("__currentTime", 0, prim_currentTime);
// Miscellaneous // Miscellaneous
#endif
addPrimOp("import", 1, prim_import); addPrimOp("import", 1, prim_import);
#if 0
addPrimOp("isNull", 1, prim_isNull); addPrimOp("isNull", 1, prim_isNull);
addPrimOp("__isFunction", 1, prim_isFunction); addPrimOp("__isFunction", 1, prim_isFunction);
addPrimOp("__isString", 1, prim_isString); addPrimOp("__isString", 1, prim_isString);