diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am index 1e77420d5..d59b798c0 100644 --- a/src/libexpr/Makefile.am +++ b/src/libexpr/Makefile.am @@ -3,7 +3,8 @@ lib_LTLIBRARIES = libexpr.la libexpr_la_SOURCES = nixexpr.cc nixexpr.hh parser.cc parser.hh \ eval.cc eval.hh primops.cc \ lexer-tab.c lexer-tab.h parser-tab.c parser-tab.h \ - get-drvs.cc get-drvs.hh + get-drvs.cc get-drvs.hh \ + attr-path.cc attr-path.hh BUILT_SOURCES = nixexpr-ast.cc nixexpr-ast.hh \ parser-tab.h lexer-tab.h parser-tab.c lexer-tab.c diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc new file mode 100644 index 000000000..274f49cea --- /dev/null +++ b/src/libexpr/attr-path.cc @@ -0,0 +1,74 @@ +#include "attr-path.hh" +#include "nixexpr-ast.hh" + + +bool isAttrs(EvalState & state, Expr e, ATermMap & attrs) +{ + e = evalExpr(state, e); + ATermList dummy; + if (!matchAttrs(e, dummy)) return false; + queryAllAttrs(e, attrs, false); + return true; +} + + +Expr findAlongAttrPath(EvalState & state, const string & attrPath, Expr e) +{ + Strings tokens = tokenizeString(attrPath, "."); + + Error attrError = + Error(format("attribute selection path `%1%' does not match expression") % attrPath); + + string curPath; + + for (Strings::iterator i = tokens.begin(); i != tokens.end(); ++i) { + + if (!curPath.empty()) curPath += "."; + curPath += *i; + + /* Is *i an index (integer) or a normal attribute name? */ + enum { apAttr, apIndex } apType = apAttr; + string attr = *i; + int attrIndex = -1; + if (string2Int(attr, attrIndex)) apType = apIndex; + + /* Evaluate the expression. */ + e = evalExpr(state, autoCallFunction(evalExpr(state, e))); + + /* It should evaluate to either an attribute set or an + expression, according to what is specified in the + attrPath. */ + + if (apType == apAttr) { + + ATermMap attrs(128); + + if (!isAttrs(state, e, attrs)) + throw TypeError( + format("the expression selected by the selection path `%1%' should be an attribute set but is %2%") + % curPath % showType(e)); + + e = attrs.get(toATerm(attr)); + if (!e) + throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath); + + } + + else if (apType == apIndex) { + + ATermList es; + if (!matchList(e, es)) + throw TypeError( + format("the expression selected by the selection path `%1%' should be a list but is %2%") + % curPath % showType(e)); + + e = ATelementAt(es, attrIndex); + if (!e) + throw Error(format("list index %1% in selection path `%2%' not found") % attrIndex % curPath); + + } + + } + + return e; +} diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh new file mode 100644 index 000000000..f64ef7a7c --- /dev/null +++ b/src/libexpr/attr-path.hh @@ -0,0 +1,13 @@ +#ifndef __ATTR_PATH_H +#define __ATTR_PATH_H + +#include +#include + +#include "eval.hh" + + +Expr findAlongAttrPath(EvalState & state, const string & attrPath, Expr e); + + +#endif /* !__ATTR_PATH_H */ diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f1a600103..6c78356db 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -287,6 +287,24 @@ static ATerm concatStrings(EvalState & state, const ATermVector & args) } +Expr autoCallFunction(Expr e) +{ + ATermList formals; + ATerm body, pos; + if (matchFunction(e, formals, body, pos)) { + for (ATermIterator i(formals); i; ++i) { + Expr name, def; ATerm values, def2; + if (!matchFormal(*i, name, values, def2)) abort(); + if (!matchDefaultValue(def2, def)) + throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')") + % aterm2String(name)); + } + e = makeCall(e, makeAttrs(ATermMap(0))); + } + return e; +} + + Expr evalExpr2(EvalState & state, Expr e) { Expr e1, e2, e3, e4; @@ -380,7 +398,9 @@ Expr evalExpr2(EvalState & state, Expr e) } } - else throw TypeError("the left-hand side of the function call is neither a function nor a primop (built-in operation)"); + else throw TypeError( + format("the left-hand side of the function call is neither a function nor a primop (built-in operation) but %1%") + % showType(e1)); } /* Attribute selection. */ diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index a3815733e..018c6b726 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -59,6 +59,11 @@ string coerceToStringWithContext(EvalState & state, ATermList & context, Expr e, bool & isPath); Expr wrapInContext(ATermList context, Expr e); +/* Automatically call a function for which each argument has a default + value. Note: result is a call, not a normal form; it should be + evaluated by calling evalExpr(). */ +Expr autoCallFunction(Expr e); + /* Print statistics. */ void printEvalStats(EvalState & state); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 736c3e522..0afc7bd6d 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -58,7 +58,7 @@ typedef set Exprs; it makes sense for the caller to recursively search for derivations in `e'. */ static bool getDerivation(EvalState & state, Expr e, - DrvInfos & drvs, Exprs & doneExprs, string attributeName) + const string & attrPath, DrvInfos & drvs, Exprs & doneExprs) { try { @@ -92,7 +92,7 @@ static bool getDerivation(EvalState & state, Expr e, drv.attrs = attrs; - drv.attrPath = attributeName; + drv.attrPath = attrPath; drvs.push_back(drv); return false; @@ -107,7 +107,7 @@ bool getDerivation(EvalState & state, Expr e, DrvInfo & drv) { Exprs doneExprs; DrvInfos drvs; - getDerivation(state, e, drvs, doneExprs, ""); + getDerivation(state, e, "", drvs, doneExprs); if (drvs.size() != 1) return false; drv = drvs.front(); return true; @@ -121,118 +121,53 @@ static string addToPath(const string & s1, const string & s2) static void getDerivations(EvalState & state, Expr e, - DrvInfos & drvs, Exprs & doneExprs, const string & attrPath, - const string & pathTaken) + const string & pathPrefix, DrvInfos & drvs, Exprs & doneExprs) { - /* Automatically call functions for which each argument has a - default value. */ - ATermList formals; - ATerm body, pos; - if (matchFunction(e, formals, body, pos)) { - for (ATermIterator i(formals); i; ++i) { - Expr name, def; ATerm values, def2; - if (!matchFormal(*i, name, values, def2)) abort(); - if (!matchDefaultValue(def2, def)) - throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')") - % aterm2String(name)); - } - getDerivations(state, - makeCall(e, makeAttrs(ATermMap(0))), - drvs, doneExprs, attrPath, pathTaken); - return; - } - - /* Parse the start of attrPath. */ - enum { apNone, apAttr, apIndex } apType; - string attrPathRest; - string attr; - int attrIndex; - Error attrError = - Error(format("attribute selection path `%1%' does not match expression") % attrPath); - - if (attrPath.empty()) - apType = apNone; - else { - string::size_type dot = attrPath.find("."); - if (dot == string::npos) { - attrPathRest = ""; - attr = attrPath; - } else { - attrPathRest = string(attrPath, dot + 1); - attr = string(attrPath, 0, dot); - } - apType = apAttr; - if (string2Int(attr, attrIndex)) apType = apIndex; - } + e = evalExpr(state, autoCallFunction(evalExpr(state, e))); /* Process the expression. */ ATermList es; DrvInfo drv; - if (!getDerivation(state, e, drvs, doneExprs, pathTaken)) { - if (apType != apNone) throw attrError; + if (!getDerivation(state, e, pathPrefix, drvs, doneExprs)) return; - } - - e = evalExpr(state, e); if (matchAttrs(e, es)) { - if (apType != apNone && apType != apAttr) throw attrError; ATermMap drvMap(ATgetLength(es)); queryAllAttrs(e, drvMap); - if (apType == apNone) { - for (ATermMap::const_iterator i = drvMap.begin(); i != drvMap.end(); ++i) { - startNest(nest, lvlDebug, - format("evaluating attribute `%1%'") % aterm2String(i->key)); - string pathTaken2 = addToPath(pathTaken, aterm2String(i->key)); - if (getDerivation(state, i->value, drvs, doneExprs, pathTaken2)) { - /* If the value of this attribute is itself an - attribute set, should we recurse into it? - => Only if it has a `recurseForDerivations = true' - attribute. */ - ATermList es; - Expr e = evalExpr(state, i->value); - if (matchAttrs(e, es)) { - ATermMap attrs(ATgetLength(es)); - queryAllAttrs(e, attrs, false); - Expr e2 = attrs.get(toATerm("recurseForDerivations")); - if (e2 && evalBool(state, e2)) - getDerivations(state, e, drvs, doneExprs, attrPathRest, pathTaken2); - } + + for (ATermMap::const_iterator i = drvMap.begin(); i != drvMap.end(); ++i) { + startNest(nest, lvlDebug, + format("evaluating attribute `%1%'") % aterm2String(i->key)); + string pathPrefix2 = addToPath(pathPrefix, aterm2String(i->key)); + if (getDerivation(state, i->value, pathPrefix2, drvs, doneExprs)) { + /* If the value of this attribute is itself an + attribute set, should we recurse into it? => Only + if it has a `recurseForDerivations = true' + attribute. */ + ATermList es; + Expr e = evalExpr(state, i->value); + if (matchAttrs(e, es)) { + ATermMap attrs(ATgetLength(es)); + queryAllAttrs(e, attrs, false); + Expr e2 = attrs.get(toATerm("recurseForDerivations")); + if (e2 && evalBool(state, e2)) + getDerivations(state, e, pathPrefix2, drvs, doneExprs); } } - } else { - Expr e2 = drvMap.get(toATerm(attr)); - if (!e2) throw Error(format("attribute `%1%' in selection path not found") % attr); - startNest(nest, lvlDebug, - format("evaluating attribute `%1%'") % attr); - string pathTaken2 = addToPath(pathTaken, attr); - getDerivation(state, e2, drvs, doneExprs, pathTaken2); - if (!attrPath.empty()) - getDerivations(state, e2, drvs, doneExprs, attrPathRest, pathTaken2); } + return; } if (matchList(e, es)) { - if (apType != apNone && apType != apIndex) throw attrError; - if (apType == apNone) { - int n = 0; - for (ATermIterator i(es); i; ++i, ++n) { - startNest(nest, lvlDebug, - format("evaluating list element")); - string pathTaken2 = addToPath(pathTaken, (format("%1%") % n).str()); - if (getDerivation(state, *i, drvs, doneExprs, pathTaken2)) - getDerivations(state, *i, drvs, doneExprs, attrPathRest, pathTaken2); - } - } else { - Expr e2 = ATelementAt(es, attrIndex); - if (!e2) throw Error(format("list index %1% in selection path not found") % attrIndex); + int n = 0; + for (ATermIterator i(es); i; ++i, ++n) { startNest(nest, lvlDebug, format("evaluating list element")); - string pathTaken2 = addToPath(pathTaken, (format("%1%") % attrIndex).str()); - if (getDerivation(state, e2, drvs, doneExprs, pathTaken2)) - getDerivations(state, e2, drvs, doneExprs, attrPathRest, pathTaken2); + string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); + if (getDerivation(state, *i, pathPrefix2, drvs, doneExprs)) + getDerivations(state, *i, pathPrefix2, drvs, doneExprs); } return; } @@ -241,9 +176,9 @@ static void getDerivations(EvalState & state, Expr e, } -void getDerivations(EvalState & state, Expr e, DrvInfos & drvs, - const string & attrPath) +void getDerivations(EvalState & state, Expr e, const string & pathPrefix, + DrvInfos & drvs) { Exprs doneExprs; - getDerivations(state, e, drvs, doneExprs, attrPath, ""); + getDerivations(state, e, pathPrefix, drvs, doneExprs); } diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 4fb8f43fe..84ffe25ca 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -49,8 +49,8 @@ typedef list DrvInfos; Otherwise, return false. */ bool getDerivation(EvalState & state, Expr e, DrvInfo & drv); -void getDerivations(EvalState & state, Expr e, DrvInfos & drvs, - const string & attrPath = ""); +void getDerivations(EvalState & state, Expr e, const string & pathPrefix, + DrvInfos & drvs); #endif /* !__GET_DRVS_H */ diff --git a/src/nix-env/main.cc b/src/nix-env/main.cc index 875cdd744..3fcaaff42 100644 --- a/src/nix-env/main.cc +++ b/src/nix-env/main.cc @@ -10,6 +10,7 @@ #include "help.txt.hh" #include "nixexpr-ast.hh" #include "get-drvs.hh" +#include "attr-path.hh" #include "pathlocks.hh" #include @@ -36,7 +37,6 @@ struct InstallSourceInfo Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */ Path profile; /* for srcProfile */ string systemFilter; /* for srcNixExprDrvs */ - string attrPath; /* srcAttrPath */ }; @@ -65,7 +65,7 @@ static void loadDerivations(EvalState & state, Path nixExprPath, string systemFilter, DrvInfos & elems) { getDerivations(state, - parseExprFromFile(state, absPath(nixExprPath)), elems); + parseExprFromFile(state, absPath(nixExprPath)), "", elems); /* Filter out all derivations not applicable to the current system. */ @@ -119,7 +119,7 @@ static DrvInfos queryInstalled(EvalState & state, const Path & userEnv) e = bottomupRewrite(addPos, e); DrvInfos elems; - getDerivations(state, e, elems); + getDerivations(state, e, "", elems); return elems; } @@ -334,7 +334,7 @@ static void queryInstSources(EvalState & state, { Expr e2 = parseExprFromString(state, *i, absPath(".")); Expr call = makeCall(e2, e1); - getDerivations(state, call, elems); + getDerivations(state, call, "", elems); } break; @@ -388,7 +388,9 @@ static void queryInstSources(EvalState & state, for (Strings::const_iterator i = args.begin(); i != args.end(); ++i) getDerivations(state, - parseExprFromFile(state, instSource.nixExprPath), elems, *i); + findAlongAttrPath(state, *i, + parseExprFromFile(state, instSource.nixExprPath)), + "", elems); break; } } diff --git a/src/nix-instantiate/main.cc b/src/nix-instantiate/main.cc index 31d61f3af..963e81bac 100644 --- a/src/nix-instantiate/main.cc +++ b/src/nix-instantiate/main.cc @@ -9,6 +9,7 @@ #include "parser.hh" #include "nixexpr-ast.hh" #include "get-drvs.hh" +#include "attr-path.hh" #include "help.txt.hh" @@ -18,13 +19,12 @@ void printHelp() } -static Expr evalStdin(EvalState & state, bool parseOnly) +static Expr parseStdin(EvalState & state) { - startNest(nest, lvlTalkative, format("evaluating standard input")); + startNest(nest, lvlTalkative, format("parsing standard input")); string s, s2; while (getline(cin, s2)) s += s2 + "\n"; - Expr e = parseExprFromString(state, s, absPath(".")); - return parseOnly ? e : evalExpr(state, e); + return parseExprFromString(state, s, absPath(".")); } @@ -34,7 +34,7 @@ static bool indirectRoot = false; static void printResult(EvalState & state, Expr e, - bool evalOnly, bool printArgs, const string & attrPath) + bool evalOnly, bool printArgs) { if (evalOnly) cout << format("%1%\n") % e; @@ -54,7 +54,7 @@ static void printResult(EvalState & state, Expr e, else { DrvInfos drvs; - getDerivations(state, e, drvs, attrPath); + getDerivations(state, e, "", drvs); for (DrvInfos::iterator i = drvs.begin(); i != drvs.end(); ++i) { Path drvPath = i->queryDrvPath(state); if (gcRoot == "") @@ -119,18 +119,19 @@ void run(Strings args) openDB(); if (readStdin) { - Expr e = evalStdin(state, parseOnly); - printResult(state, e, evalOnly, printArgs, attrPath); + Expr e = findAlongAttrPath(state, attrPath, parseStdin(state)); + if (!parseOnly) e = evalExpr(state, e); + printResult(state, e, evalOnly, printArgs); } for (Strings::iterator i = files.begin(); i != files.end(); i++) { Path path = absPath(*i); - Expr e = parseOnly - ? parseExprFromFile(state, path) - : evalFile(state, path); - printResult(state, e, evalOnly, printArgs, attrPath); + Expr e = findAlongAttrPath(state, attrPath, + parseExprFromFile(state, path)); + if (!parseOnly) e = evalExpr(state, e); + printResult(state, e, evalOnly, printArgs); } printEvalStats(state);