1 : #include "eval.hh"
2 : #include "parser.hh"
3 : #include "hash.hh"
4 : #include "util.hh"
5 : #include "store.hh"
6 : #include "nixexpr-ast.hh"
7 :
8 :
9 : namespace nix {
10 :
11 :
12 : EvalState::EvalState()
13 118 : : normalForms(32768), primOps(128)
14 118 : {
15 118 : nrEvaluated = nrCached = 0;
16 :
17 118 : initNixExprHelpers();
18 :
19 118 : addPrimOps();
20 : }
21 :
22 :
23 : void EvalState::addPrimOp(const string & name,
24 : unsigned int arity, PrimOp primOp)
25 3540 : {
26 3540 : primOps.set(toATerm(name), makePrimOpDef(arity, ATmakeBlob(0, (void *) primOp)));
27 : }
28 :
29 :
30 : /* Substitute an argument set into the body of a function. */
31 : static Expr substArgs(EvalState & state,
32 : Expr body, ATermList formals, Expr arg)
33 46 : {
34 46 : unsigned int nrFormals = ATgetLength(formals);
35 46 : ATermMap subs(nrFormals);
36 :
37 : /* Get the actual arguments and put them in the substitution. */
38 46 : ATermMap args(128); /* !!! fix */
39 46 : queryAllAttrs(arg, args);
40 137 : for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
41 91 : subs.set(i->key, i->value);
42 :
43 : /* Get the formal arguments. */
44 46 : ATermVector defsUsed;
45 46 : ATermList recAttrs = ATempty;
46 158 : for (ATermIterator i(formals); i; ++i) {
47 114 : Expr name, def;
48 114 : ValidValues valids2;
49 114 : DefaultValue def2;
50 114 : if (!matchFormal(*i, name, valids2, def2)) abort(); /* can't happen */
51 :
52 114 : Expr value = subs[name];
53 :
54 114 : if (value == 0) {
55 25 : if (!matchDefaultValue(def2, def)) def = 0;
56 25 : if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
57 6 : % aterm2String(name));
58 24 : value = def;
59 24 : defsUsed.push_back(name);
60 24 : recAttrs = ATinsert(recAttrs, makeBind(name, def, makeNoPos()));
61 : }
62 :
63 113 : ATermList valids;
64 113 : if (matchValidValues(valids2, valids)) {
65 2 : value = evalExpr(state, value);
66 2 : bool found = false;
67 5 : for (ATermIterator j(valids); j; ++j) {
68 4 : Expr v = evalExpr(state, *j);
69 4 : if (value == v) {
70 1 : found = true;
71 1 : break;
72 : }
73 : }
74 2 : if (!found) throw TypeError(format("the argument named `%1%' has an illegal value")
75 : % aterm2String(name));
76 : }
77 : }
78 :
79 : /* Make a recursive attribute set out of the (argument-name,
80 : value) tuples. This is so that we can support default
81 : parameters that refer to each other, e.g. ({x, y ? x + x}: y)
82 : {x = "foo";} evaluates to "foofoo". */
83 44 : if (defsUsed.size() != 0) {
84 18 : for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
85 4 : recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
86 14 : Expr rec = makeRec(recAttrs, ATempty);
87 38 : for (ATermVector::iterator i = defsUsed.begin(); i != defsUsed.end(); ++i)
88 24 : subs.set(*i, makeSelect(rec, *i));
89 : }
90 :
91 44 : if (subs.size() != nrFormals) {
92 : /* One or more actual arguments were not declared as formal
93 : arguments. Find out which. */
94 3 : for (ATermIterator i(formals); i; ++i) {
95 2 : Expr name; ATerm d1, d2;
96 2 : if (!matchFormal(*i, name, d1, d2)) abort();
97 2 : subs.remove(name);
98 : }
99 1 : throw TypeError(format("the function does not expect an argument named `%1%'")
100 : % aterm2String(subs.begin()->key));
101 : }
102 :
103 43 : return substitute(Substitution(0, &subs), body);
104 : }
105 :
106 :
107 : /* Transform a mutually recursive set into a non-recursive set. Each
108 : attribute is transformed into an expression that has all references
109 : to attributes substituted with selection expressions on the
110 : original set. E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f
111 : (e.x) (e.y); y = e.x;}'. */
112 : ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds)
113 69 : {
114 69 : ATerm name;
115 69 : Expr e2;
116 69 : Pos pos;
117 :
118 : /* Create the substitution list. */
119 69 : ATermMap subs(ATgetLength(rbnds) + ATgetLength(nrbnds));
120 266 : for (ATermIterator i(rbnds); i; ++i) {
121 197 : if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
122 197 : subs.set(name, makeSelect(e, name));
123 : }
124 70 : for (ATermIterator i(nrbnds); i; ++i) {
125 1 : if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
126 1 : subs.set(name, e2);
127 : }
128 :
129 69 : Substitution subs_(0, &subs);
130 :
131 : /* Create the non-recursive set. */
132 69 : ATermMap as(ATgetLength(rbnds) + ATgetLength(nrbnds));
133 266 : for (ATermIterator i(rbnds); i; ++i) {
134 197 : if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
135 197 : as.set(name, makeAttrRHS(substitute(subs_, e2), pos));
136 : }
137 :
138 : /* Copy the non-recursive bindings. !!! inefficient */
139 70 : for (ATermIterator i(nrbnds); i; ++i) {
140 1 : if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
141 1 : as.set(name, makeAttrRHS(e2, pos));
142 : }
143 :
144 69 : return makeAttrs(as);
145 : }
146 :
147 :
148 : static Expr updateAttrs(Expr e1, Expr e2)
149 49 : {
150 : /* Note: e1 and e2 should be in normal form. */
151 :
152 49 : ATermMap attrs(128); /* !!! */
153 49 : queryAllAttrs(e1, attrs, true);
154 49 : queryAllAttrs(e2, attrs, true);
155 :
156 49 : return makeAttrs(attrs);
157 : }
158 :
159 :
160 : string evalString(EvalState & state, Expr e)
161 565 : {
162 565 : e = evalExpr(state, e);
163 565 : ATerm s;
164 565 : if (!matchStr(e, s))
165 0 : throw TypeError(format("value is %1% while a string was expected") % showType(e));
166 565 : return aterm2String(s);
167 : }
168 :
169 :
170 : Path evalPath(EvalState & state, Expr e)
171 159 : {
172 159 : e = evalExpr(state, e);
173 157 : ATerm s;
174 157 : if (!matchPath(e, s))
175 0 : throw TypeError(format("value is %1% while a path was expected") % showType(e));
176 157 : return aterm2String(s);
177 : }
178 :
179 :
180 : int evalInt(EvalState & state, Expr e)
181 302 : {
182 302 : e = evalExpr(state, e);
183 302 : int i;
184 302 : if (!matchInt(e, i))
185 0 : throw TypeError(format("value is %1% while an integer was expected") % showType(e));
186 302 : return i;
187 : }
188 :
189 :
190 : bool evalBool(EvalState & state, Expr e)
191 180 : {
192 180 : e = evalExpr(state, e);
193 180 : if (e == eTrue) return true;
194 140 : else if (e == eFalse) return false;
195 0 : else throw TypeError(format("value is %1% while a boolean was expected") % showType(e));
196 : }
197 :
198 :
199 : ATermList evalList(EvalState & state, Expr e)
200 275 : {
201 275 : e = evalExpr(state, e);
202 275 : ATermList list;
203 275 : if (!matchList(e, list))
204 0 : throw TypeError(format("value is %1% while a list was expected") % showType(e));
205 275 : return list;
206 : }
207 :
208 :
209 : /* String concatenation and context nodes: in order to allow users to
210 : write things like
211 :
212 : "--with-freetype2-library=" + freetype + "/lib"
213 :
214 : where `freetype' is a derivation, we automatically coerce
215 : derivations into their output path (e.g.,
216 : /nix/store/hashcode-freetype) in concatenations. However, if we do
217 : this naively, we could introduce an undeclared dependency: when the
218 : string is used in another derivation, that derivation would not
219 : have an explicitly dependency on `freetype' in its inputDrvs
220 : field. Thus `freetype' would not necessarily be built.
221 :
222 : To prevent this, we wrap the string resulting from the
223 : concatenation in a *context node*, like this:
224 :
225 : Context([freetype],
226 : Str("--with-freetype2-library=/nix/store/hashcode-freetype/lib"))
227 :
228 : Thus the context is the list of all derivations used in the
229 : computation of a value. These contexts are propagated through
230 : further concatenations. In processBinding() in primops.cc, context
231 : nodes are unwrapped and added to inputDrvs.
232 :
233 : !!! Should the ordering of the context list have a canonical form?
234 :
235 : !!! Contexts are not currently recognised in most places in the
236 : evaluator. */
237 :
238 :
239 : /* Coerce a value to a string, keeping track of contexts. */
240 : string coerceToStringWithContext(EvalState & state,
241 : ATermList & context, Expr e, bool & isPath)
242 145 : {
243 145 : isPath = false;
244 :
245 145 : e = evalExpr(state, e);
246 :
247 144 : bool isWrapped = false;
248 144 : ATermList es;
249 144 : ATerm e2;
250 144 : if (matchContext(e, es, e2)) {
251 0 : isWrapped = true;
252 0 : e = e2;
253 0 : context = ATconcat(es, context);
254 : }
255 :
256 144 : ATerm s;
257 144 : if (matchStr(e, s) || matchUri(e, s))
258 114 : return aterm2String(s);
259 :
260 30 : if (matchPath(e, s)) {
261 23 : isPath = true;
262 23 : Path path = aterm2String(s);
263 23 : if (isInStore(path) && !isWrapped) {
264 0 : context = ATinsert(context, makePath(toATerm(toStorePath(path))));
265 : }
266 23 : return path;
267 : }
268 :
269 7 : if (matchAttrs(e, es)) {
270 7 : ATermMap attrs(128); /* !!! */
271 7 : queryAllAttrs(e, attrs, false);
272 :
273 7 : Expr a = attrs.get(toATerm("type"));
274 7 : if (a && evalString(state, a) == "derivation") {
275 7 : a = attrs.get(toATerm("outPath"));
276 7 : if (!a) throw TypeError("output path missing from derivation");
277 7 : isPath = true;
278 7 : context = ATinsert(context, e);
279 7 : return evalPath(state, a);
280 : }
281 : }
282 :
283 0 : throw TypeError(format("cannot coerce %1% to a string") % showType(e));
284 : }
285 :
286 :
287 : /* Wrap an expression in a context if the context is not empty. */
288 : Expr wrapInContext(ATermList context, Expr e)
289 73 : {
290 73 : return context == ATempty ? e : makeContext(context, e);
291 : }
292 :
293 :
294 : static ATerm concatStrings(EvalState & state, const ATermVector & args)
295 65 : {
296 65 : ATermList context = ATempty;
297 65 : std::ostringstream s;
298 65 : bool isPath = false;
299 :
300 : /* Note that if the first argument in the concatenation is a path,
301 : then the result is also a path. */
302 :
303 194 : for (ATermVector::const_iterator i = args.begin(); i != args.end(); ++i) {
304 130 : bool isPath2;
305 130 : s << coerceToStringWithContext(state, context, *i, isPath2);
306 129 : if (i == args.begin()) isPath = isPath2;
307 : }
308 :
309 64 : return wrapInContext(context, isPath
310 : ? makePath(toATerm(canonPath(s.str())))
311 : : makeStr(toATerm(s.str())));
312 : }
313 :
314 :
315 : Expr autoCallFunction(Expr e, const ATermMap & args)
316 75 : {
317 75 : ATermList formals;
318 75 : ATerm body, pos;
319 :
320 75 : if (matchFunction(e, formals, body, pos)) {
321 9 : ATermMap actualArgs(128);
322 :
323 27 : for (ATermIterator i(formals); i; ++i) {
324 18 : Expr name, def, value; ATerm values, def2;
325 18 : if (!matchFormal(*i, name, values, def2)) abort();
326 18 : if ((value = args.get(name)))
327 0 : actualArgs.set(name, makeAttrRHS(value, makeNoPos()));
328 18 : else if (!matchDefaultValue(def2, def))
329 0 : throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')")
330 : % aterm2String(name));
331 : }
332 :
333 9 : e = makeCall(e, makeAttrs(actualArgs));
334 : }
335 :
336 75 : return e;
337 : }
338 :
339 :
340 : Expr evalExpr2(EvalState & state, Expr e)
341 4324 : {
342 4324 : Expr e1, e2, e3, e4;
343 4324 : ATerm name, pos;
344 4324 : AFun sym = ATgetAFun(e);
345 :
346 : /* Normal forms. */
347 4324 : if (sym == symStr ||
348 : sym == symPath ||
349 : sym == symUri ||
350 : sym == symNull ||
351 : sym == symInt ||
352 : sym == symBool ||
353 : sym == symFunction ||
354 : sym == symFunction1 ||
355 : sym == symAttrs ||
356 : sym == symList ||
357 : sym == symPrimOp ||
358 : sym == symContext)
359 1546 : return e;
360 :
361 : /* The `Closed' constructor is just a way to prevent substitutions
362 : into expressions not containing free variables. */
363 2778 : if (matchClosed(e, e1))
364 573 : return evalExpr(state, e1);
365 :
366 : /* Any encountered variables must be primops (since undefined
367 : variables are detected after parsing). */
368 2205 : if (matchVar(e, name)) {
369 128 : ATerm primOp = state.primOps.get(name);
370 128 : if (!primOp)
371 0 : throw EvalError(format("impossible: undefined variable `%1%'") % aterm2String(name));
372 128 : int arity;
373 128 : ATermBlob fun;
374 128 : if (!matchPrimOpDef(primOp, arity, fun)) abort();
375 128 : if (arity == 0)
376 18 : return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector());
377 : else
378 110 : return makePrimOp(arity, fun, ATempty);
379 : }
380 :
381 : /* Function application. */
382 2077 : if (matchCall(e, e1, e2)) {
383 :
384 1221 : ATermList formals;
385 1221 : ATerm pos;
386 :
387 : /* Evaluate the left-hand side. */
388 1221 : e1 = evalExpr(state, e1);
389 :
390 : /* Is it a primop or a function? */
391 1221 : int arity;
392 1221 : ATermBlob fun;
393 1221 : ATermList args;
394 1221 : if (matchPrimOp(e1, arity, fun, args)) {
395 663 : args = ATinsert(args, e2);
396 663 : if (ATgetLength(args) == arity) {
397 : /* Put the arguments in a vector in reverse (i.e.,
398 : actual) order. */
399 555 : ATermVector args2(arity);
400 1268 : for (ATermIterator i(args); i; ++i)
401 713 : args2[--arity] = *i;
402 555 : return ((PrimOp) ATgetBlobData((ATermBlob) fun))
403 : (state, args2);
404 : } else
405 : /* Need more arguments, so propagate the primop. */
406 108 : return makePrimOp(arity, fun, args);
407 : }
408 :
409 558 : else if (matchFunction(e1, formals, e4, pos)) {
410 46 : e2 = evalExpr(state, e2);
411 46 : try {
412 46 : return evalExpr(state, substArgs(state, e4, formals, e2));
413 4 : } catch (Error & e) {
414 4 : e.addPrefix(format("while evaluating the function at %1%:\n")
415 : % showPos(pos));
416 4 : throw;
417 : }
418 : }
419 :
420 512 : else if (matchFunction1(e1, name, e4, pos)) {
421 512 : try {
422 512 : ATermMap subs(1);
423 512 : subs.set(name, e2);
424 512 : return evalExpr(state, substitute(Substitution(0, &subs), e4));
425 0 : } catch (Error & e) {
426 1 : e.addPrefix(format("while evaluating the function at %1%:\n")
427 : % showPos(pos));
428 1 : throw;
429 : }
430 : }
431 :
432 0 : else throw TypeError(
433 : format("the left-hand side of the function call is neither a function nor a primop (built-in operation) but %1%")
434 : % showType(e1));
435 : }
436 :
437 : /* Attribute selection. */
438 856 : if (matchSelect(e, e1, name)) {
439 321 : ATerm pos;
440 321 : string s1 = aterm2String(name);
441 321 : Expr a = queryAttr(evalExpr(state, e1), s1, pos);
442 319 : if (!a) throw EvalError(format("attribute `%1%' missing") % s1);
443 318 : try {
444 318 : return evalExpr(state, a);
445 9 : } catch (Error & e) {
446 9 : e.addPrefix(format("while evaluating the attribute `%1%' at %2%:\n")
447 : % s1 % showPos(pos));
448 9 : throw;
449 : }
450 : }
451 :
452 : /* Mutually recursive sets. */
453 535 : ATermList rbnds, nrbnds;
454 535 : if (matchRec(e, rbnds, nrbnds))
455 69 : return expandRec(e, rbnds, nrbnds);
456 :
457 : /* Conditionals. */
458 466 : if (matchIf(e, e1, e2, e3)) {
459 153 : if (evalBool(state, e1))
460 17 : return evalExpr(state, e2);
461 : else
462 136 : return evalExpr(state, e3);
463 : }
464 :
465 : /* Assertions. */
466 313 : if (matchAssert(e, e1, e2, pos)) {
467 15 : if (!evalBool(state, e1))
468 3 : throw AssertionError(format("assertion failed at %1%") % showPos(pos));
469 14 : return evalExpr(state, e2);
470 : }
471 :
472 : /* Withs. */
473 298 : if (matchWith(e, e1, e2, pos)) {
474 5 : ATermMap attrs(128); /* !!! */
475 5 : try {
476 5 : e1 = evalExpr(state, e1);
477 5 : queryAllAttrs(e1, attrs);
478 0 : } catch (Error & e) {
479 0 : e.addPrefix(format("while evaluating the `with' definitions at %1%:\n")
480 : % showPos(pos));
481 0 : throw;
482 : }
483 5 : try {
484 5 : e2 = substitute(Substitution(0, &attrs), e2);
485 5 : checkVarDefs(state.primOps, e2);
486 5 : return evalExpr(state, e2);
487 0 : } catch (Error & e) {
488 0 : e.addPrefix(format("while evaluating the `with' body at %1%:\n")
489 : % showPos(pos));
490 0 : throw;
491 : }
492 : }
493 :
494 : /* Generic equality/inequality. Note that the behaviour on
495 : composite data (lists, attribute sets) and functions is
496 : undefined, since the subterms of those terms are not evaluated.
497 : However, we don't want to make (==) strict, because that would
498 : make operations like `big_derivation == null' very slow (unless
499 : we were to evaluate them side-by-side). */
500 293 : if (matchOpEq(e, e1, e2))
501 93 : return makeBool(evalExpr(state, e1) == evalExpr(state, e2));
502 :
503 200 : if (matchOpNEq(e, e1, e2))
504 1 : return makeBool(evalExpr(state, e1) != evalExpr(state, e2));
505 :
506 : /* Negation. */
507 199 : if (matchOpNot(e, e1))
508 3 : return makeBool(!evalBool(state, e1));
509 :
510 : /* Implication. */
511 196 : if (matchOpImpl(e, e1, e2))
512 1 : return makeBool(!evalBool(state, e1) || evalBool(state, e2));
513 :
514 : /* Conjunction (logical AND). */
515 195 : if (matchOpAnd(e, e1, e2))
516 3 : return makeBool(evalBool(state, e1) && evalBool(state, e2));
517 :
518 : /* Disjunction (logical OR). */
519 192 : if (matchOpOr(e, e1, e2))
520 1 : return makeBool(evalBool(state, e1) || evalBool(state, e2));
521 :
522 : /* Attribute set update (//). */
523 191 : if (matchOpUpdate(e, e1, e2))
524 49 : return updateAttrs(evalExpr(state, e1), evalExpr(state, e2));
525 :
526 : /* Attribute existence test (?). */
527 142 : if (matchOpHasAttr(e, e1, name)) {
528 8 : ATermMap attrs(128); /* !!! */
529 8 : queryAllAttrs(evalExpr(state, e1), attrs);
530 8 : return makeBool(attrs.get(name) != 0);
531 : }
532 :
533 : /* String or path concatenation. */
534 134 : ATermList es = ATempty;
535 134 : if (matchOpPlus(e, e1, e2) || matchConcatStrings(e, es)) {
536 65 : ATermVector args;
537 65 : if (matchOpPlus(e, e1, e2)) {
538 63 : args.push_back(e1);
539 63 : args.push_back(e2);
540 : } else
541 2 : for (ATermIterator i(es); i; ++i) args.push_back(*i);
542 :
543 65 : try {
544 65 : return concatStrings(state, args);
545 1 : } catch (Error & e) {
546 1 : e.addPrefix(format("in a string concatenation:\n"));
547 1 : throw;
548 : }
549 : }
550 :
551 : /* Backwards compatability: subpath operator (~). */
552 69 : if (matchSubPath(e, e1, e2)) {
553 7 : static bool haveWarned = false;
554 7 : warnOnce(haveWarned, "the subpath operator (~) is deprecated, use string concatenation (+) instead");
555 7 : ATermList context = ATempty;
556 7 : bool dummy;
557 7 : string s1 = coerceToStringWithContext(state, context, e1, dummy);
558 7 : string s2 = coerceToStringWithContext(state, context, e2, dummy);
559 7 : return wrapInContext(context, makePath(toATerm(canonPath(s1 + "/" + s2))));
560 : }
561 :
562 : /* List concatenation. */
563 62 : if (matchOpConcat(e, e1, e2)) {
564 62 : try {
565 62 : ATermList l1 = evalList(state, e1);
566 62 : ATermList l2 = evalList(state, e2);
567 62 : return makeList(ATconcat(l1, l2));
568 0 : } catch (Error & e) {
569 0 : e.addPrefix(format("in a list concatenation:\n"));
570 0 : throw;
571 : }
572 : }
573 :
574 : /* Barf. */
575 0 : throw badTerm("invalid expression", e);
576 : }
577 :
578 :
579 : Expr evalExpr(EvalState & state, Expr e)
580 6690 : {
581 6690 : checkInterrupt();
582 :
583 6690 : startNest(nest, lvlVomit,
584 : format("evaluating expression: %1%") % e);
585 :
586 6690 : state.nrEvaluated++;
587 :
588 : /* Consult the memo table to quickly get the normal form of
589 : previously evaluated expressions. */
590 6690 : Expr nf = state.normalForms.get(e);
591 6690 : if (nf) {
592 2366 : if (nf == makeBlackHole())
593 2 : throw EvalError("infinite recursion encountered");
594 2364 : state.nrCached++;
595 2364 : return nf;
596 : }
597 :
598 : /* Otherwise, evaluate and memoize. */
599 4324 : state.normalForms.set(e, makeBlackHole());
600 4324 : try {
601 4324 : nf = evalExpr2(state, e);
602 27 : } catch (Error & err) {
603 27 : debug("removing black hole");
604 27 : state.normalForms.remove(e);
605 27 : throw;
606 : }
607 4297 : state.normalForms.set(e, nf);
608 4297 : return nf;
609 : }
610 :
611 :
612 : Expr evalFile(EvalState & state, const Path & path)
613 5 : {
614 5 : startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
615 5 : Expr e = parseExprFromFile(state, path);
616 5 : try {
617 5 : return evalExpr(state, e);
618 0 : } catch (Error & e) {
619 0 : e.addPrefix(format("while evaluating the file `%1%':\n")
620 : % path);
621 0 : throw;
622 : }
623 : }
624 :
625 :
626 : Expr strictEvalExpr(EvalState & state, Expr e, bool canonicalise)
627 17 : {
628 17 : e = evalExpr(state, e);
629 :
630 17 : ATermList as;
631 17 : if (matchAttrs(e, as)) {
632 2 : ATermList as2 = ATempty;
633 9 : for (ATermIterator i(as); i; ++i) {
634 7 : ATerm name; Expr e; ATerm pos;
635 7 : if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
636 7 : as2 = ATinsert(as2, makeBind(name, strictEvalExpr(state, e, canonicalise),
637 : canonicalise ? makeNoPos() : pos));
638 : }
639 : /* !!! sort attributes if canonicalise == true */
640 2 : return makeAttrs(ATreverse(as2));
641 : }
642 :
643 15 : ATermList es;
644 15 : if (matchList(e, es)) {
645 1 : ATermList es2 = ATempty;
646 4 : for (ATermIterator i(es); i; ++i)
647 3 : es2 = ATinsert(es2, strictEvalExpr(state, *i, canonicalise));
648 1 : return makeList(ATreverse(es2));
649 : }
650 :
651 14 : ATermList formals;
652 14 : ATerm body, pos;
653 14 : if (matchFunction(e, formals, body, pos)) {
654 1 : ATermList formals2 = ATempty;
655 :
656 4 : for (ATermIterator i(formals); i; ++i) {
657 3 : Expr name; ValidValues valids; ATerm dummy;
658 3 : if (!matchFormal(*i, name, valids, dummy)) abort();
659 :
660 3 : ATermList valids2;
661 3 : if (matchValidValues(valids, valids2)) {
662 2 : ATermList valids3 = ATempty;
663 7 : for (ATermIterator j(valids2); j; ++j)
664 5 : valids3 = ATinsert(valids3, strictEvalExpr(state, *j, canonicalise));
665 2 : valids = makeValidValues(ATreverse(valids3));
666 : }
667 :
668 3 : formals2 = ATinsert(formals2, makeFormal(name, valids, dummy));
669 : }
670 1 : return makeFunction(ATreverse(formals2), body,
671 : canonicalise ? makeNoPos() : pos);
672 : }
673 :
674 13 : return e;
675 : }
676 :
677 :
678 : /* Yes, this is a really bad idea... */
679 : extern "C" {
680 : unsigned long AT_calcAllocatedSize();
681 : }
682 :
683 : void printEvalStats(EvalState & state)
684 96 : {
685 96 : bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
686 96 : printMsg(showStats ? lvlInfo : lvlDebug,
687 : format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes")
688 : % state.nrEvaluated % state.nrCached
689 : % ((float) state.nrCached / (float) state.nrEvaluated * 100)
690 : % AT_calcAllocatedSize());
691 96 : if (showStats)
692 0 : printATermMapStats();
693 : }
694 :
695 :
696 : }
|