* Working evaluator.
* Mutually recursive attribute sets. * Print evaluator efficiency statistics.
This commit is contained in:
		
							parent
							
								
									f1c1a3c97f
								
							
						
					
					
						commit
						9210d4d530
					
				
					 10 changed files with 540 additions and 309 deletions
				
			
		|  | @ -1,6 +1,6 @@ | ||||||
| bin_PROGRAMS = fix-ng | bin_PROGRAMS = fix-ng | ||||||
| 
 | 
 | ||||||
| fix_ng_SOURCES = fix-expr.cc parser.cc eval.cc fix.cc | fix_ng_SOURCES = fix-expr.cc parser.cc eval.cc primops.cc fix.cc | ||||||
| fix_ng_LDADD = ../libmain/libmain.a ../libnix/libnix.a ../boost/format/libformat.a \ | fix_ng_LDADD = ../libmain/libmain.a ../libnix/libnix.a ../boost/format/libformat.a \ | ||||||
|  -L../../externals/inst/lib -ldb_cxx -lsglr -lATB -lconversion -lasfix2 -lmept -lATerm |  -L../../externals/inst/lib -ldb_cxx -lsglr -lATB -lconversion -lasfix2 -lmept -lATerm | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,18 +1,167 @@ | ||||||
| #include "eval.hh" | #include "eval.hh" | ||||||
| #include "expr.hh" | #include "expr.hh" | ||||||
| #include "parser.hh" | #include "parser.hh" | ||||||
|  | #include "primops.hh" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| EvalState::EvalState() | EvalState::EvalState() | ||||||
| { | { | ||||||
|     blackHole = ATmake("BlackHole()"); |     blackHole = ATmake("BlackHole()"); | ||||||
|     if (!blackHole) throw Error("cannot build black hole"); |     if (!blackHole) throw Error("cannot build black hole"); | ||||||
|  |     nrEvaluated = nrCached = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Expr getAttr(EvalState & state, Expr e, const string & name) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Substitute an argument set into the body of a function. */ | ||||||
|  | static Expr substArgs(Expr body, ATermList formals, Expr arg) | ||||||
|  | { | ||||||
|  |     Subs subs; | ||||||
|  |     Expr undefined = ATmake("Undefined"); | ||||||
|  | 
 | ||||||
|  |     /* Get the formal arguments. */ | ||||||
|  |     while (!ATisEmpty(formals)) { | ||||||
|  |         char * s; | ||||||
|  |         if (!ATmatch(ATgetFirst(formals), "<str>", &s)) | ||||||
|  |             abort(); /* can't happen */ | ||||||
|  |         subs[s] = undefined; | ||||||
|  |         formals = ATgetNext(formals); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Get the actual arguments, and check that they match with the
 | ||||||
|  |        formals. */ | ||||||
|  |     Attrs args; | ||||||
|  |     queryAllAttrs(arg, args); | ||||||
|  |     for (Attrs::iterator i = args.begin(); i != args.end(); i++) { | ||||||
|  |         if (subs.find(i->first) == subs.end()) | ||||||
|  |             throw badTerm(format("argument `%1%' not declared") % i->first, arg); | ||||||
|  |         subs[i->first] = i->second; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Check that all arguments are defined. */ | ||||||
|  |     for (Subs::iterator i = subs.begin(); i != subs.end(); i++) | ||||||
|  |         if (i->second == undefined) | ||||||
|  |             throw badTerm(format("formal argument `%1%' missing") % i->first, arg); | ||||||
|  |      | ||||||
|  |     return substitute(subs, body); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Transform a mutually recursive set into a non-recursive set.  Each
 | ||||||
|  |    attribute is transformed into an expression that has all references | ||||||
|  |    to attributes substituted with selection expressions on the | ||||||
|  |    original set.  E.g., e = `rec {x = f x y, y = x}' becomes `{x = f | ||||||
|  |    (e.x) (e.y), y = e.x}'. */ | ||||||
|  | ATerm expandRec(ATerm e, ATermList bnds) | ||||||
|  | { | ||||||
|  |     /* Create the substitution list. */ | ||||||
|  |     Subs subs; | ||||||
|  |     ATermList bs = bnds; | ||||||
|  |     while (!ATisEmpty(bs)) { | ||||||
|  |         char * s; | ||||||
|  |         Expr e2; | ||||||
|  |         if (!ATmatch(ATgetFirst(bs), "Bind(<str>, <term>)", &s, &e2)) | ||||||
|  |             abort(); /* can't happen */ | ||||||
|  |         subs[s] = ATmake("Select(<term>, <str>)", e, s); | ||||||
|  |         bs = ATgetNext(bs); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Create the non-recursive set. */ | ||||||
|  |     Attrs as; | ||||||
|  |     bs = bnds; | ||||||
|  |     while (!ATisEmpty(bs)) { | ||||||
|  |         char * s; | ||||||
|  |         Expr e2; | ||||||
|  |         if (!ATmatch(ATgetFirst(bs), "Bind(<str>, <term>)", &s, &e2)) | ||||||
|  |             abort(); /* can't happen */ | ||||||
|  |         as[s] = substitute(subs, e2); | ||||||
|  |         bs = ATgetNext(bs); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return makeAttrs(as); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | string evalString(EvalState & state, Expr e) | ||||||
|  | { | ||||||
|  |     e = evalExpr(state, e); | ||||||
|  |     char * s; | ||||||
|  |     if (!ATmatch(e, "Str(<str>)", &s)) | ||||||
|  |         throw badTerm("string expected", e); | ||||||
|  |     return s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Path evalPath(EvalState & state, Expr e) | ||||||
|  | { | ||||||
|  |     e = evalExpr(state, e); | ||||||
|  |     char * s; | ||||||
|  |     if (!ATmatch(e, "Path(<str>)", &s)) | ||||||
|  |         throw badTerm("path expected", e); | ||||||
|  |     return s; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Expr evalExpr2(EvalState & state, Expr e) | Expr evalExpr2(EvalState & state, Expr e) | ||||||
| { | { | ||||||
|  |     Expr e1, e2, e3, e4; | ||||||
|  |     char * s1; | ||||||
|  | 
 | ||||||
|  |     /* Normal forms. */ | ||||||
|  |     if (ATmatch(e, "Str(<str>)", &s1) || | ||||||
|  |         ATmatch(e, "Path(<str>)", &s1) || | ||||||
|  |         ATmatch(e, "Uri(<str>)", &s1) || | ||||||
|  |         ATmatch(e, "Function([<list>], <term>)", &e1, &e2) || | ||||||
|  |         ATmatch(e, "Attrs([<list>])", &e1) || | ||||||
|  |         ATmatch(e, "List([<list>])", &e1)) | ||||||
|         return e; |         return e; | ||||||
|  | 
 | ||||||
|  |     /* Any encountered variables must be undeclared or primops. */ | ||||||
|  |     if (ATmatch(e, "Var(<str>)", &s1)) { | ||||||
|  |         return e; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Function application. */ | ||||||
|  |     if (ATmatch(e, "Call(<term>, <term>)", &e1, &e2)) { | ||||||
|  |          | ||||||
|  |         /* Evaluate the left-hand side. */ | ||||||
|  |         e1 = evalExpr(state, e1); | ||||||
|  | 
 | ||||||
|  |         /* Is it a primop or a function? */ | ||||||
|  |         if (ATmatch(e1, "Var(<str>)", &s1)) { | ||||||
|  |             string primop(s1); | ||||||
|  |             if (primop == "import") return primImport(state, e2); | ||||||
|  |             if (primop == "derivation") return primDerivation(state, e2); | ||||||
|  |             else throw badTerm("undefined variable/primop", e1); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (ATmatch(e1, "Function([<list>], <term>)", &e3, &e4)) { | ||||||
|  |             return evalExpr(state,  | ||||||
|  |                 substArgs(e4, (ATermList) e3, evalExpr(state, e2))); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         else throw badTerm("expecting a function or primop", e1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Attribute selection. */ | ||||||
|  |     if (ATmatch(e, "Select(<term>, <str>)", &e1, &s1)) { | ||||||
|  |         string name(s1); | ||||||
|  |         Expr a = queryAttr(evalExpr(state, e1), name); | ||||||
|  |         if (!a) throw badTerm(format("missing attribute `%1%'") % name, e); | ||||||
|  |         return evalExpr(state, a); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Mutually recursive sets. */ | ||||||
|  |     ATermList bnds; | ||||||
|  |     if (ATmatch(e, "Rec([<list>])", &bnds)) | ||||||
|  |         return expandRec(e, (ATermList) bnds); | ||||||
|  | 
 | ||||||
|  |     /* Barf. */ | ||||||
|  |     throw badTerm("invalid expression", e); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -20,12 +169,15 @@ Expr evalExpr(EvalState & state, Expr e) | ||||||
| { | { | ||||||
|     Nest nest(lvlVomit, format("evaluating expression: %1%") % printTerm(e)); |     Nest nest(lvlVomit, format("evaluating expression: %1%") % printTerm(e)); | ||||||
| 
 | 
 | ||||||
|  |     state.nrEvaluated++; | ||||||
|  | 
 | ||||||
|     /* Consult the memo table to quickly get the normal form of
 |     /* Consult the memo table to quickly get the normal form of
 | ||||||
|        previously evaluated expressions. */ |        previously evaluated expressions. */ | ||||||
|     NormalForms::iterator i = state.normalForms.find(e); |     NormalForms::iterator i = state.normalForms.find(e); | ||||||
|     if (i != state.normalForms.end()) { |     if (i != state.normalForms.end()) { | ||||||
|         if (i->second == state.blackHole) |         if (i->second == state.blackHole) | ||||||
|             throw badTerm("infinite recursion", e); |             throw badTerm("infinite recursion", e); | ||||||
|  |         state.nrCached++; | ||||||
|         return i->second; |         return i->second; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -43,3 +195,11 @@ Expr evalFile(EvalState & state, const Path & path) | ||||||
|     Expr e = parseExprFromFile(path); |     Expr e = parseExprFromFile(path); | ||||||
|     return evalExpr(state, e); |     return evalExpr(state, e); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void printEvalStats(EvalState & state) | ||||||
|  | { | ||||||
|  |     debug(format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency") | ||||||
|  |         % state.nrEvaluated % state.nrCached | ||||||
|  |         % ((float) state.nrCached / (float) state.nrEvaluated * 100)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -4,19 +4,23 @@ | ||||||
| #include <map> | #include <map> | ||||||
| 
 | 
 | ||||||
| #include "fix-expr.hh" | #include "fix-expr.hh" | ||||||
|  | #include "expr.hh" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| typedef map<Expr, Expr> NormalForms; | typedef map<Expr, Expr> NormalForms; | ||||||
| //typedef map<Path, PathSet> PkgPaths;
 | typedef map<Path, PathSet> DrvPaths; | ||||||
| //typedef map<Path, Hash> PkgHashes;
 | typedef map<Path, Hash> DrvHashes; | ||||||
| 
 | 
 | ||||||
| struct EvalState  | struct EvalState  | ||||||
| { | { | ||||||
|     NormalForms normalForms; |     NormalForms normalForms; | ||||||
|     //    PkgPaths pkgPaths;
 |     DrvPaths drvPaths; | ||||||
|     //    PkgHashes pkgHashes; /* normalised package hashes */
 |     DrvHashes drvHashes; /* normalised derivation hashes */ | ||||||
|     Expr blackHole; |     Expr blackHole; | ||||||
| 
 | 
 | ||||||
|  |     unsigned int nrEvaluated; | ||||||
|  |     unsigned int nrCached; | ||||||
|  | 
 | ||||||
|     EvalState(); |     EvalState(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -27,5 +31,12 @@ Expr evalExpr(EvalState & state, Expr e); | ||||||
| /* Evaluate an expression read from the given file to normal form. */ | /* Evaluate an expression read from the given file to normal form. */ | ||||||
| Expr evalFile(EvalState & state, const Path & path); | Expr evalFile(EvalState & state, const Path & path); | ||||||
| 
 | 
 | ||||||
|  | /* Specific results. */ | ||||||
|  | string evalString(EvalState & state, Expr e); | ||||||
|  | Path evalPath(EvalState & state, Expr e); | ||||||
|  | 
 | ||||||
|  | /* Print statistics. */ | ||||||
|  | void printEvalStats(EvalState & state); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| #endif /* !__EVAL_H */ | #endif /* !__EVAL_H */ | ||||||
|  |  | ||||||
|  | @ -31,3 +31,96 @@ ATerm bottomupRewrite(TermFun & f, ATerm e) | ||||||
| 
 | 
 | ||||||
|     return e; |     return e; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void queryAllAttrs(Expr e, Attrs & attrs) | ||||||
|  | { | ||||||
|  |     ATermList bnds; | ||||||
|  |     if (!ATmatch(e, "Attrs([<list>])", &bnds)) | ||||||
|  |         throw badTerm("expected attribute set", e); | ||||||
|  | 
 | ||||||
|  |     while (!ATisEmpty(bnds)) { | ||||||
|  |         char * s; | ||||||
|  |         Expr e; | ||||||
|  |         if (!ATmatch(ATgetFirst(bnds), "Bind(<str>, <term>)", &s, &e)) | ||||||
|  |             abort(); /* can't happen */ | ||||||
|  |         attrs[s] = e; | ||||||
|  |         bnds = ATgetNext(bnds); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Expr queryAttr(Expr e, const string & name) | ||||||
|  | { | ||||||
|  |     Attrs attrs; | ||||||
|  |     queryAllAttrs(e, attrs); | ||||||
|  |     Attrs::iterator i = attrs.find(name); | ||||||
|  |     return i == attrs.end() ? 0 : i->second; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Expr makeAttrs(const Attrs & attrs) | ||||||
|  | { | ||||||
|  |     ATermList bnds = ATempty; | ||||||
|  |     for (Attrs::const_iterator i = attrs.begin(); i != attrs.end(); i++) | ||||||
|  |         bnds = ATinsert(bnds,  | ||||||
|  |             ATmake("Bind(<str>, <term>)", i->first.c_str(), i->second)); | ||||||
|  |     return ATmake("Attrs(<term>)", ATreverse(bnds)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ATerm substitute(Subs & subs, ATerm e) | ||||||
|  | { | ||||||
|  |     char * s; | ||||||
|  | 
 | ||||||
|  |     if (ATmatch(e, "Var(<str>)", &s)) { | ||||||
|  |         Subs::iterator i = subs.find(s); | ||||||
|  |         if (i == subs.end()) | ||||||
|  |             return e; | ||||||
|  |         else | ||||||
|  |             return i->second; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* In case of a function, filter out all variables bound by this
 | ||||||
|  |        function. */ | ||||||
|  |     ATermList formals; | ||||||
|  |     ATerm body; | ||||||
|  |     if (ATmatch(e, "Function([<list>], <term>)", &formals, &body)) { | ||||||
|  |         Subs subs2(subs); | ||||||
|  |         ATermList fs = formals; | ||||||
|  |         while (!ATisEmpty(fs)) { | ||||||
|  |             if (!ATmatch(ATgetFirst(fs), "<str>", &s)) abort(); | ||||||
|  |             subs2.erase(s); | ||||||
|  |             fs = ATgetNext(fs); | ||||||
|  |         } | ||||||
|  |         return ATmake("Function(<term>, <term>)", formals, | ||||||
|  |             substitute(subs2, body)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* !!! Rec(...) */ | ||||||
|  | 
 | ||||||
|  |     if (ATgetType(e) == AT_APPL) { | ||||||
|  |         AFun fun = ATgetAFun(e); | ||||||
|  |         int arity = ATgetArity(fun); | ||||||
|  |         ATermList args = ATempty; | ||||||
|  | 
 | ||||||
|  |         for (int i = arity - 1; i >= 0; i--) | ||||||
|  |             args = ATinsert(args, substitute(subs, ATgetArgument(e, i))); | ||||||
|  |          | ||||||
|  |         return (ATerm) ATmakeApplList(fun, args); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (ATgetType(e) == AT_LIST) { | ||||||
|  |         ATermList in = (ATermList) e; | ||||||
|  |         ATermList out = ATempty; | ||||||
|  | 
 | ||||||
|  |         while (!ATisEmpty(in)) { | ||||||
|  |             out = ATinsert(out, substitute(subs, ATgetFirst(in))); | ||||||
|  |             in = ATgetNext(in); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return (ATerm) ATreverse(out); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return e; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,8 @@ | ||||||
| #ifndef __FIXEXPR_H | #ifndef __FIXEXPR_H | ||||||
| #define __FIXEXPR_H | #define __FIXEXPR_H | ||||||
| 
 | 
 | ||||||
|  | #include <map> | ||||||
|  | 
 | ||||||
| #include <aterm2.h> | #include <aterm2.h> | ||||||
| 
 | 
 | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
|  | @ -15,13 +17,27 @@ typedef ATerm Expr; | ||||||
| /* Generic bottomup traversal over ATerms.  The traversal first
 | /* Generic bottomup traversal over ATerms.  The traversal first
 | ||||||
|    recursively descends into subterms, and then applies the given term |    recursively descends into subterms, and then applies the given term | ||||||
|    function to the resulting term. */ |    function to the resulting term. */ | ||||||
| 
 |  | ||||||
| struct TermFun | struct TermFun | ||||||
| { | { | ||||||
|     virtual ATerm operator () (ATerm e) = 0; |     virtual ATerm operator () (ATerm e) = 0; | ||||||
| }; | }; | ||||||
| 
 |  | ||||||
| ATerm bottomupRewrite(TermFun & f, ATerm e); | ATerm bottomupRewrite(TermFun & f, ATerm e); | ||||||
| 
 | 
 | ||||||
|  | /* Query all attributes in an attribute set expression.  The
 | ||||||
|  |    expression must be in normal form. */ | ||||||
|  | typedef map<string, Expr> Attrs; | ||||||
|  | void queryAllAttrs(Expr e, Attrs & attrs); | ||||||
|  | 
 | ||||||
|  | /* Query a specific attribute from an attribute set expression.  The
 | ||||||
|  |    expression must be in normal form. */ | ||||||
|  | Expr queryAttr(Expr e, const string & name); | ||||||
|  | 
 | ||||||
|  | /* Create an attribute set expression from an Attrs value. */ | ||||||
|  | Expr makeAttrs(const Attrs & attrs); | ||||||
|  | 
 | ||||||
|  | /* Perform a set of substitutions on an expression. */ | ||||||
|  | typedef map<string, Expr> Subs; | ||||||
|  | ATerm substitute(Subs & subs, ATerm e); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| #endif /* !__FIXEXPR_H */ | #endif /* !__FIXEXPR_H */ | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ | ||||||
| #include "eval.hh" | #include "eval.hh" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #if 0 |  | ||||||
| #if 0 | #if 0 | ||||||
| static Path searchPath(const Paths & searchDirs, const Path & relPath) | static Path searchPath(const Paths & searchDirs, const Path & relPath) | ||||||
| { | { | ||||||
|  | @ -28,178 +27,9 @@ static Path searchPath(const Paths & searchDirs, const Path & relPath) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static Expr substExpr(string x, Expr rep, Expr e) | #if 0 | ||||||
| { |  | ||||||
|     char * s; |  | ||||||
|     Expr e2; |  | ||||||
| 
 |  | ||||||
|     if (ATmatch(e, "Var(<str>)", &s)) |  | ||||||
|         if (x == s) |  | ||||||
|             return rep; |  | ||||||
|         else |  | ||||||
|             return e; |  | ||||||
| 
 |  | ||||||
|     ATermList formals; |  | ||||||
|     if (ATmatch(e, "Function([<list>], <term>)", &formals, &e2)) { |  | ||||||
|         while (!ATisEmpty(formals)) { |  | ||||||
|             if (!ATmatch(ATgetFirst(formals), "<str>", &s)) |  | ||||||
|                 throw badTerm("not a list of formals", (ATerm) formals); |  | ||||||
|             if (x == (string) s) |  | ||||||
|                 return e; |  | ||||||
|             formals = ATgetNext(formals); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Generically substitute in subterms. */ |  | ||||||
| 
 |  | ||||||
|     if (ATgetType(e) == AT_APPL) { |  | ||||||
|         AFun fun = ATgetAFun(e); |  | ||||||
|         int arity = ATgetArity(fun); |  | ||||||
|         ATermList args = ATempty; |  | ||||||
| 
 |  | ||||||
|         for (int i = arity - 1; i >= 0; i--) |  | ||||||
|             args = ATinsert(args, substExpr(x, rep, ATgetArgument(e, i))); |  | ||||||
|          |  | ||||||
|         return (ATerm) ATmakeApplList(fun, args); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (ATgetType(e) == AT_LIST) { |  | ||||||
|         ATermList in = (ATermList) e; |  | ||||||
|         ATermList out = ATempty; |  | ||||||
| 
 |  | ||||||
|         while (!ATisEmpty(in)) { |  | ||||||
|             out = ATinsert(out, substExpr(x, rep, ATgetFirst(in))); |  | ||||||
|             in = ATgetNext(in); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return (ATerm) ATreverse(out); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     throw badTerm("do not know how to substitute", e); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static Expr substExprMany(ATermList formals, ATermList args, Expr body) |  | ||||||
| { |  | ||||||
|     char * s; |  | ||||||
|     Expr e; |  | ||||||
| 
 |  | ||||||
|     /* !!! check args against formals */ |  | ||||||
| 
 |  | ||||||
|     while (!ATisEmpty(args)) { |  | ||||||
|         ATerm tup = ATgetFirst(args); |  | ||||||
|         if (!ATmatch(tup, "(<str>, <term>)", &s, &e)) |  | ||||||
|             throw badTerm("expected an argument tuple", tup); |  | ||||||
| 
 |  | ||||||
|         body = substExpr(s, e, body); |  | ||||||
| 
 |  | ||||||
|         args = ATgetNext(args); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     return body; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static PathSet nixExprRootsCached(EvalState & state, const Path & nePath) |  | ||||||
| { |  | ||||||
|     PkgPaths::iterator i = state.pkgPaths.find(nePath); |  | ||||||
|     if (i != state.pkgPaths.end()) |  | ||||||
|         return i->second; |  | ||||||
|     else { |  | ||||||
|         PathSet paths = nixExprRoots(nePath); |  | ||||||
|         state.pkgPaths[nePath] = paths; |  | ||||||
|         return paths; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static Hash hashPackage(EvalState & state, NixExpr ne) |  | ||||||
| { |  | ||||||
|     if (ne.type == NixExpr::neDerivation) { |  | ||||||
| 	PathSet inputs2; |  | ||||||
|         for (PathSet::iterator i = ne.derivation.inputs.begin(); |  | ||||||
|              i != ne.derivation.inputs.end(); i++) |  | ||||||
|         { |  | ||||||
|             PkgHashes::iterator j = state.pkgHashes.find(*i); |  | ||||||
|             if (j == state.pkgHashes.end()) |  | ||||||
|                 throw Error(format("don't know expression `%1%'") % (string) *i); |  | ||||||
|             inputs2.insert(j->second); |  | ||||||
|         } |  | ||||||
| 	ne.derivation.inputs = inputs2; |  | ||||||
|     } |  | ||||||
|     return hashTerm(unparseNixExpr(ne)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static string processBinding(EvalState & state, Expr e, NixExpr & ne) |  | ||||||
| { |  | ||||||
|     char * s1; |  | ||||||
| 
 |  | ||||||
|     if (ATmatch(e, "NixExpr(<str>)", &s1)) { |  | ||||||
|         Path nePath(s1); |  | ||||||
|         PathSet paths = nixExprRootsCached(state, nePath); |  | ||||||
|         if (paths.size() != 1) abort(); |  | ||||||
|         Path path = *(paths.begin()); |  | ||||||
|         ne.derivation.inputs.insert(nePath); |  | ||||||
|         return path; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     if (ATmatch(e, "<str>", &s1)) |  | ||||||
|         return s1; |  | ||||||
| 
 |  | ||||||
|     if (ATmatch(e, "True")) return "1"; |  | ||||||
|      |  | ||||||
|     if (ATmatch(e, "False")) return ""; |  | ||||||
| 
 |  | ||||||
|     ATermList l; |  | ||||||
|     if (ATmatch(e, "[<list>]", &l)) { |  | ||||||
| 	string s; |  | ||||||
| 	bool first = true; |  | ||||||
|         while (!ATisEmpty(l)) { |  | ||||||
| 	    if (!first) s = s + " "; else first = false; |  | ||||||
| 	    s += processBinding(state, evalExpr(state, ATgetFirst(l)), ne); |  | ||||||
|             l = ATgetNext(l); |  | ||||||
|         } |  | ||||||
| 	return s; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     throw badTerm("invalid package binding", e); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static Expr evalExpr2(EvalState & state, Expr e) | static Expr evalExpr2(EvalState & state, Expr e) | ||||||
| { | { | ||||||
|     char * s1; |  | ||||||
|     Expr e1, e2, e3, e4; |  | ||||||
|     ATermList bnds; |  | ||||||
| 
 |  | ||||||
|     /* Normal forms. */ |  | ||||||
|     if (ATmatch(e, "<str>", &s1) || |  | ||||||
|         ATmatch(e, "[<list>]", &e1) || |  | ||||||
|         ATmatch(e, "True") || |  | ||||||
|         ATmatch(e, "False") || |  | ||||||
|         ATmatch(e, "Function([<list>], <term>)", &e1, &e2) || |  | ||||||
|         ATmatch(e, "NixExpr(<str>)", &s1)) |  | ||||||
|         return e; |  | ||||||
| 
 |  | ||||||
|     try { |  | ||||||
|         Hash pkgHash = hashPackage(state, parseNixExpr(e)); |  | ||||||
|         Path pkgPath = writeTerm(e, ""); |  | ||||||
|         state.pkgHashes[pkgPath] = pkgHash; |  | ||||||
|         return ATmake("NixExpr(<str>)", pkgPath.c_str()); |  | ||||||
|     } catch (...) { /* !!! catch parse errors only */ |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Application. */ |  | ||||||
|     if (ATmatch(e, "Call(<term>, [<list>])", &e1, &e2) || |  | ||||||
|         ATmatch(e, "App(<term>, [<list>])", &e1, &e2)) { |  | ||||||
|         e1 = evalExpr(state, e1); |  | ||||||
|         if (!ATmatch(e1, "Function([<list>], <term>)", &e3, &e4)) |  | ||||||
|             throw badTerm("expecting a function", e1); |  | ||||||
|         return evalExpr(state, |  | ||||||
|             substExprMany((ATermList) e3, (ATermList) e2, e4)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Conditional. */ |     /* Conditional. */ | ||||||
|     if (ATmatch(e, "If(<term>, <term>, <term>)", &e1, &e2, &e3)) { |     if (ATmatch(e, "If(<term>, <term>, <term>)", &e1, &e2, &e3)) { | ||||||
|         e1 = evalExpr(state, e1); |         e1 = evalExpr(state, e1); | ||||||
|  | @ -226,127 +56,6 @@ static Expr evalExpr2(EvalState & state, Expr e) | ||||||
|             ATmake("True") : ATmake("False"); |             ATmake("True") : ATmake("False"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* Platform constant. */ |  | ||||||
|     if (ATmatch(e, "Platform")) { |  | ||||||
|         return ATmake("<str>", thisSystem.c_str()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Fix inclusion. */ |  | ||||||
|     if (ATmatch(e, "IncludeFix(<str>)", &s1)) { |  | ||||||
|         Path fileName(s1); |  | ||||||
|         return evalFile(state, s1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Relative files. */ |  | ||||||
|     if (ATmatch(e, "Relative(<str>)", &s1)) { |  | ||||||
|         Path srcPath = s1; |  | ||||||
|         Path dstPath = addToStore(srcPath); |  | ||||||
| 
 |  | ||||||
|         ClosureElem elem; |  | ||||||
|         NixExpr ne; |  | ||||||
|         ne.type = NixExpr::neClosure; |  | ||||||
|         ne.closure.roots.insert(dstPath); |  | ||||||
|         ne.closure.elems[dstPath] = elem; |  | ||||||
| 
 |  | ||||||
|         Hash pkgHash = hashPackage(state, ne); |  | ||||||
|         Path pkgPath = writeTerm(unparseNixExpr(ne), ""); |  | ||||||
|         state.pkgHashes[pkgPath] = pkgHash; |  | ||||||
| 
 |  | ||||||
|         msg(lvlChatty, format("copied `%1%' -> closure `%2%'") |  | ||||||
|             % srcPath % pkgPath); |  | ||||||
| 
 |  | ||||||
|         return ATmake("NixExpr(<str>)", pkgPath.c_str()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Packages are transformed into Nix derivation expressions. */ |  | ||||||
|     if (ATmatch(e, "Package([<list>])", &bnds)) { |  | ||||||
| 
 |  | ||||||
|         /* Evaluate the bindings and put them in a map. */ |  | ||||||
|         map<string, ATerm> bndMap; |  | ||||||
|         bndMap["platform"] = ATmake("<str>", thisSystem.c_str()); |  | ||||||
|         while (!ATisEmpty(bnds)) { |  | ||||||
|             ATerm bnd = ATgetFirst(bnds); |  | ||||||
|             if (!ATmatch(bnd, "(<str>, <term>)", &s1, &e1)) |  | ||||||
|                 throw badTerm("binding expected", bnd); |  | ||||||
|             bndMap[s1] = evalExpr(state, e1); |  | ||||||
|             bnds = ATgetNext(bnds); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         /* Gather information for building the derivation
 |  | ||||||
|            expression. */ |  | ||||||
|         NixExpr ne; |  | ||||||
|         ne.type = NixExpr::neDerivation; |  | ||||||
|         ne.derivation.platform = thisSystem; |  | ||||||
|         string name; |  | ||||||
|         Path outPath; |  | ||||||
|         Hash outHash; |  | ||||||
|         bool outHashGiven = false; |  | ||||||
|         bnds = ATempty; |  | ||||||
| 
 |  | ||||||
|         for (map<string, ATerm>::iterator it = bndMap.begin(); |  | ||||||
|              it != bndMap.end(); it++) |  | ||||||
|         { |  | ||||||
|             string key = it->first; |  | ||||||
|             ATerm value = it->second; |  | ||||||
| 
 |  | ||||||
|             if (key == "args") { |  | ||||||
|                 ATermList args; |  | ||||||
|                 if (!ATmatch(value, "[<list>]", &args)) |  | ||||||
|                     throw badTerm("list expected", value); |  | ||||||
|                  |  | ||||||
|                 while (!ATisEmpty(args)) { |  | ||||||
|                     Expr arg = evalExpr(state, ATgetFirst(args)); |  | ||||||
|                     ne.derivation.args.push_back(processBinding(state, arg, ne)); |  | ||||||
|                     args = ATgetNext(args); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else { |  | ||||||
|                 string s = processBinding(state, value, ne); |  | ||||||
|                 ne.derivation.env[key] = s; |  | ||||||
| 
 |  | ||||||
|                 if (key == "build") ne.derivation.builder = s; |  | ||||||
|                 if (key == "name") name = s; |  | ||||||
|                 if (key == "outPath") outPath = s; |  | ||||||
|                 if (key == "id") {  |  | ||||||
|                     outHash = parseHash(s); |  | ||||||
|                     outHashGiven = true; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             bnds = ATinsert(bnds,  |  | ||||||
|                 ATmake("(<str>, <term>)", key.c_str(), value)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (ne.derivation.builder == "") |  | ||||||
|             throw badTerm("no builder specified", e); |  | ||||||
|          |  | ||||||
|         if (name == "") |  | ||||||
|             throw badTerm("no package name specified", e); |  | ||||||
|          |  | ||||||
|         /* Determine the output path. */ |  | ||||||
|         if (!outHashGiven) outHash = hashPackage(state, ne); |  | ||||||
|         if (outPath == "") |  | ||||||
|             /* Hash the Nix expression with no outputs to produce a
 |  | ||||||
|                unique but deterministic path name for this package. */ |  | ||||||
|             outPath =  |  | ||||||
|                 canonPath(nixStore + "/" + ((string) outHash).c_str() + "-" + name); |  | ||||||
|         ne.derivation.env["out"] = outPath; |  | ||||||
|         ne.derivation.outputs.insert(outPath); |  | ||||||
| 
 |  | ||||||
|         /* Write the resulting term into the Nix store directory. */ |  | ||||||
|         Hash pkgHash = outHashGiven |  | ||||||
|             ? hashString((string) outHash + outPath) |  | ||||||
|             : hashPackage(state, ne); |  | ||||||
|         Path pkgPath = writeTerm(unparseNixExpr(ne), "-d-" + name); |  | ||||||
|         state.pkgHashes[pkgPath] = pkgHash; |  | ||||||
| 
 |  | ||||||
|         msg(lvlChatty, format("instantiated `%1%' -> `%2%'") |  | ||||||
|             % name % pkgPath); |  | ||||||
| 
 |  | ||||||
|         return ATmake("NixExpr(<str>)", pkgPath.c_str()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* BaseName primitive function. */ |     /* BaseName primitive function. */ | ||||||
|     if (ATmatch(e, "BaseName(<term>)", &e1)) { |     if (ATmatch(e, "BaseName(<term>)", &e1)) { | ||||||
|         e1 = evalExpr(state, e1); |         e1 = evalExpr(state, e1); | ||||||
|  | @ -355,8 +64,6 @@ static Expr evalExpr2(EvalState & state, Expr e) | ||||||
|         return ATmake("<str>", baseNameOf(s1).c_str()); |         return ATmake("<str>", baseNameOf(s1).c_str()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* Barf. */ |  | ||||||
|     throw badTerm("invalid expression", e); |  | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | @ -374,17 +81,27 @@ static Expr evalStdin(EvalState & state) | ||||||
| static void printNixExpr(EvalState & state, Expr e) | static void printNixExpr(EvalState & state, Expr e) | ||||||
| { | { | ||||||
|     ATermList es; |     ATermList es; | ||||||
|     char * s; | 
 | ||||||
|     if (ATmatch(e, "NixExpr(<str>)", &s)) { |     if (ATmatch(e, "Attrs([<list>])", &es)) { | ||||||
|         cout << format("%1%\n") % s; |         Expr a = queryAttr(e, "type"); | ||||||
|  |         if (a && evalString(state, a) == "derivation") { | ||||||
|  |             a = queryAttr(e, "drvPath"); | ||||||
|  |             if (a) { | ||||||
|  |                 cout << format("%1%\n") % evalPath(state, a); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|     else if (ATmatch(e, "[<list>]", &es)) { |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (ATmatch(e, "[<list>]", &es)) { | ||||||
|         while (!ATisEmpty(es)) { |         while (!ATisEmpty(es)) { | ||||||
|             printNixExpr(state, evalExpr(state, ATgetFirst(es))); |             printNixExpr(state, evalExpr(state, ATgetFirst(es))); | ||||||
|             es = ATgetNext(es); |             es = ATgetNext(es); | ||||||
|         } |         } | ||||||
|  |         return; | ||||||
|     } |     } | ||||||
|     else throw badTerm("top level does not evaluate to a (list of) Nix expression(s)", e); | 
 | ||||||
|  |     throw badTerm("top level does not evaluate to one or more Nix expressions", e); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -435,6 +152,8 @@ void run(Strings args) | ||||||
|         Expr e = evalFile(state, absPath(*it)); |         Expr e = evalFile(state, absPath(*it)); | ||||||
|         printNixExpr(state, e); |         printNixExpr(state, e); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     printEvalStats(state); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -44,6 +44,9 @@ exports | ||||||
|     "{" {Id ","}* "}" ":" Expr |     "{" {Id ","}* "}" ":" Expr | ||||||
|     -> Expr {cons("Function"), right} |     -> Expr {cons("Function"), right} | ||||||
| 
 | 
 | ||||||
|  |     "rec" "{" {Bind ","}* "}" | ||||||
|  |     -> Expr {cons("Rec")} | ||||||
|  | 
 | ||||||
|     "{" {Bind ","}* "}" |     "{" {Bind ","}* "}" | ||||||
|     -> Expr {cons("Attrs")} |     -> Expr {cons("Attrs")} | ||||||
| 
 | 
 | ||||||
|  | @ -71,6 +74,7 @@ exports | ||||||
|   sorts Id Path |   sorts Id Path | ||||||
|   lexical syntax |   lexical syntax | ||||||
|     [a-zA-Z\_][a-zA-Z0-9\_\']* -> Id |     [a-zA-Z\_][a-zA-Z0-9\_\']* -> Id | ||||||
|  |     "rec" -> Id {reject} | ||||||
|     [0-9]+ -> Int |     [0-9]+ -> Int | ||||||
|     "\"" ~[\n\"]* "\"" -> Str |     "\"" ~[\n\"]* "\"" -> Str | ||||||
|     PathComp ("/" PathComp)+ -> Path |     PathComp ("/" PathComp)+ -> Path | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ struct Cleanup : TermFun | ||||||
|             string path(s); |             string path(s); | ||||||
|             if (path[0] != '/') |             if (path[0] != '/') | ||||||
|                 path = basePath + "/" + path; |                 path = basePath + "/" + path; | ||||||
|             return ATmake("Str(<str>)", canonPath(path).c_str()); |             return ATmake("Path(<str>)", canonPath(path).c_str()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (ATmatch(e, "Int(<str>)", &s)) { |         if (ATmatch(e, "Int(<str>)", &s)) { | ||||||
|  |  | ||||||
							
								
								
									
										206
									
								
								src/fix-ng/primops.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								src/fix-ng/primops.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,206 @@ | ||||||
|  | #include "primops.hh" | ||||||
|  | #include "normalise.hh" | ||||||
|  | #include "globals.hh" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Expr primImport(EvalState & state, Expr arg) | ||||||
|  | { | ||||||
|  |     char * path; | ||||||
|  |     if (!ATmatch(arg, "Path(<str>)", &path)) | ||||||
|  |         throw badTerm("path expected", arg); | ||||||
|  |     return evalFile(state, path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static PathSet nixExprRootsCached(EvalState & state, const Path & nePath) | ||||||
|  | { | ||||||
|  |     DrvPaths::iterator i = state.drvPaths.find(nePath); | ||||||
|  |     if (i != state.drvPaths.end()) | ||||||
|  |         return i->second; | ||||||
|  |     else { | ||||||
|  |         PathSet paths = nixExprRoots(nePath); | ||||||
|  |         state.drvPaths[nePath] = paths; | ||||||
|  |         return paths; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static Hash hashDerivation(EvalState & state, NixExpr ne) | ||||||
|  | { | ||||||
|  |     if (ne.type == NixExpr::neDerivation) { | ||||||
|  | 	PathSet inputs2; | ||||||
|  |         for (PathSet::iterator i = ne.derivation.inputs.begin(); | ||||||
|  |              i != ne.derivation.inputs.end(); i++) | ||||||
|  |         { | ||||||
|  |             DrvHashes::iterator j = state.drvHashes.find(*i); | ||||||
|  |             if (j == state.drvHashes.end()) | ||||||
|  |                 throw Error(format("don't know expression `%1%'") % (string) *i); | ||||||
|  |             inputs2.insert(j->second); | ||||||
|  |         } | ||||||
|  | 	ne.derivation.inputs = inputs2; | ||||||
|  |     } | ||||||
|  |     return hashTerm(unparseNixExpr(ne)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static Path copyAtom(EvalState & state, const Path & srcPath) | ||||||
|  | { | ||||||
|  |     /* !!! should be cached */ | ||||||
|  |     Path dstPath(addToStore(srcPath)); | ||||||
|  | 
 | ||||||
|  |     ClosureElem elem; | ||||||
|  |     NixExpr ne; | ||||||
|  |     ne.type = NixExpr::neClosure; | ||||||
|  |     ne.closure.roots.insert(dstPath); | ||||||
|  |     ne.closure.elems[dstPath] = elem; | ||||||
|  | 
 | ||||||
|  |     Hash drvHash = hashDerivation(state, ne); | ||||||
|  |     Path drvPath = writeTerm(unparseNixExpr(ne), ""); | ||||||
|  |     state.drvHashes[drvPath] = drvHash; | ||||||
|  | 
 | ||||||
|  |     msg(lvlChatty, format("copied `%1%' -> closure `%2%'") | ||||||
|  |         % srcPath % drvPath); | ||||||
|  |     return drvPath; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static string addInput(EvalState & state,  | ||||||
|  |     Path & nePath, NixExpr & ne) | ||||||
|  | { | ||||||
|  |     PathSet paths = nixExprRootsCached(state, nePath); | ||||||
|  |     if (paths.size() != 1) abort(); | ||||||
|  |     Path path = *(paths.begin()); | ||||||
|  |     ne.derivation.inputs.insert(nePath); | ||||||
|  |     return path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static string processBinding(EvalState & state, Expr e, NixExpr & ne) | ||||||
|  | { | ||||||
|  |     e = evalExpr(state, e); | ||||||
|  | 
 | ||||||
|  |     char * s; | ||||||
|  |     ATermList es; | ||||||
|  | 
 | ||||||
|  |     if (ATmatch(e, "Str(<str>)", &s)) return s; | ||||||
|  |     if (ATmatch(e, "Uri(<str>)", &s)) return s; | ||||||
|  |     if (ATmatch(e, "True")) return "1"; | ||||||
|  |     if (ATmatch(e, "False")) return ""; | ||||||
|  | 
 | ||||||
|  |     if (ATmatch(e, "Attrs([<list>])", &es)) { | ||||||
|  |         Expr a = queryAttr(e, "type"); | ||||||
|  |         if (a && evalString(state, a) == "derivation") { | ||||||
|  |             a = queryAttr(e, "drvPath"); | ||||||
|  |             if (a) { | ||||||
|  |                 Path drvPath = evalPath(state, a); | ||||||
|  |                 return addInput(state, drvPath, ne); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (ATmatch(e, "Path(<str>)", &s)) { | ||||||
|  |         Path drvPath = copyAtom(state, s); | ||||||
|  |         return addInput(state, drvPath, ne); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     if (ATmatch(e, "List([<list>])", &es)) { | ||||||
|  | 	string s; | ||||||
|  | 	bool first = true; | ||||||
|  |         while (!ATisEmpty(es)) { | ||||||
|  |             Nest nest(lvlVomit, format("processing list element")); | ||||||
|  | 	    if (!first) s = s + " "; else first = false; | ||||||
|  | 	    s += processBinding(state, evalExpr(state, ATgetFirst(es)), ne); | ||||||
|  |             es = ATgetNext(es); | ||||||
|  |         } | ||||||
|  | 	return s; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     throw badTerm("invalid derivation binding", e); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Expr primDerivation(EvalState & state, Expr args) | ||||||
|  | { | ||||||
|  |     Nest nest(lvlVomit, "evaluating derivation"); | ||||||
|  | 
 | ||||||
|  |     Attrs attrs; | ||||||
|  |     args = evalExpr(state, args); | ||||||
|  |     queryAllAttrs(args, attrs); | ||||||
|  | 
 | ||||||
|  |     /* Build the derivation expression by processing the attributes. */ | ||||||
|  |     NixExpr ne; | ||||||
|  |     ne.type = NixExpr::neDerivation; | ||||||
|  | 
 | ||||||
|  |     string drvName; | ||||||
|  |     Path outPath; | ||||||
|  |     Hash outHash; | ||||||
|  |     bool outHashGiven = false; | ||||||
|  | 
 | ||||||
|  |     for (Attrs::iterator i = attrs.begin(); i != attrs.end(); i++) { | ||||||
|  |         string key = i->first; | ||||||
|  |         Expr value = i->second; | ||||||
|  |         Nest nest(lvlVomit, format("processing attribute `%1%'") % key); | ||||||
|  | 
 | ||||||
|  |         /* The `args' attribute is special: it supplies the
 | ||||||
|  |            command-line arguments to the builder. */ | ||||||
|  |         if (key == "args") { | ||||||
|  |             ATermList args; | ||||||
|  |             if (!ATmatch(value, "[<list>]", &args)) | ||||||
|  |                 throw badTerm("list expected", value); | ||||||
|  |             while (!ATisEmpty(args)) { | ||||||
|  |                 Expr arg = evalExpr(state, ATgetFirst(args)); | ||||||
|  |                 ne.derivation.args.push_back(processBinding(state, arg, ne)); | ||||||
|  |                 args = ATgetNext(args); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* All other attributes are passed to the builder through the
 | ||||||
|  |            environment. */ | ||||||
|  |         else { | ||||||
|  |             string s = processBinding(state, value, ne); | ||||||
|  |             ne.derivation.env[key] = s; | ||||||
|  |             if (key == "builder") ne.derivation.builder = s; | ||||||
|  |             else if (key == "system") ne.derivation.platform = s; | ||||||
|  |             else if (key == "name") drvName = s; | ||||||
|  |             else if (key == "outPath") outPath = s; | ||||||
|  |             else if (key == "id") {  | ||||||
|  |                 outHash = parseHash(s); | ||||||
|  |                 outHashGiven = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /* Do we have all required attributes? */ | ||||||
|  |     if (ne.derivation.builder == "") | ||||||
|  |         throw badTerm("required attribute `builder' missing", args); | ||||||
|  |     if (ne.derivation.platform == "") | ||||||
|  |         throw badTerm("required attribute `system' missing", args); | ||||||
|  |     if (drvName == "") | ||||||
|  |         throw badTerm("required attribute `name' missing", args); | ||||||
|  |          | ||||||
|  |     /* Determine the output path. */ | ||||||
|  |     if (!outHashGiven) outHash = hashDerivation(state, ne); | ||||||
|  |     if (outPath == "") | ||||||
|  |         /* Hash the Nix expression with no outputs to produce a
 | ||||||
|  |            unique but deterministic path name for this derivation. */ | ||||||
|  |         outPath = canonPath(nixStore + "/" +  | ||||||
|  |             ((string) outHash).c_str() + "-" + drvName); | ||||||
|  |     ne.derivation.env["out"] = outPath; | ||||||
|  |     ne.derivation.outputs.insert(outPath); | ||||||
|  | 
 | ||||||
|  |     /* Write the resulting term into the Nix store directory. */ | ||||||
|  |     Hash drvHash = outHashGiven | ||||||
|  |         ? hashString((string) outHash + outPath) | ||||||
|  |         : hashDerivation(state, ne); | ||||||
|  |     Path drvPath = writeTerm(unparseNixExpr(ne), "-d-" + drvName); | ||||||
|  |     state.drvHashes[drvPath] = drvHash; | ||||||
|  | 
 | ||||||
|  |     msg(lvlChatty, format("instantiated `%1%' -> `%2%'") | ||||||
|  |         % drvName % drvPath); | ||||||
|  | 
 | ||||||
|  |     attrs["outPath"] = ATmake("Path(<str>)", outPath.c_str()); | ||||||
|  |     attrs["drvPath"] = ATmake("Path(<str>)", drvPath.c_str()); | ||||||
|  |     attrs["type"] = ATmake("Str(\"derivation\")"); | ||||||
|  | 
 | ||||||
|  |     return makeAttrs(attrs); | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								src/fix-ng/primops.hh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/fix-ng/primops.hh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | #ifndef __PRIMOPS_H | ||||||
|  | #define __PRIMOPS_H | ||||||
|  | 
 | ||||||
|  | #include "eval.hh" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Load and evaluate an expression from path specified by the
 | ||||||
|  |    argument. */  | ||||||
|  | Expr primImport(EvalState & state, Expr arg); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Construct (as a unobservable) side effect) a Nix derivation
 | ||||||
|  |    expression that performs the derivation described by the argument | ||||||
|  |    set.  Returns the original set extended with the following | ||||||
|  |    attributes: `outPath' containing the primary output path of the | ||||||
|  |    derivation; `drvPath' containing the path of the Nix expression; | ||||||
|  |    and `type' set to `derivation' to indicate that this is a | ||||||
|  |    derivation. */ | ||||||
|  | Expr primDerivation(EvalState & state, Expr args); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif /* !__PRIMOPS_H */ | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue