* Organise primops.cc a bit better.

This commit is contained in:
Eelco Dolstra 2007-01-29 15:11:32 +00:00
parent 7349bd0176
commit 18e6096105
1 changed files with 429 additions and 374 deletions

View File

@ -17,6 +17,11 @@
namespace nix {
/*************************************************************
* Constants
*************************************************************/
static Expr primBuiltins(EvalState & state, const ATermVector & args)
{
/* Return an attribute set containing all primops. This allows
@ -41,6 +46,47 @@ static Expr primBuiltins(EvalState & state, const ATermVector & args)
}
/* Boolean constructors. */
static Expr primTrue(EvalState & state, const ATermVector & args)
{
return eTrue;
}
static Expr primFalse(EvalState & state, const ATermVector & args)
{
return eFalse;
}
/* Return the null value. */
static Expr primNull(EvalState & state, const ATermVector & args)
{
return makeNull();
}
/* 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 primCurrentSystem(EvalState & state, const ATermVector & args)
{
return makeStr(thisSystem);
}
static Expr primCurrentTime(EvalState & state, const ATermVector & args)
{
return ATmake("Int(<int>)", time(0));
}
/*************************************************************
* Miscellaneous
*************************************************************/
/* Load and evaluate an expression from path specified by the
argument. */
static Expr primImport(EvalState & state, const ATermVector & args)
@ -61,16 +107,201 @@ static Expr primImport(EvalState & state, const ATermVector & args)
}
static Expr primPathExists(EvalState & state, const ATermVector & args)
/* Convert the argument to a string. Paths are *not* copied to the
store, so `toString /foo/bar' yields `"/foo/bar"', not
`"/nix/store/whatever..."'. */
static Expr primToString(EvalState & state, const ATermVector & args)
{
PathSet context;
Path path = coerceToPath(state, args[0], context);
if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
return makeBool(pathExists(path));
string s = coerceToString(state, args[0], context, true, false);
return makeStr(s, context);
}
/* Determine whether the argument is the null value. */
static Expr primIsNull(EvalState & state, const ATermVector & args)
{
return makeBool(matchNull(evalExpr(state, args[0])));
}
static Path findDependency(Path dir, string dep)
{
if (dep[0] == '/') throw EvalError(
format("illegal absolute dependency `%1%'") % dep);
Path p = canonPath(dir + "/" + dep);
if (pathExists(p))
return p;
else
return "";
}
/* Make path `p' relative to directory `pivot'. E.g.,
relativise("/a/b/c", "a/b/x/y") => "../x/y". Both input paths
should be in absolute canonical form. */
static string relativise(Path pivot, Path p)
{
assert(pivot.size() > 0 && pivot[0] == '/');
assert(p.size() > 0 && p[0] == '/');
if (pivot == p) return ".";
/* `p' is in `pivot'? */
Path pivot2 = pivot + "/";
if (p.substr(0, pivot2.size()) == pivot2) {
return p.substr(pivot2.size());
}
/* Otherwise, `p' is in a parent of `pivot'. Find up till which
path component `p' and `pivot' match, and add an appropriate
number of `..' components. */
string::size_type i = 1;
while (1) {
string::size_type j = pivot.find('/', i);
if (j == string::npos) break;
j++;
if (pivot.substr(0, j) != p.substr(0, j)) break;
i = j;
}
string prefix;
unsigned int slashes = count(pivot.begin() + i, pivot.end(), '/') + 1;
while (slashes--) {
prefix += "../";
}
return prefix + p.substr(i);
}
static Expr primDependencyClosure(EvalState & state, const ATermVector & args)
{
startNest(nest, lvlDebug, "finding dependencies");
Expr attrs = evalExpr(state, args[0]);
/* Get the start set. */
Expr startSet = queryAttr(attrs, "startSet");
if (!startSet) throw EvalError("attribute `startSet' required");
ATermList startSet2 = evalList(state, startSet);
Path pivot;
PathSet workSet;
for (ATermIterator i(startSet2); i; ++i) {
PathSet context; /* !!! what to do? */
Path p = coerceToPath(state, *i, context);
workSet.insert(p);
pivot = dirOf(p);
}
/* Get the search path. */
PathSet searchPath;
Expr e = queryAttr(attrs, "searchPath");
if (e) {
ATermList list = evalList(state, e);
for (ATermIterator i(list); i; ++i) {
PathSet context; /* !!! what to do? */
Path p = coerceToPath(state, *i, context);
searchPath.insert(p);
}
}
Expr scanner = queryAttr(attrs, "scanner");
if (!scanner) throw EvalError("attribute `scanner' required");
/* Construct the dependency closure by querying the dependency of
each path in `workSet', adding the dependencies to
`workSet'. */
PathSet doneSet;
while (!workSet.empty()) {
Path path = *(workSet.begin());
workSet.erase(path);
if (doneSet.find(path) != doneSet.end()) continue;
doneSet.insert(path);
try {
/* Call the `scanner' function with `path' as argument. */
debug(format("finding dependencies in `%1%'") % path);
ATermList deps = evalList(state, makeCall(scanner, makeStr(path)));
/* Try to find the dependencies relative to the `path'. */
for (ATermIterator i(deps); i; ++i) {
string s = evalStringNoCtx(state, *i);
Path dep = findDependency(dirOf(path), s);
if (dep == "") {
for (PathSet::iterator j = searchPath.begin();
j != searchPath.end(); ++j)
{
dep = findDependency(*j, s);
if (dep != "") break;
}
}
if (dep == "")
debug(format("did NOT find dependency `%1%'") % s);
else {
debug(format("found dependency `%1%'") % dep);
workSet.insert(dep);
}
}
} catch (Error & e) {
e.addPrefix(format("while finding dependencies in `%1%':\n")
% path);
throw;
}
}
/* Return a list of the dependencies we've just found. */
ATermList deps = ATempty;
for (PathSet::iterator i = doneSet.begin(); i != doneSet.end(); ++i) {
deps = ATinsert(deps, makeStr(relativise(pivot, *i)));
deps = ATinsert(deps, makeStr(*i));
}
debug(format("dependency list is `%1%'") % makeList(deps));
return makeList(deps);
}
static Expr primAbort(EvalState & state, const ATermVector & args)
{
PathSet context;
throw Abort(format("evaluation aborted with the following error message: `%1%'") %
evalString(state, args[0], context));
}
/* Return an environment variable. Use with care. */
static Expr primGetEnv(EvalState & state, const ATermVector & args)
{
string name = evalStringNoCtx(state, args[0]);
return makeStr(getEnv(name));
}
static Expr primRelativise(EvalState & state, const ATermVector & args)
{
PathSet context; /* !!! what to do? */
Path pivot = coerceToPath(state, args[0], context);
Path path = coerceToPath(state, args[1], context);
return makeStr(relativise(pivot, path));
}
/*************************************************************
* Derivations
*************************************************************/
/* Returns the hash of a derivation modulo fixed-output
subderivations. A fixed-output derivation is a derivation with one
output (`out') for which an expected hash and hash algorithm are
@ -324,6 +555,30 @@ static Expr primDerivationLazy(EvalState & state, const ATermVector & args)
}
/*************************************************************
* Paths
*************************************************************/
/* Convert the argument to a path. !!! obsolete? */
static Expr primToPath(EvalState & state, const ATermVector & args)
{
PathSet context;
string path = coerceToPath(state, args[0], context);
return makeStr(canonPath(path), context);
}
static Expr primPathExists(EvalState & state, const ATermVector & args)
{
PathSet context;
Path path = coerceToPath(state, args[0], context);
if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
return makeBool(pathExists(path));
}
/* Return the base name of the given string, i.e., everything
following the last slash. */
static Expr primBaseNameOf(EvalState & state, const ATermVector & args)
@ -346,24 +601,9 @@ static Expr primDirOf(EvalState & state, const ATermVector & args)
}
/* Convert the argument to a string. Paths are *not* copied to the
store, so `toString /foo/bar' yields `"/foo/bar"', not
`"/nix/store/whatever..."'. */
static Expr primToString(EvalState & state, const ATermVector & args)
{
PathSet context;
string s = coerceToString(state, args[0], context, true, false);
return makeStr(s, context);
}
/* Convert the argument to a path. !!! obsolete? */
static Expr primToPath(EvalState & state, const ATermVector & args)
{
PathSet context;
string path = coerceToPath(state, args[0], context);
return makeStr(canonPath(path), context);
}
/*************************************************************
* Creating files
*************************************************************/
/* Convert the argument (which can be any Nix expression) to an XML
@ -406,339 +646,6 @@ static Expr primToFile(EvalState & state, const ATermVector & args)
}
/* Boolean constructors. */
static Expr primTrue(EvalState & state, const ATermVector & args)
{
return eTrue;
}
static Expr primFalse(EvalState & state, const ATermVector & args)
{
return eFalse;
}
/* Return the null value. */
static Expr primNull(EvalState & state, const ATermVector & args)
{
return makeNull();
}
/* Determine whether the argument is the null value. */
static Expr primIsNull(EvalState & state, const ATermVector & args)
{
return makeBool(matchNull(evalExpr(state, args[0])));
}
/* Determine whether the argument is a list. */
static Expr primIsList(EvalState & state, const ATermVector & args)
{
ATermList list;
return makeBool(matchList(evalExpr(state, args[0]), list));
}
static Path findDependency(Path dir, string dep)
{
if (dep[0] == '/') throw EvalError(
format("illegal absolute dependency `%1%'") % dep);
Path p = canonPath(dir + "/" + dep);
if (pathExists(p))
return p;
else
return "";
}
/* Make path `p' relative to directory `pivot'. E.g.,
relativise("/a/b/c", "a/b/x/y") => "../x/y". Both input paths
should be in absolute canonical form. */
static string relativise(Path pivot, Path p)
{
assert(pivot.size() > 0 && pivot[0] == '/');
assert(p.size() > 0 && p[0] == '/');
if (pivot == p) return ".";
/* `p' is in `pivot'? */
Path pivot2 = pivot + "/";
if (p.substr(0, pivot2.size()) == pivot2) {
return p.substr(pivot2.size());
}
/* Otherwise, `p' is in a parent of `pivot'. Find up till which
path component `p' and `pivot' match, and add an appropriate
number of `..' components. */
string::size_type i = 1;
while (1) {
string::size_type j = pivot.find('/', i);
if (j == string::npos) break;
j++;
if (pivot.substr(0, j) != p.substr(0, j)) break;
i = j;
}
string prefix;
unsigned int slashes = count(pivot.begin() + i, pivot.end(), '/') + 1;
while (slashes--) {
prefix += "../";
}
return prefix + p.substr(i);
}
static Expr primDependencyClosure(EvalState & state, const ATermVector & args)
{
startNest(nest, lvlDebug, "finding dependencies");
Expr attrs = evalExpr(state, args[0]);
/* Get the start set. */
Expr startSet = queryAttr(attrs, "startSet");
if (!startSet) throw EvalError("attribute `startSet' required");
ATermList startSet2 = evalList(state, startSet);
Path pivot;
PathSet workSet;
for (ATermIterator i(startSet2); i; ++i) {
PathSet context; /* !!! what to do? */
Path p = coerceToPath(state, *i, context);
workSet.insert(p);
pivot = dirOf(p);
}
/* Get the search path. */
PathSet searchPath;
Expr e = queryAttr(attrs, "searchPath");
if (e) {
ATermList list = evalList(state, e);
for (ATermIterator i(list); i; ++i) {
PathSet context; /* !!! what to do? */
Path p = coerceToPath(state, *i, context);
searchPath.insert(p);
}
}
Expr scanner = queryAttr(attrs, "scanner");
if (!scanner) throw EvalError("attribute `scanner' required");
/* Construct the dependency closure by querying the dependency of
each path in `workSet', adding the dependencies to
`workSet'. */
PathSet doneSet;
while (!workSet.empty()) {
Path path = *(workSet.begin());
workSet.erase(path);
if (doneSet.find(path) != doneSet.end()) continue;
doneSet.insert(path);
try {
/* Call the `scanner' function with `path' as argument. */
debug(format("finding dependencies in `%1%'") % path);
ATermList deps = evalList(state, makeCall(scanner, makeStr(path)));
/* Try to find the dependencies relative to the `path'. */
for (ATermIterator i(deps); i; ++i) {
string s = evalStringNoCtx(state, *i);
Path dep = findDependency(dirOf(path), s);
if (dep == "") {
for (PathSet::iterator j = searchPath.begin();
j != searchPath.end(); ++j)
{
dep = findDependency(*j, s);
if (dep != "") break;
}
}
if (dep == "")
debug(format("did NOT find dependency `%1%'") % s);
else {
debug(format("found dependency `%1%'") % dep);
workSet.insert(dep);
}
}
} catch (Error & e) {
e.addPrefix(format("while finding dependencies in `%1%':\n")
% path);
throw;
}
}
/* Return a list of the dependencies we've just found. */
ATermList deps = ATempty;
for (PathSet::iterator i = doneSet.begin(); i != doneSet.end(); ++i) {
deps = ATinsert(deps, makeStr(relativise(pivot, *i)));
deps = ATinsert(deps, makeStr(*i));
}
debug(format("dependency list is `%1%'") % makeList(deps));
return makeList(deps);
}
static Expr primAbort(EvalState & state, const ATermVector & args)
{
PathSet context;
throw Abort(format("evaluation aborted with the following error message: `%1%'") %
evalString(state, args[0], context));
}
/* Return the first element of a list. */
static Expr primHead(EvalState & state, const ATermVector & args)
{
ATermList list = evalList(state, args[0]);
if (ATisEmpty(list))
throw Error("`head' called on an empty list");
return evalExpr(state, ATgetFirst(list));
}
/* Return a list consisting of everything but the the first element of
a list. */
static Expr primTail(EvalState & state, const ATermVector & args)
{
ATermList list = evalList(state, args[0]);
if (ATisEmpty(list))
throw Error("`tail' called on an empty list");
return makeList(ATgetNext(list));
}
/* Return an environment variable. Use with care. */
static Expr primGetEnv(EvalState & state, const ATermVector & args)
{
string name = evalStringNoCtx(state, args[0]);
return makeStr(getEnv(name));
}
/* Return the names of the attributes in an attribute set as a sorted
list of strings. */
static Expr primAttrNames(EvalState & state, const ATermVector & args)
{
ATermMap attrs;
queryAllAttrs(evalExpr(state, args[0]), attrs);
StringSet names;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
names.insert(aterm2String(i->key));
ATermList list = ATempty;
for (StringSet::const_reverse_iterator i = names.rbegin();
i != names.rend(); ++i)
list = ATinsert(list, makeStr(*i, PathSet()));
return makeList(list);
}
/* Apply a function to every element of a list. */
static Expr primMap(EvalState & state, const ATermVector & args)
{
Expr fun = evalExpr(state, args[0]);
ATermList list = evalList(state, args[1]);
ATermList res = ATempty;
for (ATermIterator i(list); i; ++i)
res = ATinsert(res, makeCall(fun, *i));
return makeList(ATreverse(res));
}
/* 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 primCurrentSystem(EvalState & state, const ATermVector & args)
{
return makeStr(thisSystem);
}
static Expr primCurrentTime(EvalState & state, const ATermVector & args)
{
return ATmake("Int(<int>)", time(0));
}
/* Dynamic version of the `.' operator. */
static Expr primGetAttr(EvalState & state, const ATermVector & args)
{
string attr = evalStringNoCtx(state, args[0]);
return evalExpr(state, makeSelect(args[1], toATerm(attr)));
}
/* Dynamic version of the `?' operator. */
static Expr primHasAttr(EvalState & state, const ATermVector & args)
{
string attr = evalStringNoCtx(state, args[0]);
return evalExpr(state, makeOpHasAttr(args[1], toATerm(attr)));
}
static Expr primRemoveAttrs(EvalState & state, const ATermVector & args)
{
ATermMap attrs;
queryAllAttrs(evalExpr(state, args[0]), attrs, true);
ATermList list = evalList(state, args[1]);
for (ATermIterator i(list); i; ++i)
/* It's not an error for *i not to exist. */
attrs.remove(toATerm(evalStringNoCtx(state, *i)));
return makeAttrs(attrs);
}
static Expr primRelativise(EvalState & state, const ATermVector & args)
{
PathSet context; /* !!! what to do? */
Path pivot = coerceToPath(state, args[0], context);
Path path = coerceToPath(state, args[1], context);
return makeStr(relativise(pivot, path));
}
static Expr primAdd(EvalState & state, const ATermVector & args)
{
int i1 = evalInt(state, args[0]);
int i2 = evalInt(state, args[1]);
return makeInt(i1 + i2);
}
static Expr primSub(EvalState & state, const ATermVector & args)
{
int i1 = evalInt(state, args[0]);
int i2 = evalInt(state, args[1]);
return makeInt(i1 - i2);
}
static Expr primLessThan(EvalState & state, const ATermVector & args)
{
int i1 = evalInt(state, args[0]);
int i2 = evalInt(state, args[1]);
return makeBool(i1 < i2);
}
struct FilterFromExpr : PathFilter
{
EvalState & state;
@ -787,6 +694,139 @@ static Expr primFilterSource(EvalState & state, const ATermVector & args)
}
/*************************************************************
* Attribute sets
*************************************************************/
/* Return the names of the attributes in an attribute set as a sorted
list of strings. */
static Expr primAttrNames(EvalState & state, const ATermVector & args)
{
ATermMap attrs;
queryAllAttrs(evalExpr(state, args[0]), attrs);
StringSet names;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
names.insert(aterm2String(i->key));
ATermList list = ATempty;
for (StringSet::const_reverse_iterator i = names.rbegin();
i != names.rend(); ++i)
list = ATinsert(list, makeStr(*i, PathSet()));
return makeList(list);
}
/* Dynamic version of the `.' operator. */
static Expr primGetAttr(EvalState & state, const ATermVector & args)
{
string attr = evalStringNoCtx(state, args[0]);
return evalExpr(state, makeSelect(args[1], toATerm(attr)));
}
/* Dynamic version of the `?' operator. */
static Expr primHasAttr(EvalState & state, const ATermVector & args)
{
string attr = evalStringNoCtx(state, args[0]);
return evalExpr(state, makeOpHasAttr(args[1], toATerm(attr)));
}
static Expr primRemoveAttrs(EvalState & state, const ATermVector & args)
{
ATermMap attrs;
queryAllAttrs(evalExpr(state, args[0]), attrs, true);
ATermList list = evalList(state, args[1]);
for (ATermIterator i(list); i; ++i)
/* It's not an error for *i not to exist. */
attrs.remove(toATerm(evalStringNoCtx(state, *i)));
return makeAttrs(attrs);
}
/*************************************************************
* Lists
*************************************************************/
/* Determine whether the argument is a list. */
static Expr primIsList(EvalState & state, const ATermVector & args)
{
ATermList list;
return makeBool(matchList(evalExpr(state, args[0]), list));
}
/* Return the first element of a list. */
static Expr primHead(EvalState & state, const ATermVector & args)
{
ATermList list = evalList(state, args[0]);
if (ATisEmpty(list))
throw Error("`head' called on an empty list");
return evalExpr(state, ATgetFirst(list));
}
/* Return a list consisting of everything but the the first element of
a list. */
static Expr primTail(EvalState & state, const ATermVector & args)
{
ATermList list = evalList(state, args[0]);
if (ATisEmpty(list))
throw Error("`tail' called on an empty list");
return makeList(ATgetNext(list));
}
/* Apply a function to every element of a list. */
static Expr primMap(EvalState & state, const ATermVector & args)
{
Expr fun = evalExpr(state, args[0]);
ATermList list = evalList(state, args[1]);
ATermList res = ATempty;
for (ATermIterator i(list); i; ++i)
res = ATinsert(res, makeCall(fun, *i));
return makeList(ATreverse(res));
}
/*************************************************************
* Integer arithmetic
*************************************************************/
static Expr primAdd(EvalState & state, const ATermVector & args)
{
int i1 = evalInt(state, args[0]);
int i2 = evalInt(state, args[1]);
return makeInt(i1 + i2);
}
static Expr primSub(EvalState & state, const ATermVector & args)
{
int i1 = evalInt(state, args[0]);
int i2 = evalInt(state, args[1]);
return makeInt(i1 - i2);
}
static Expr primLessThan(EvalState & state, const ATermVector & args)
{
int i1 = evalInt(state, args[0]);
int i2 = evalInt(state, args[1]);
return makeBool(i1 < i2);
}
/*************************************************************
* String manipulation
*************************************************************/
@ -821,41 +861,56 @@ void EvalState::addPrimOps()
{
addPrimOp("builtins", 0, primBuiltins);
// Constants
addPrimOp("true", 0, primTrue);
addPrimOp("false", 0, primFalse);
addPrimOp("null", 0, primNull);
addPrimOp("__currentSystem", 0, primCurrentSystem);
addPrimOp("__currentTime", 0, primCurrentTime);
// Miscellaneous
addPrimOp("import", 1, primImport);
addPrimOp("__pathExists", 1, primPathExists);
addPrimOp("derivation!", 1, primDerivationStrict);
addPrimOp("derivation", 1, primDerivationLazy);
addPrimOp("baseNameOf", 1, primBaseNameOf);
addPrimOp("dirOf", 1, primDirOf);
addPrimOp("toString", 1, primToString);
addPrimOp("__toPath", 1, primToPath);
addPrimOp("__toXML", 1, primToXML);
addPrimOp("isNull", 1, primIsNull);
addPrimOp("__isList", 1, primIsList);
addPrimOp("dependencyClosure", 1, primDependencyClosure);
addPrimOp("abort", 1, primAbort);
addPrimOp("__head", 1, primHead);
addPrimOp("__tail", 1, primTail);
addPrimOp("__getEnv", 1, primGetEnv);
addPrimOp("__attrNames", 1, primAttrNames);
addPrimOp("map", 2, primMap);
addPrimOp("__getAttr", 2, primGetAttr);
addPrimOp("__hasAttr", 2, primHasAttr);
addPrimOp("removeAttrs", 2, primRemoveAttrs);
addPrimOp("relativise", 2, primRelativise);
addPrimOp("__add", 2, primAdd);
addPrimOp("__sub", 2, primSub);
addPrimOp("__lessThan", 2, primLessThan);
// Derivations
addPrimOp("derivation!", 1, primDerivationStrict);
addPrimOp("derivation", 1, primDerivationLazy);
// Paths
addPrimOp("__toPath", 1, primToPath);
addPrimOp("__pathExists", 1, primPathExists);
addPrimOp("baseNameOf", 1, primBaseNameOf);
addPrimOp("dirOf", 1, primDirOf);
// Creating files
addPrimOp("__toXML", 1, primToXML);
addPrimOp("__toFile", 2, primToFile);
addPrimOp("__filterSource", 2, primFilterSource);
// Attribute sets
addPrimOp("__attrNames", 1, primAttrNames);
addPrimOp("__getAttr", 2, primGetAttr);
addPrimOp("__hasAttr", 2, primHasAttr);
addPrimOp("removeAttrs", 2, primRemoveAttrs);
// Lists
addPrimOp("__isList", 1, primIsList);
addPrimOp("__head", 1, primHead);
addPrimOp("__tail", 1, primTail);
addPrimOp("map", 2, primMap);
// Integer arithmetic
addPrimOp("__add", 2, primAdd);
addPrimOp("__sub", 2, primSub);
addPrimOp("__lessThan", 2, primLessThan);
// String manipulation
addPrimOp("__substring", 3, prim_substring);
addPrimOp("__stringLength", 1, prim_stringLength);
}