LTP GCOV extension - code coverage report
Current view: directory - src/libexpr - eval.cc
Test: app.info
Date: 2006-10-11 Instrumented lines: 357
Code covered: 91.9 % Executed lines: 328

       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                 : }

Generated by: LTP GCOV extension version 1.1