* Started integrating the new evaluator.
This commit is contained in:
		
							parent
							
								
									52090d2418
								
							
						
					
					
						commit
						31428c3a06
					
				
					 8 changed files with 745 additions and 709 deletions
				
			
		|  | @ -6,6 +6,7 @@ | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
| bool isAttrs(EvalState & state, Expr e, ATermMap & attrs) | bool isAttrs(EvalState & state, Expr e, ATermMap & attrs) | ||||||
| { | { | ||||||
|     e = evalExpr(state, e); |     e = evalExpr(state, e); | ||||||
|  | @ -77,6 +78,7 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath, | ||||||
|      |      | ||||||
|     return e; |     return e; | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|   |   | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,668 +10,13 @@ | ||||||
| using namespace nix; | using namespace nix; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| struct Env; |  | ||||||
| struct Value; |  | ||||||
| 
 |  | ||||||
| typedef ATerm Sym; |  | ||||||
| 
 |  | ||||||
| typedef std::map<Sym, Value> Bindings; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| struct Env |  | ||||||
| { |  | ||||||
|     Env * up; |  | ||||||
|     Bindings bindings; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     tInt = 1, |  | ||||||
|     tBool, |  | ||||||
|     tString, |  | ||||||
|     tPath, |  | ||||||
|     tNull, |  | ||||||
|     tAttrs, |  | ||||||
|     tList, |  | ||||||
|     tThunk, |  | ||||||
|     tLambda, |  | ||||||
|     tCopy, |  | ||||||
|     tBlackhole, |  | ||||||
|     tPrimOp, |  | ||||||
|     tPrimOpApp, |  | ||||||
| } ValueType; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| typedef void (* PrimOp_) (Value * * args, Value & v); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| struct Value |  | ||||||
| { |  | ||||||
|     ValueType type; |  | ||||||
|     union  |  | ||||||
|     { |  | ||||||
|         int integer; |  | ||||||
|         bool boolean; |  | ||||||
|         struct { |  | ||||||
|             const char * s; |  | ||||||
|             const char * * context; |  | ||||||
|         } string; |  | ||||||
|         Bindings * attrs; |  | ||||||
|         struct { |  | ||||||
|             unsigned int length; |  | ||||||
|             Value * elems; |  | ||||||
|         } list; |  | ||||||
|         struct { |  | ||||||
|             Env * env; |  | ||||||
|             Expr expr; |  | ||||||
|         } thunk; |  | ||||||
|         struct { |  | ||||||
|             Env * env; |  | ||||||
|             Pattern pat; |  | ||||||
|             Expr body; |  | ||||||
|         } lambda; |  | ||||||
|         Value * val; |  | ||||||
|         struct { |  | ||||||
|             PrimOp_ fun; |  | ||||||
|             unsigned int arity; |  | ||||||
|         } primOp; |  | ||||||
|         struct { |  | ||||||
|             Value * left, * right; |  | ||||||
|             unsigned int argsLeft; |  | ||||||
|         } primOpApp; |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void mkThunk(Value & v, Env & env, Expr expr) |  | ||||||
| { |  | ||||||
|     v.type = tThunk; |  | ||||||
|     v.thunk.env = &env; |  | ||||||
|     v.thunk.expr = expr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void mkInt(Value & v, int n) |  | ||||||
| { |  | ||||||
|     v.type = tInt; |  | ||||||
|     v.integer = n; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void mkBool(Value & v, bool b) |  | ||||||
| { |  | ||||||
|     v.type = tBool; |  | ||||||
|     v.boolean = b; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void mkString(Value & v, const char * s) |  | ||||||
| { |  | ||||||
|     v.type = tString; |  | ||||||
|     v.string.s = s; |  | ||||||
|     v.string.context = 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| std::ostream & operator << (std::ostream & str, Value & v) |  | ||||||
| { |  | ||||||
|     switch (v.type) { |  | ||||||
|     case tInt: |  | ||||||
|         str << v.integer; |  | ||||||
|         break; |  | ||||||
|     case tBool: |  | ||||||
|         str << (v.boolean ? "true" : "false"); |  | ||||||
|         break; |  | ||||||
|     case tString: |  | ||||||
|         str << "\"" << v.string.s << "\""; // !!! escaping
 |  | ||||||
|         break; |  | ||||||
|     case tNull: |  | ||||||
|         str << "true"; |  | ||||||
|         break; |  | ||||||
|     case tAttrs: |  | ||||||
|         str << "{ "; |  | ||||||
|         foreach (Bindings::iterator, i, *v.attrs) |  | ||||||
|             str << aterm2String(i->first) << " = " << i->second << "; "; |  | ||||||
|         str << "}"; |  | ||||||
|         break; |  | ||||||
|     case tList: |  | ||||||
|         str << "[ "; |  | ||||||
|         for (unsigned int n = 0; n < v.list.length; ++n) |  | ||||||
|             str << v.list.elems[n] << " "; |  | ||||||
|         str << "]"; |  | ||||||
|         break; |  | ||||||
|     case tThunk: |  | ||||||
|         str << "<CODE>"; |  | ||||||
|         break; |  | ||||||
|     case tLambda: |  | ||||||
|         str << "<LAMBDA>"; |  | ||||||
|         break; |  | ||||||
|     case tPrimOp: |  | ||||||
|         str << "<PRIMOP>"; |  | ||||||
|         break; |  | ||||||
|     case tPrimOpApp: |  | ||||||
|         str << "<PRIMOP-APP>"; |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         abort(); |  | ||||||
|     } |  | ||||||
|     return str; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void eval(Env & env, Expr e, Value & v); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| string showType(Value & v) |  | ||||||
| { |  | ||||||
|     switch (v.type) { |  | ||||||
|         case tString: return "a string"; |  | ||||||
|         case tPath: return "a path"; |  | ||||||
|         case tNull: return "null"; |  | ||||||
|         case tInt: return "an integer"; |  | ||||||
|         case tBool: return "a boolean"; |  | ||||||
|         case tLambda: return "a function"; |  | ||||||
|         case tAttrs: return "an attribute set"; |  | ||||||
|         case tList: return "a list"; |  | ||||||
|         case tPrimOpApp: return "a partially applied built-in function"; |  | ||||||
|         default: throw Error("unknown type"); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void forceValue(Value & v) |  | ||||||
| { |  | ||||||
|     if (v.type == tThunk) { |  | ||||||
|         v.type = tBlackhole; |  | ||||||
|         eval(*v.thunk.env, v.thunk.expr, v); |  | ||||||
|     } |  | ||||||
|     else if (v.type == tCopy) { |  | ||||||
|         forceValue(*v.val); |  | ||||||
|         v = *v.val; |  | ||||||
|     } |  | ||||||
|     else if (v.type == tBlackhole) |  | ||||||
|         throw EvalError("infinite recursion encountered"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void forceInt(Value & v) |  | ||||||
| { |  | ||||||
|     forceValue(v); |  | ||||||
|     if (v.type != tInt) |  | ||||||
|         throw TypeError(format("value is %1% while an integer was expected") % showType(v)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void forceAttrs(Value & v) |  | ||||||
| { |  | ||||||
|     forceValue(v); |  | ||||||
|     if (v.type != tAttrs) |  | ||||||
|         throw TypeError(format("value is %1% while an attribute set was expected") % showType(v)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void forceList(Value & v) |  | ||||||
| { |  | ||||||
|     forceValue(v); |  | ||||||
|     if (v.type != tList) |  | ||||||
|         throw TypeError(format("value is %1% while a list was expected") % showType(v)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static Value * lookupWith(Env * env, Sym name) |  | ||||||
| { |  | ||||||
|     if (!env) return 0; |  | ||||||
|     Value * v = lookupWith(env->up, name); |  | ||||||
|     if (v) return v; |  | ||||||
|     Bindings::iterator i = env->bindings.find(sWith); |  | ||||||
|     if (i == env->bindings.end()) return 0; |  | ||||||
|     Bindings::iterator j = i->second.attrs->find(name); |  | ||||||
|     if (j != i->second.attrs->end()) return &j->second; |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static Value * lookupVar(Env * env, Sym name) |  | ||||||
| { |  | ||||||
|     /* First look for a regular variable binding for `name'. */ |  | ||||||
|     for (Env * env2 = env; env2; env2 = env2->up) { |  | ||||||
|         Bindings::iterator i = env2->bindings.find(name); |  | ||||||
|         if (i != env2->bindings.end()) return &i->second; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Otherwise, look for a `with' attribute set containing `name'.
 |  | ||||||
|        Outer `withs' take precedence (i.e. `with {x=1;}; with {x=2;}; |  | ||||||
|        x' evaluates to 1).  */ |  | ||||||
|     Value * v = lookupWith(env, name); |  | ||||||
|     if (v) return v; |  | ||||||
| 
 |  | ||||||
|     /* Alternative implementation where the inner `withs' take
 |  | ||||||
|        precedence (i.e. `with {x=1;}; with {x=2;}; x' evaluates to |  | ||||||
|        2). */ |  | ||||||
| #if 0 |  | ||||||
|     for (Env * env2 = env; env2; env2 = env2->up) { |  | ||||||
|         Bindings::iterator i = env2->bindings.find(sWith); |  | ||||||
|         if (i == env2->bindings.end()) continue; |  | ||||||
|         Bindings::iterator j = i->second.attrs->find(name); |  | ||||||
|         if (j != i->second.attrs->end()) return &j->second; |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|      |  | ||||||
|     throw Error("undefined variable"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static bool eqValues(Value & v1, Value & v2) |  | ||||||
| { |  | ||||||
|     forceValue(v1); |  | ||||||
|     forceValue(v2); |  | ||||||
|     switch (v1.type) { |  | ||||||
| 
 |  | ||||||
|         case tInt: |  | ||||||
|             return v2.type == tInt && v1.integer == v2.integer; |  | ||||||
| 
 |  | ||||||
|         case tBool: |  | ||||||
|             return v2.type == tBool && v1.boolean == v2.boolean; |  | ||||||
| 
 |  | ||||||
|         case tList: |  | ||||||
|             if (v2.type != tList || v1.list.length != v2.list.length) return false; |  | ||||||
|             for (unsigned int n = 0; n < v1.list.length; ++n) |  | ||||||
|                 if (!eqValues(v1.list.elems[n], v2.list.elems[n])) return false; |  | ||||||
|             return true; |  | ||||||
| 
 |  | ||||||
|         case tAttrs: { |  | ||||||
|             if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false; |  | ||||||
|             Bindings::iterator i, j; |  | ||||||
|             for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) |  | ||||||
|                 if (!eqValues(i->second, j->second)) return false; |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         default: |  | ||||||
|             throw Error("cannot compare given values"); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| unsigned long nrValues = 0, nrEnvs = 0, nrEvaluated = 0; |  | ||||||
| 
 |  | ||||||
| static Value * allocValues(unsigned int count) |  | ||||||
| { |  | ||||||
|     nrValues += count; |  | ||||||
|     return new Value[count]; // !!! check destructor
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static Env & allocEnv() |  | ||||||
| { |  | ||||||
|     nrEnvs++; |  | ||||||
|     return *(new Env); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| char * p1 = 0, * p2 = 0; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static bool evalBool(Env & env, Expr e) |  | ||||||
| { |  | ||||||
|     Value v; |  | ||||||
|     eval(env, e, v); |  | ||||||
|     if (v.type != tBool) |  | ||||||
|         throw TypeError(format("value is %1% while a Boolean was expected") % showType(v)); |  | ||||||
|     return v.boolean; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void eval(Env & env, Expr e, Value & v) |  | ||||||
| { |  | ||||||
|     char c; |  | ||||||
|     if (!p1) p1 = &c; else if (!p2) p2 = &c; |  | ||||||
| 
 |  | ||||||
|     printMsg(lvlError, format("eval: %1%") % e); |  | ||||||
| 
 |  | ||||||
|     nrEvaluated++; |  | ||||||
| 
 |  | ||||||
|     Sym name; |  | ||||||
|     if (matchVar(e, name)) { |  | ||||||
|         Value * v2 = lookupVar(&env, name); |  | ||||||
|         forceValue(*v2); |  | ||||||
|         v = *v2; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     int n; |  | ||||||
|     if (matchInt(e, n)) { |  | ||||||
|         mkInt(v, n); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ATerm s; ATermList context; |  | ||||||
|     if (matchStr(e, s, context)) { |  | ||||||
|         assert(context == ATempty); |  | ||||||
|         mkString(v, ATgetName(ATgetAFun(s))); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ATermList es; |  | ||||||
|     if (matchAttrs(e, es)) { |  | ||||||
|         v.type = tAttrs; |  | ||||||
|         v.attrs = new Bindings; |  | ||||||
|         ATerm e2, pos; |  | ||||||
|         for (ATermIterator i(es); i; ++i) { |  | ||||||
|             if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ |  | ||||||
|             Value & v2 = (*v.attrs)[name]; |  | ||||||
|             nrValues++; |  | ||||||
|             mkThunk(v2, env, e2); |  | ||||||
|         } |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ATermList rbnds, nrbnds; |  | ||||||
|     if (matchRec(e, rbnds, nrbnds)) { |  | ||||||
|         Env & env2(allocEnv()); |  | ||||||
|         env2.up = &env; |  | ||||||
|          |  | ||||||
|         v.type = tAttrs; |  | ||||||
|         v.attrs = &env2.bindings; |  | ||||||
|         ATerm name, e2, pos; |  | ||||||
|         for (ATermIterator i(rbnds); i; ++i) { |  | ||||||
|             if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ |  | ||||||
|             Value & v2 = env2.bindings[name]; |  | ||||||
|             nrValues++; |  | ||||||
|             mkThunk(v2, env2, e2); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Expr e1, e2; |  | ||||||
|     if (matchSelect(e, e2, name)) { |  | ||||||
|         eval(env, e2, v); |  | ||||||
|         forceAttrs(v); // !!! eval followed by force is slightly inefficient
 |  | ||||||
|         Bindings::iterator i = v.attrs->find(name); |  | ||||||
|         if (i == v.attrs->end()) throw TypeError("attribute not found"); |  | ||||||
|         forceValue(i->second); |  | ||||||
|         v = i->second; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Pattern pat; Expr body; Pos pos; |  | ||||||
|     if (matchFunction(e, pat, body, pos)) { |  | ||||||
|         v.type = tLambda; |  | ||||||
|         v.lambda.env = &env; |  | ||||||
|         v.lambda.pat = pat; |  | ||||||
|         v.lambda.body = body; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Expr fun, arg; |  | ||||||
|     if (matchCall(e, fun, arg)) { |  | ||||||
|         eval(env, fun, v); |  | ||||||
| 
 |  | ||||||
|         if (v.type == tPrimOp || v.type == tPrimOpApp) { |  | ||||||
|             unsigned int argsLeft = |  | ||||||
|                 v.type == tPrimOp ? v.primOp.arity : v.primOpApp.argsLeft; |  | ||||||
|             if (argsLeft == 1) { |  | ||||||
|                 /* We have all the arguments, so call the primop.
 |  | ||||||
|                    First find the primop. */ |  | ||||||
|                 Value * primOp = &v; |  | ||||||
|                 while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left; |  | ||||||
|                 assert(primOp->type == tPrimOp); |  | ||||||
|                 unsigned int arity = primOp->primOp.arity; |  | ||||||
|                  |  | ||||||
|                 Value vLastArg; |  | ||||||
|                 mkThunk(vLastArg, env, arg); |  | ||||||
| 
 |  | ||||||
|                 /* Put all the arguments in an array. */ |  | ||||||
|                 Value * vArgs[arity]; |  | ||||||
|                 unsigned int n = arity - 1; |  | ||||||
|                 vArgs[n--] = &vLastArg; |  | ||||||
|                 for (Value * arg = &v; arg->type == tPrimOpApp; arg = arg->primOpApp.left) |  | ||||||
|                     vArgs[n--] = arg->primOpApp.right; |  | ||||||
| 
 |  | ||||||
|                 /* And call the primop. */ |  | ||||||
|                 primOp->primOp.fun(vArgs, v); |  | ||||||
|             } else { |  | ||||||
|                 Value * v2 = allocValues(2); |  | ||||||
|                 v2[0] = v; |  | ||||||
|                 mkThunk(v2[1], env, arg); |  | ||||||
|                 v.type = tPrimOpApp; |  | ||||||
|                 v.primOpApp.left = &v2[0]; |  | ||||||
|                 v.primOpApp.right = &v2[1]; |  | ||||||
|                 v.primOpApp.argsLeft = argsLeft - 1; |  | ||||||
|             } |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         if (v.type != tLambda) throw TypeError("expected function"); |  | ||||||
| 
 |  | ||||||
|         Env & env2(allocEnv()); |  | ||||||
|         env2.up = &env; |  | ||||||
| 
 |  | ||||||
|         ATermList formals; ATerm ellipsis; |  | ||||||
| 
 |  | ||||||
|         if (matchVarPat(v.lambda.pat, name)) { |  | ||||||
|             Value & vArg = env2.bindings[name]; |  | ||||||
|             nrValues++; |  | ||||||
|             mkThunk(vArg, env, arg); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         else if (matchAttrsPat(v.lambda.pat, formals, ellipsis, name)) { |  | ||||||
|             Value * vArg; |  | ||||||
|             Value vArg_; |  | ||||||
| 
 |  | ||||||
|             if (name == sNoAlias) |  | ||||||
|                 vArg = &vArg_; |  | ||||||
|             else { |  | ||||||
|                 vArg = &env2.bindings[name]; |  | ||||||
|                 nrValues++; |  | ||||||
|             }                 |  | ||||||
| 
 |  | ||||||
|             eval(env, arg, *vArg); |  | ||||||
|             forceAttrs(*vArg); |  | ||||||
|              |  | ||||||
|             /* For each formal argument, get the actual argument.  If
 |  | ||||||
|                there is no matching actual argument but the formal |  | ||||||
|                argument has a default, use the default. */ |  | ||||||
|             unsigned int attrsUsed = 0; |  | ||||||
|             for (ATermIterator i(formals); i; ++i) { |  | ||||||
|                 Expr def; Sym name; |  | ||||||
|                 DefaultValue def2; |  | ||||||
|                 if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ |  | ||||||
| 
 |  | ||||||
|                 Bindings::iterator j = vArg->attrs->find(name); |  | ||||||
|                  |  | ||||||
|                 Value & v = env2.bindings[name]; |  | ||||||
|                 nrValues++; |  | ||||||
|                  |  | ||||||
|                 if (j == vArg->attrs->end()) { |  | ||||||
|                     if (!matchDefaultValue(def2, def)) def = 0; |  | ||||||
|                     if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") |  | ||||||
|                         % aterm2String(name)); |  | ||||||
|                     mkThunk(v, env2, def); |  | ||||||
|                 } else { |  | ||||||
|                     attrsUsed++; |  | ||||||
|                     v.type = tCopy; |  | ||||||
|                     v.val = &j->second; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             /* Check that each actual argument is listed as a formal
 |  | ||||||
|                argument (unless the attribute match specifies a |  | ||||||
|                `...').  TODO: show the names of the |  | ||||||
|                expected/unexpected arguments. */ |  | ||||||
|             if (ellipsis == eFalse && attrsUsed != vArg->attrs->size()) |  | ||||||
|                 throw TypeError("function called with unexpected argument"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         else abort(); |  | ||||||
|          |  | ||||||
|         eval(env2, v.lambda.body, v); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Expr attrs; |  | ||||||
|     if (matchWith(e, attrs, body, pos)) { |  | ||||||
|         Env & env2(allocEnv()); |  | ||||||
|         env2.up = &env; |  | ||||||
| 
 |  | ||||||
|         Value & vAttrs = env2.bindings[sWith]; |  | ||||||
|         nrValues++; |  | ||||||
|         eval(env, attrs, vAttrs); |  | ||||||
|         forceAttrs(vAttrs); |  | ||||||
|          |  | ||||||
|         eval(env2, body, v); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (matchList(e, es)) { |  | ||||||
|         v.type = tList; |  | ||||||
|         v.list.length = ATgetLength(es); |  | ||||||
|         v.list.elems = allocValues(v.list.length); |  | ||||||
|         for (unsigned int n = 0; n < v.list.length; ++n, es = ATgetNext(es)) |  | ||||||
|             mkThunk(v.list.elems[n], env, ATgetFirst(es)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (matchOpEq(e, e1, e2)) { |  | ||||||
|         Value v1; eval(env, e1, v1); |  | ||||||
|         Value v2; eval(env, e2, v2); |  | ||||||
|         mkBool(v, eqValues(v1, v2)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (matchOpNEq(e, e1, e2)) { |  | ||||||
|         Value v1; eval(env, e1, v1); |  | ||||||
|         Value v2; eval(env, e2, v2); |  | ||||||
|         mkBool(v, !eqValues(v1, v2)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (matchOpConcat(e, e1, e2)) { |  | ||||||
|         Value v1; eval(env, e1, v1); |  | ||||||
|         forceList(v1); |  | ||||||
|         Value v2; eval(env, e2, v2); |  | ||||||
|         forceList(v2); |  | ||||||
|         v.type = tList; |  | ||||||
|         v.list.length = v1.list.length + v2.list.length; |  | ||||||
|         v.list.elems = allocValues(v.list.length); |  | ||||||
|         /* !!! This loses sharing with the original lists.  We could
 |  | ||||||
|            use a tCopy node, but that would use more memory. */ |  | ||||||
|         for (unsigned int n = 0; n < v1.list.length; ++n) |  | ||||||
|             v.list.elems[n] = v1.list.elems[n]; |  | ||||||
|         for (unsigned int n = 0; n < v2.list.length; ++n) |  | ||||||
|             v.list.elems[n + v1.list.length] = v2.list.elems[n]; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (matchConcatStrings(e, es)) { |  | ||||||
|         unsigned int n = ATgetLength(es), j = 0; |  | ||||||
|         Value vs[n]; |  | ||||||
|         unsigned int len = 0; |  | ||||||
|         for (ATermIterator i(es); i; ++i, ++j) { |  | ||||||
|             eval(env, *i, vs[j]); |  | ||||||
|             if (vs[j].type != tString) throw TypeError("string expected"); |  | ||||||
|             len += strlen(vs[j].string.s); |  | ||||||
|         } |  | ||||||
|         char * s = new char[len + 1], * t = s; |  | ||||||
|         for (unsigned int i = 0; i < j; ++i) { |  | ||||||
|             strcpy(t, vs[i].string.s); |  | ||||||
|             t += strlen(vs[i].string.s); |  | ||||||
|         } |  | ||||||
|         *t = 0; |  | ||||||
|         mkString(v, s); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Expr e3; |  | ||||||
|     if (matchIf(e, e1, e2, e3)) { |  | ||||||
|         eval(env, evalBool(env, e1) ? e2 : e3, v); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (matchOpOr(e, e1, e2)) { |  | ||||||
|         mkBool(v, evalBool(env, e1) || evalBool(env, e2)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     throw Error("unsupported term"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void strictEval(Env & env, Expr e, Value & v) |  | ||||||
| { |  | ||||||
|     eval(env, e, v); |  | ||||||
|      |  | ||||||
|     if (v.type == tAttrs) { |  | ||||||
|         foreach (Bindings::iterator, i, *v.attrs) |  | ||||||
|             forceValue(i->second); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     else if (v.type == tList) { |  | ||||||
|         for (unsigned int n = 0; n < v.list.length; ++n) |  | ||||||
|             forceValue(v.list.elems[n]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void prim_head(Value * * args, Value & v) |  | ||||||
| { |  | ||||||
|     forceList(*args[0]); |  | ||||||
|     if (args[0]->list.length == 0) |  | ||||||
|         throw Error("`head' called on an empty list"); |  | ||||||
|     forceValue(args[0]->list.elems[0]); |  | ||||||
|     v = args[0]->list.elems[0]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void prim_add(Value * * args, Value & v) |  | ||||||
| { |  | ||||||
|     forceInt(*args[0]); |  | ||||||
|     forceInt(*args[1]); |  | ||||||
|     mkInt(v, args[0]->integer + args[1]->integer); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void addPrimOp(Env & env, const string & name, unsigned int arity, PrimOp_ fun) |  | ||||||
| { |  | ||||||
|     Value & v = env.bindings[toATerm(name)]; |  | ||||||
|     nrValues++; |  | ||||||
|     v.type = tPrimOp; |  | ||||||
|     v.primOp.arity = arity; |  | ||||||
|     v.primOp.fun = fun; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| void doTest(string s) | void doTest(string s) | ||||||
| { | { | ||||||
|     Env baseEnv; |  | ||||||
|     baseEnv.up = 0; |  | ||||||
| 
 |  | ||||||
|     /* Add global constants such as `true' to the base environment. */ |  | ||||||
|     { |  | ||||||
|         Value & v = baseEnv.bindings[toATerm("true")]; |  | ||||||
|         v.type = tBool; |  | ||||||
|         v.boolean = true; |  | ||||||
|     } |  | ||||||
|     { |  | ||||||
|         Value & v = baseEnv.bindings[toATerm("false")]; |  | ||||||
|         v.type = tBool; |  | ||||||
|         v.boolean = false; |  | ||||||
|     } |  | ||||||
|     { |  | ||||||
|         Value & v = baseEnv.bindings[toATerm("null")]; |  | ||||||
|         v.type = tNull; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Add primops to the base environment. */ |  | ||||||
|     addPrimOp(baseEnv, "__head", 1, prim_head); |  | ||||||
|     addPrimOp(baseEnv, "__add", 2, prim_add); |  | ||||||
|      |  | ||||||
|     p1 = p2 = 0; |  | ||||||
|     EvalState state; |     EvalState state; | ||||||
|     Expr e = parseExprFromString(state, s, "/"); |     Expr e = parseExprFromString(state, s, "/"); | ||||||
|     printMsg(lvlError, format(">>>>> %1%") % e); |     printMsg(lvlError, format(">>>>> %1%") % e); | ||||||
|     Value v; |     Value v; | ||||||
|     strictEval(baseEnv, e, v); |     state.strictEval(e, v); | ||||||
|     printMsg(lvlError, format("result: %1%") % v); |     printMsg(lvlError, format("result: %1%") % v); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -721,11 +66,6 @@ void run(Strings args) | ||||||
|     doTest("if false then 1 else 2"); |     doTest("if false then 1 else 2"); | ||||||
|     doTest("if false || true then 1 else 2"); |     doTest("if false || true then 1 else 2"); | ||||||
|     doTest("let x = x; in if true || x then 1 else 2"); |     doTest("let x = x; in if true || x then 1 else 2"); | ||||||
|      |  | ||||||
|     printMsg(lvlError, format("alloced %1% values") % nrValues); |  | ||||||
|     printMsg(lvlError, format("alloced %1% environments") % nrEnvs); |  | ||||||
|     printMsg(lvlError, format("evaluated %1% expressions") % nrEvaluated); |  | ||||||
|     printMsg(lvlError, format("each eval() uses %1% bytes of stack space") % (p1 - p2)); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,6 +7,8 @@ | ||||||
| #include "nixexpr-ast.hh" | #include "nixexpr-ast.hh" | ||||||
| #include "globals.hh" | #include "globals.hh" | ||||||
| 
 | 
 | ||||||
|  | #include <cstring> | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| #define LocalNoInline(f) static f __attribute__((noinline)); f | #define LocalNoInline(f) static f __attribute__((noinline)); f | ||||||
| #define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f | #define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f | ||||||
|  | @ -15,14 +17,76 @@ | ||||||
| namespace nix { | namespace nix { | ||||||
|      |      | ||||||
| 
 | 
 | ||||||
| EvalState::EvalState() | std::ostream & operator << (std::ostream & str, Value & v) | ||||||
|     : normalForms(32768), primOps(128) |  | ||||||
| { | { | ||||||
|     nrEvaluated = nrCached = 0; |     switch (v.type) { | ||||||
|  |     case tInt: | ||||||
|  |         str << v.integer; | ||||||
|  |         break; | ||||||
|  |     case tBool: | ||||||
|  |         str << (v.boolean ? "true" : "false"); | ||||||
|  |         break; | ||||||
|  |     case tString: | ||||||
|  |         str << "\"" << v.string.s << "\""; // !!! escaping
 | ||||||
|  |         break; | ||||||
|  |     case tNull: | ||||||
|  |         str << "true"; | ||||||
|  |         break; | ||||||
|  |     case tAttrs: | ||||||
|  |         str << "{ "; | ||||||
|  |         foreach (Bindings::iterator, i, *v.attrs) | ||||||
|  |             str << aterm2String(i->first) << " = " << i->second << "; "; | ||||||
|  |         str << "}"; | ||||||
|  |         break; | ||||||
|  |     case tList: | ||||||
|  |         str << "[ "; | ||||||
|  |         for (unsigned int n = 0; n < v.list.length; ++n) | ||||||
|  |             str << v.list.elems[n] << " "; | ||||||
|  |         str << "]"; | ||||||
|  |         break; | ||||||
|  |     case tThunk: | ||||||
|  |         str << "<CODE>"; | ||||||
|  |         break; | ||||||
|  |     case tLambda: | ||||||
|  |         str << "<LAMBDA>"; | ||||||
|  |         break; | ||||||
|  |     case tPrimOp: | ||||||
|  |         str << "<PRIMOP>"; | ||||||
|  |         break; | ||||||
|  |     case tPrimOpApp: | ||||||
|  |         str << "<PRIMOP-APP>"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         throw Error("invalid value"); | ||||||
|  |     } | ||||||
|  |     return str; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | string showType(Value & v) | ||||||
|  | { | ||||||
|  |     switch (v.type) { | ||||||
|  |         case tString: return "a string"; | ||||||
|  |         case tPath: return "a path"; | ||||||
|  |         case tNull: return "null"; | ||||||
|  |         case tInt: return "an integer"; | ||||||
|  |         case tBool: return "a boolean"; | ||||||
|  |         case tLambda: return "a function"; | ||||||
|  |         case tAttrs: return "an attribute set"; | ||||||
|  |         case tList: return "a list"; | ||||||
|  |         case tPrimOpApp: return "a partially applied built-in function"; | ||||||
|  |         default: throw Error("unknown type"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | EvalState::EvalState() : baseEnv(allocEnv()) | ||||||
|  | { | ||||||
|  |     nrValues = nrEnvs = nrEvaluated = 0; | ||||||
| 
 | 
 | ||||||
|     initNixExprHelpers(); |     initNixExprHelpers(); | ||||||
| 
 | 
 | ||||||
|     addPrimOps(); |     createBaseEnv(); | ||||||
|      |      | ||||||
|     allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == ""; |     allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == ""; | ||||||
| } | } | ||||||
|  | @ -31,7 +95,11 @@ EvalState::EvalState() | ||||||
| void EvalState::addPrimOp(const string & name, | void EvalState::addPrimOp(const string & name, | ||||||
|     unsigned int arity, PrimOp primOp) |     unsigned int arity, PrimOp primOp) | ||||||
| { | { | ||||||
|     primOps.set(toATerm(name), makePrimOpDef(arity, ATmakeBlob(0, (void *) primOp))); |     Value & v = baseEnv.bindings[toATerm(name)]; | ||||||
|  |     nrValues++; | ||||||
|  |     v.type = tPrimOp; | ||||||
|  |     v.primOp.arity = arity; | ||||||
|  |     v.primOp.fun = primOp; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -76,6 +144,471 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static void mkThunk(Value & v, Env & env, Expr expr) | ||||||
|  | { | ||||||
|  |     v.type = tThunk; | ||||||
|  |     v.thunk.env = &env; | ||||||
|  |     v.thunk.expr = expr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static Value * lookupWith(Env * env, Sym name) | ||||||
|  | { | ||||||
|  |     if (!env) return 0; | ||||||
|  |     Value * v = lookupWith(env->up, name); | ||||||
|  |     if (v) return v; | ||||||
|  |     Bindings::iterator i = env->bindings.find(sWith); | ||||||
|  |     if (i == env->bindings.end()) return 0; | ||||||
|  |     Bindings::iterator j = i->second.attrs->find(name); | ||||||
|  |     if (j != i->second.attrs->end()) return &j->second; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static Value * lookupVar(Env * env, Sym name) | ||||||
|  | { | ||||||
|  |     /* First look for a regular variable binding for `name'. */ | ||||||
|  |     for (Env * env2 = env; env2; env2 = env2->up) { | ||||||
|  |         Bindings::iterator i = env2->bindings.find(name); | ||||||
|  |         if (i != env2->bindings.end()) return &i->second; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Otherwise, look for a `with' attribute set containing `name'.
 | ||||||
|  |        Outer `withs' take precedence (i.e. `with {x=1;}; with {x=2;}; | ||||||
|  |        x' evaluates to 1).  */ | ||||||
|  |     Value * v = lookupWith(env, name); | ||||||
|  |     if (v) return v; | ||||||
|  | 
 | ||||||
|  |     /* Alternative implementation where the inner `withs' take
 | ||||||
|  |        precedence (i.e. `with {x=1;}; with {x=2;}; x' evaluates to | ||||||
|  |        2). */ | ||||||
|  | #if 0 | ||||||
|  |     for (Env * env2 = env; env2; env2 = env2->up) { | ||||||
|  |         Bindings::iterator i = env2->bindings.find(sWith); | ||||||
|  |         if (i == env2->bindings.end()) continue; | ||||||
|  |         Bindings::iterator j = i->second.attrs->find(name); | ||||||
|  |         if (j != i->second.attrs->end()) return &j->second; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |      | ||||||
|  |     throw Error("undefined variable"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Value * EvalState::allocValues(unsigned int count) | ||||||
|  | { | ||||||
|  |     nrValues += count; | ||||||
|  |     return new Value[count]; // !!! check destructor
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Env & EvalState::allocEnv() | ||||||
|  | { | ||||||
|  |     nrEnvs++; | ||||||
|  |     return *(new Env); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static char * deepestStack = (char *) -1; /* for measuring stack usage */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void EvalState::eval(Env & env, Expr e, Value & v) | ||||||
|  | { | ||||||
|  |     /* When changing this function, make sure that you don't cause a
 | ||||||
|  |        (large) increase in stack consumption! */ | ||||||
|  |      | ||||||
|  |     char x; | ||||||
|  |     if (&x < deepestStack) deepestStack = &x; | ||||||
|  |      | ||||||
|  |     printMsg(lvlError, format("eval: %1%") % e); | ||||||
|  | 
 | ||||||
|  |     nrEvaluated++; | ||||||
|  | 
 | ||||||
|  |     Sym name; | ||||||
|  |     if (matchVar(e, name)) { | ||||||
|  |         Value * v2 = lookupVar(&env, name); | ||||||
|  |         forceValue(*v2); | ||||||
|  |         v = *v2; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int n; | ||||||
|  |     if (matchInt(e, n)) { | ||||||
|  |         mkInt(v, n); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ATerm s; ATermList context; | ||||||
|  |     if (matchStr(e, s, context)) { | ||||||
|  |         assert(context == ATempty); | ||||||
|  |         mkString(v, ATgetName(ATgetAFun(s))); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ATermList es; | ||||||
|  |     if (matchAttrs(e, es)) { | ||||||
|  |         v.type = tAttrs; | ||||||
|  |         v.attrs = new Bindings; | ||||||
|  |         ATerm e2, pos; | ||||||
|  |         for (ATermIterator i(es); i; ++i) { | ||||||
|  |             if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ | ||||||
|  |             Value & v2 = (*v.attrs)[name]; | ||||||
|  |             nrValues++; | ||||||
|  |             mkThunk(v2, env, e2); | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ATermList rbnds, nrbnds; | ||||||
|  |     if (matchRec(e, rbnds, nrbnds)) { | ||||||
|  |         Env & env2(allocEnv()); | ||||||
|  |         env2.up = &env; | ||||||
|  |          | ||||||
|  |         v.type = tAttrs; | ||||||
|  |         v.attrs = &env2.bindings; | ||||||
|  |         ATerm name, e2, pos; | ||||||
|  |         for (ATermIterator i(rbnds); i; ++i) { | ||||||
|  |             if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ | ||||||
|  |             Value & v2 = env2.bindings[name]; | ||||||
|  |             nrValues++; | ||||||
|  |             mkThunk(v2, env2, e2); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Expr e1, e2; | ||||||
|  |     if (matchSelect(e, e2, name)) { | ||||||
|  |         eval(env, e2, v); | ||||||
|  |         forceAttrs(v); // !!! eval followed by force is slightly inefficient
 | ||||||
|  |         Bindings::iterator i = v.attrs->find(name); | ||||||
|  |         if (i == v.attrs->end()) throw TypeError("attribute not found"); | ||||||
|  |         forceValue(i->second); | ||||||
|  |         v = i->second; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Pattern pat; Expr body; Pos pos; | ||||||
|  |     if (matchFunction(e, pat, body, pos)) { | ||||||
|  |         v.type = tLambda; | ||||||
|  |         v.lambda.env = &env; | ||||||
|  |         v.lambda.pat = pat; | ||||||
|  |         v.lambda.body = body; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Expr fun, arg; | ||||||
|  |     if (matchCall(e, fun, arg)) { | ||||||
|  |         eval(env, fun, v); | ||||||
|  | 
 | ||||||
|  |         if (v.type == tPrimOp || v.type == tPrimOpApp) { | ||||||
|  |             unsigned int argsLeft = | ||||||
|  |                 v.type == tPrimOp ? v.primOp.arity : v.primOpApp.argsLeft; | ||||||
|  |             if (argsLeft == 1) { | ||||||
|  |                 /* We have all the arguments, so call the primop.
 | ||||||
|  |                    First find the primop. */ | ||||||
|  |                 Value * primOp = &v; | ||||||
|  |                 while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left; | ||||||
|  |                 assert(primOp->type == tPrimOp); | ||||||
|  |                 unsigned int arity = primOp->primOp.arity; | ||||||
|  |                  | ||||||
|  |                 Value vLastArg; | ||||||
|  |                 mkThunk(vLastArg, env, arg); | ||||||
|  | 
 | ||||||
|  |                 /* Put all the arguments in an array. */ | ||||||
|  |                 Value * vArgs[arity]; | ||||||
|  |                 unsigned int n = arity - 1; | ||||||
|  |                 vArgs[n--] = &vLastArg; | ||||||
|  |                 for (Value * arg = &v; arg->type == tPrimOpApp; arg = arg->primOpApp.left) | ||||||
|  |                     vArgs[n--] = arg->primOpApp.right; | ||||||
|  | 
 | ||||||
|  |                 /* And call the primop. */ | ||||||
|  |                 primOp->primOp.fun(*this, vArgs, v); | ||||||
|  |             } else { | ||||||
|  |                 Value * v2 = allocValues(2); | ||||||
|  |                 v2[0] = v; | ||||||
|  |                 mkThunk(v2[1], env, arg); | ||||||
|  |                 v.type = tPrimOpApp; | ||||||
|  |                 v.primOpApp.left = &v2[0]; | ||||||
|  |                 v.primOpApp.right = &v2[1]; | ||||||
|  |                 v.primOpApp.argsLeft = argsLeft - 1; | ||||||
|  |             } | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (v.type != tLambda) throw TypeError("expected function"); | ||||||
|  | 
 | ||||||
|  |         Env & env2(allocEnv()); | ||||||
|  |         env2.up = &env; | ||||||
|  | 
 | ||||||
|  |         ATermList formals; ATerm ellipsis; | ||||||
|  | 
 | ||||||
|  |         if (matchVarPat(v.lambda.pat, name)) { | ||||||
|  |             Value & vArg = env2.bindings[name]; | ||||||
|  |             nrValues++; | ||||||
|  |             mkThunk(vArg, env, arg); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (matchAttrsPat(v.lambda.pat, formals, ellipsis, name)) { | ||||||
|  |             Value * vArg; | ||||||
|  |             Value vArg_; | ||||||
|  | 
 | ||||||
|  |             if (name == sNoAlias) | ||||||
|  |                 vArg = &vArg_; | ||||||
|  |             else { | ||||||
|  |                 vArg = &env2.bindings[name]; | ||||||
|  |                 nrValues++; | ||||||
|  |             }                 | ||||||
|  | 
 | ||||||
|  |             eval(env, arg, *vArg); | ||||||
|  |             forceAttrs(*vArg); | ||||||
|  |              | ||||||
|  |             /* For each formal argument, get the actual argument.  If
 | ||||||
|  |                there is no matching actual argument but the formal | ||||||
|  |                argument has a default, use the default. */ | ||||||
|  |             unsigned int attrsUsed = 0; | ||||||
|  |             for (ATermIterator i(formals); i; ++i) { | ||||||
|  |                 Expr def; Sym name; | ||||||
|  |                 DefaultValue def2; | ||||||
|  |                 if (!matchFormal(*i, name, def2)) abort(); /* can't happen */ | ||||||
|  | 
 | ||||||
|  |                 Bindings::iterator j = vArg->attrs->find(name); | ||||||
|  |                  | ||||||
|  |                 Value & v = env2.bindings[name]; | ||||||
|  |                 nrValues++; | ||||||
|  |                  | ||||||
|  |                 if (j == vArg->attrs->end()) { | ||||||
|  |                     if (!matchDefaultValue(def2, def)) def = 0; | ||||||
|  |                     if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") | ||||||
|  |                         % aterm2String(name)); | ||||||
|  |                     mkThunk(v, env2, def); | ||||||
|  |                 } else { | ||||||
|  |                     attrsUsed++; | ||||||
|  |                     v.type = tCopy; | ||||||
|  |                     v.val = &j->second; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             /* Check that each actual argument is listed as a formal
 | ||||||
|  |                argument (unless the attribute match specifies a | ||||||
|  |                `...').  TODO: show the names of the | ||||||
|  |                expected/unexpected arguments. */ | ||||||
|  |             if (ellipsis == eFalse && attrsUsed != vArg->attrs->size()) | ||||||
|  |                 throw TypeError("function called with unexpected argument"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else abort(); | ||||||
|  |          | ||||||
|  |         eval(env2, v.lambda.body, v); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Expr attrs; | ||||||
|  |     if (matchWith(e, attrs, body, pos)) { | ||||||
|  |         Env & env2(allocEnv()); | ||||||
|  |         env2.up = &env; | ||||||
|  | 
 | ||||||
|  |         Value & vAttrs = env2.bindings[sWith]; | ||||||
|  |         nrValues++; | ||||||
|  |         eval(env, attrs, vAttrs); | ||||||
|  |         forceAttrs(vAttrs); | ||||||
|  |          | ||||||
|  |         eval(env2, body, v); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (matchList(e, es)) { | ||||||
|  |         v.type = tList; | ||||||
|  |         v.list.length = ATgetLength(es); | ||||||
|  |         v.list.elems = allocValues(v.list.length); | ||||||
|  |         for (unsigned int n = 0; n < v.list.length; ++n, es = ATgetNext(es)) | ||||||
|  |             mkThunk(v.list.elems[n], env, ATgetFirst(es)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (matchOpEq(e, e1, e2)) { | ||||||
|  |         Value v1; eval(env, e1, v1); | ||||||
|  |         Value v2; eval(env, e2, v2); | ||||||
|  |         mkBool(v, eqValues(v1, v2)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (matchOpNEq(e, e1, e2)) { | ||||||
|  |         Value v1; eval(env, e1, v1); | ||||||
|  |         Value v2; eval(env, e2, v2); | ||||||
|  |         mkBool(v, !eqValues(v1, v2)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (matchOpConcat(e, e1, e2)) { | ||||||
|  |         Value v1; eval(env, e1, v1); | ||||||
|  |         forceList(v1); | ||||||
|  |         Value v2; eval(env, e2, v2); | ||||||
|  |         forceList(v2); | ||||||
|  |         v.type = tList; | ||||||
|  |         v.list.length = v1.list.length + v2.list.length; | ||||||
|  |         v.list.elems = allocValues(v.list.length); | ||||||
|  |         /* !!! This loses sharing with the original lists.  We could
 | ||||||
|  |            use a tCopy node, but that would use more memory. */ | ||||||
|  |         for (unsigned int n = 0; n < v1.list.length; ++n) | ||||||
|  |             v.list.elems[n] = v1.list.elems[n]; | ||||||
|  |         for (unsigned int n = 0; n < v2.list.length; ++n) | ||||||
|  |             v.list.elems[n + v1.list.length] = v2.list.elems[n]; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (matchConcatStrings(e, es)) { | ||||||
|  |         unsigned int n = ATgetLength(es), j = 0; | ||||||
|  |         Value vs[n]; | ||||||
|  |         unsigned int len = 0; | ||||||
|  |         for (ATermIterator i(es); i; ++i, ++j) { | ||||||
|  |             eval(env, *i, vs[j]); | ||||||
|  |             if (vs[j].type != tString) throw TypeError("string expected"); | ||||||
|  |             len += strlen(vs[j].string.s); | ||||||
|  |         } | ||||||
|  |         char * s = new char[len + 1], * t = s; | ||||||
|  |         for (unsigned int i = 0; i < j; ++i) { | ||||||
|  |             strcpy(t, vs[i].string.s); | ||||||
|  |             t += strlen(vs[i].string.s); | ||||||
|  |         } | ||||||
|  |         *t = 0; | ||||||
|  |         mkString(v, s); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Expr e3; | ||||||
|  |     if (matchIf(e, e1, e2, e3)) { | ||||||
|  |         eval(env, evalBool(env, e1) ? e2 : e3, v); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (matchOpOr(e, e1, e2)) { | ||||||
|  |         mkBool(v, evalBool(env, e1) || evalBool(env, e2)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     throw Error("unsupported term"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void EvalState::eval(Expr e, Value & v) | ||||||
|  | { | ||||||
|  |     eval(baseEnv, e, v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | bool EvalState::evalBool(Env & env, Expr e) | ||||||
|  | { | ||||||
|  |     Value v; | ||||||
|  |     eval(env, e, v); | ||||||
|  |     if (v.type != tBool) | ||||||
|  |         throw TypeError(format("value is %1% while a Boolean was expected") % showType(v)); | ||||||
|  |     return v.boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void EvalState::strictEval(Env & env, Expr e, Value & v) | ||||||
|  | { | ||||||
|  |     eval(env, e, v); | ||||||
|  |      | ||||||
|  |     if (v.type == tAttrs) { | ||||||
|  |         foreach (Bindings::iterator, i, *v.attrs) | ||||||
|  |             forceValue(i->second); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     else if (v.type == tList) { | ||||||
|  |         for (unsigned int n = 0; n < v.list.length; ++n) | ||||||
|  |             forceValue(v.list.elems[n]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void EvalState::strictEval(Expr e, Value & v) | ||||||
|  | { | ||||||
|  |     strictEval(baseEnv, e, v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void EvalState::forceValue(Value & v) | ||||||
|  | { | ||||||
|  |     if (v.type == tThunk) { | ||||||
|  |         v.type = tBlackhole; | ||||||
|  |         eval(*v.thunk.env, v.thunk.expr, v); | ||||||
|  |     } | ||||||
|  |     else if (v.type == tCopy) { | ||||||
|  |         forceValue(*v.val); | ||||||
|  |         v = *v.val; | ||||||
|  |     } | ||||||
|  |     else if (v.type == tBlackhole) | ||||||
|  |         throw EvalError("infinite recursion encountered"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int EvalState::forceInt(Value & v) | ||||||
|  | { | ||||||
|  |     forceValue(v); | ||||||
|  |     if (v.type != tInt) | ||||||
|  |         throw TypeError(format("value is %1% while an integer was expected") % showType(v)); | ||||||
|  |     return v.integer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void EvalState::forceAttrs(Value & v) | ||||||
|  | { | ||||||
|  |     forceValue(v); | ||||||
|  |     if (v.type != tAttrs) | ||||||
|  |         throw TypeError(format("value is %1% while an attribute set was expected") % showType(v)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void EvalState::forceList(Value & v) | ||||||
|  | { | ||||||
|  |     forceValue(v); | ||||||
|  |     if (v.type != tList) | ||||||
|  |         throw TypeError(format("value is %1% while a list was expected") % showType(v)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | bool EvalState::eqValues(Value & v1, Value & v2) | ||||||
|  | { | ||||||
|  |     forceValue(v1); | ||||||
|  |     forceValue(v2); | ||||||
|  | 
 | ||||||
|  |     if (v1.type != v2.type) return false; | ||||||
|  |      | ||||||
|  |     switch (v1.type) { | ||||||
|  | 
 | ||||||
|  |         case tInt: | ||||||
|  |             return v1.integer == v2.integer; | ||||||
|  | 
 | ||||||
|  |         case tBool: | ||||||
|  |             return v1.boolean == v2.boolean; | ||||||
|  | 
 | ||||||
|  |         case tString: | ||||||
|  |             /* !!! contexts */ | ||||||
|  |             return strcmp(v1.string.s, v2.string.s) == 0; | ||||||
|  | 
 | ||||||
|  |         case tList: | ||||||
|  |             if (v2.type != tList || v1.list.length != v2.list.length) return false; | ||||||
|  |             for (unsigned int n = 0; n < v1.list.length; ++n) | ||||||
|  |                 if (!eqValues(v1.list.elems[n], v2.list.elems[n])) return false; | ||||||
|  |             return true; | ||||||
|  | 
 | ||||||
|  |         case tAttrs: { | ||||||
|  |             if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false; | ||||||
|  |             Bindings::iterator i, j; | ||||||
|  |             for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) | ||||||
|  |                 if (!eqValues(i->second, j->second)) return false; | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             throw Error("cannot compare given values"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #if 0 | ||||||
| /* Pattern-match `pat' against `arg'.  The result is a set of
 | /* Pattern-match `pat' against `arg'.  The result is a set of
 | ||||||
|    substitutions (`subs') and a set of recursive substitutions |    substitutions (`subs') and a set of recursive substitutions | ||||||
|    (`subsRecursive').  The latter can refer to the variables bound by |    (`subsRecursive').  The latter can refer to the variables bound by | ||||||
|  | @ -683,9 +1216,6 @@ LocalNoInline(bool areEqual(EvalState & state, Expr e1, Expr e2)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static char * deepestStack = (char *) -1; /* for measuring stack usage */ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Expr evalExpr2(EvalState & state, Expr e) | Expr evalExpr2(EvalState & state, Expr e) | ||||||
| { | { | ||||||
|     /* When changing this function, make sure that you don't cause a
 |     /* When changing this function, make sure that you don't cause a
 | ||||||
|  | @ -884,23 +1414,19 @@ Expr strictEvalExpr(EvalState & state, Expr e) | ||||||
|     ATermMap strictNormalForms; |     ATermMap strictNormalForms; | ||||||
|     return strictEvalExpr(state, e, strictNormalForms); |     return strictEvalExpr(state, e, strictNormalForms); | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /* Yes, this is a really bad idea... */ |  | ||||||
| extern "C" { |  | ||||||
|     unsigned long AT_calcAllocatedSize(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void printEvalStats(EvalState & state) | void printEvalStats(EvalState & state) | ||||||
| { | { | ||||||
|     char x; |     char x; | ||||||
|     bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0"; |     bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0"; | ||||||
|     printMsg(showStats ? lvlInfo : lvlDebug, |     printMsg(showStats ? lvlInfo : lvlDebug, | ||||||
|         format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space") |         format("evaluated %1% expressions, used %2% bytes of stack space, allocated %3% values, allocated %4% environments") | ||||||
|         % state.nrEvaluated % state.nrCached |         % state.nrEvaluated | ||||||
|         % ((float) state.nrCached / (float) state.nrEvaluated * 100) |         % (&x - deepestStack) | ||||||
|         % AT_calcAllocatedSize() |         % state.nrValues | ||||||
|         % (&x - deepestStack)); |         % state.nrEnvs); | ||||||
|     if (showStats) |     if (showStats) | ||||||
|         printATermMapStats(); |         printATermMapStats(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,6 +11,100 @@ namespace nix { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Hash; | class Hash; | ||||||
|  | class EvalState; | ||||||
|  | struct Env; | ||||||
|  | struct Value; | ||||||
|  | 
 | ||||||
|  | typedef ATerm Sym; | ||||||
|  | 
 | ||||||
|  | typedef std::map<Sym, Value> Bindings; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct Env | ||||||
|  | { | ||||||
|  |     Env * up; | ||||||
|  |     Bindings bindings; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     tInt = 1, | ||||||
|  |     tBool, | ||||||
|  |     tString, | ||||||
|  |     tPath, | ||||||
|  |     tNull, | ||||||
|  |     tAttrs, | ||||||
|  |     tList, | ||||||
|  |     tThunk, | ||||||
|  |     tLambda, | ||||||
|  |     tCopy, | ||||||
|  |     tBlackhole, | ||||||
|  |     tPrimOp, | ||||||
|  |     tPrimOpApp, | ||||||
|  | } ValueType; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | typedef void (* PrimOp) (EvalState & state, Value * * args, Value & v); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct Value | ||||||
|  | { | ||||||
|  |     ValueType type; | ||||||
|  |     union  | ||||||
|  |     { | ||||||
|  |         int integer; | ||||||
|  |         bool boolean; | ||||||
|  |         struct { | ||||||
|  |             const char * s; | ||||||
|  |             const char * * context; | ||||||
|  |         } string; | ||||||
|  |         Bindings * attrs; | ||||||
|  |         struct { | ||||||
|  |             unsigned int length; | ||||||
|  |             Value * elems; | ||||||
|  |         } list; | ||||||
|  |         struct { | ||||||
|  |             Env * env; | ||||||
|  |             Expr expr; | ||||||
|  |         } thunk; | ||||||
|  |         struct { | ||||||
|  |             Env * env; | ||||||
|  |             Pattern pat; | ||||||
|  |             Expr body; | ||||||
|  |         } lambda; | ||||||
|  |         Value * val; | ||||||
|  |         struct { | ||||||
|  |             PrimOp fun; | ||||||
|  |             unsigned int arity; | ||||||
|  |         } primOp; | ||||||
|  |         struct { | ||||||
|  |             Value * left, * right; | ||||||
|  |             unsigned int argsLeft; | ||||||
|  |         } primOpApp; | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static inline void mkInt(Value & v, int n) | ||||||
|  | { | ||||||
|  |     v.type = tInt; | ||||||
|  |     v.integer = n; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static inline void mkBool(Value & v, bool b) | ||||||
|  | { | ||||||
|  |     v.type = tBool; | ||||||
|  |     v.boolean = b; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static inline void mkString(Value & v, const char * s) | ||||||
|  | { | ||||||
|  |     v.type = tString; | ||||||
|  |     v.string.s = s; | ||||||
|  |     v.string.context = 0; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| typedef std::map<Path, PathSet> DrvRoots; | typedef std::map<Path, PathSet> DrvRoots; | ||||||
|  | @ -22,32 +116,69 @@ typedef std::map<Path, Path> SrcToStore; | ||||||
| 
 | 
 | ||||||
| struct EvalState; | struct EvalState; | ||||||
| 
 | 
 | ||||||
| /* Note: using a ATermVector is safe here, since when we call a primop
 | 
 | ||||||
|    we also have an ATermList on the stack. */ | std::ostream & operator << (std::ostream & str, Value & v); | ||||||
| typedef Expr (* PrimOp) (EvalState &, const ATermVector & args); |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| struct EvalState  | struct EvalState  | ||||||
| { | { | ||||||
|     ATermMap normalForms; |  | ||||||
|     ATermMap primOps; |  | ||||||
|     DrvRoots drvRoots; |     DrvRoots drvRoots; | ||||||
|     DrvHashes drvHashes; /* normalised derivation hashes */ |     DrvHashes drvHashes; /* normalised derivation hashes */ | ||||||
|     SrcToStore srcToStore;  |     SrcToStore srcToStore;  | ||||||
| 
 | 
 | ||||||
|     unsigned int nrEvaluated; |     unsigned long nrValues; | ||||||
|     unsigned int nrCached; |     unsigned long nrEnvs; | ||||||
|  |     unsigned long nrEvaluated; | ||||||
| 
 | 
 | ||||||
|     bool allowUnsafeEquality; |     bool allowUnsafeEquality; | ||||||
| 
 | 
 | ||||||
|     EvalState(); |     EvalState(); | ||||||
| 
 | 
 | ||||||
|     void addPrimOps(); |     /* Evaluate an expression to normal form, storing the result in
 | ||||||
|  |        value `v'. */ | ||||||
|  |     void eval(Expr e, Value & v); | ||||||
|  |     void eval(Env & env, Expr e, Value & v); | ||||||
|  | 
 | ||||||
|  |     /* Evaluation the expression, then verify that it has the expected
 | ||||||
|  |        type. */ | ||||||
|  |     bool evalBool(Env & env, Expr e); | ||||||
|  | 
 | ||||||
|  |     /* Evaluate an expression, and recursively evaluate list elements
 | ||||||
|  |        and attributes. */ | ||||||
|  |     void strictEval(Expr e, Value & v); | ||||||
|  |     void strictEval(Env & env, Expr e, Value & v); | ||||||
|  | 
 | ||||||
|  |     /* If `v' is a thunk, enter it and overwrite `v' with the result
 | ||||||
|  |        of the evaluation of the thunk.  Otherwise, this is a no-op. */ | ||||||
|  |     void forceValue(Value & v); | ||||||
|  | 
 | ||||||
|  |     /* Force `v', and then verify that it has the expected type. */ | ||||||
|  |     int forceInt(Value & v); | ||||||
|  |     void forceAttrs(Value & v); | ||||||
|  |     void forceList(Value & v); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 
 | ||||||
|  |     /* The base environment, containing the builtin functions and
 | ||||||
|  |        values. */ | ||||||
|  |     Env & baseEnv; | ||||||
|  | 
 | ||||||
|  |     void createBaseEnv(); | ||||||
|  |      | ||||||
|     void addPrimOp(const string & name, |     void addPrimOp(const string & name, | ||||||
|         unsigned int arity, PrimOp primOp); |         unsigned int arity, PrimOp primOp); | ||||||
|  | 
 | ||||||
|  |     /* Do a deep equality test between two values.  That is, list
 | ||||||
|  |        elements and attributes are compared recursively. */ | ||||||
|  |     bool eqValues(Value & v1, Value & v2); | ||||||
|  | 
 | ||||||
|  |     /* Allocation primitives. */ | ||||||
|  |     Value * allocValues(unsigned int count); | ||||||
|  |     Env & allocEnv(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
| /* Evaluate an expression to normal form. */ | /* Evaluate an expression to normal form. */ | ||||||
| Expr evalExpr(EvalState & state, Expr e); | Expr evalExpr(EvalState & state, Expr e); | ||||||
| 
 | 
 | ||||||
|  | @ -86,6 +217,7 @@ Path coerceToPath(EvalState & state, Expr e, PathSet & context); | ||||||
|    value or has a binding in the `args' map.  Note: result is a call, |    value or has a binding in the `args' map.  Note: result is a call, | ||||||
|    not a normal form; it should be evaluated by calling evalExpr(). */ |    not a normal form; it should be evaluated by calling evalExpr(). */ | ||||||
| Expr autoCallFunction(Expr e, const ATermMap & args); | Expr autoCallFunction(Expr e, const ATermMap & args); | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| /* Print statistics. */ | /* Print statistics. */ | ||||||
| void printEvalStats(EvalState & state); | void printEvalStats(EvalState & state); | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
| string DrvInfo::queryDrvPath(EvalState & state) const | string DrvInfo::queryDrvPath(EvalState & state) const | ||||||
| { | { | ||||||
|     if (drvPath == "") { |     if (drvPath == "") { | ||||||
|  | @ -256,6 +257,7 @@ void getDerivations(EvalState & state, Expr e, const string & pathPrefix, | ||||||
|     Exprs doneExprs; |     Exprs doneExprs; | ||||||
|     getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs); |     getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs); | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|   |   | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -375,7 +375,7 @@ expr_op | ||||||
|   | expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); } |   | expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); } | ||||||
|   | expr_op '~' expr_op { $$ = makeSubPath($1, $3); } |   | expr_op '~' expr_op { $$ = makeSubPath($1, $3); } | ||||||
|   | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); } |   | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); } | ||||||
|   | expr_op '+' expr_op { $$ = makeOpPlus($1, $3); } |   | expr_op '+' expr_op { $$ = makeConcatStrings(ATmakeList2($1, $3)); } | ||||||
|   | expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); } |   | expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); } | ||||||
|   | expr_app |   | expr_app | ||||||
|   ; |   ; | ||||||
|  | @ -513,7 +513,7 @@ static Expr parse(EvalState & state, | ||||||
|     if (res) throw ParseError(data.error); |     if (res) throw ParseError(data.error); | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|         checkVarDefs(state.primOps, data.result); |         // !!! checkVarDefs(state.primOps, data.result); | ||||||
|     } catch (Error & e) { |     } catch (Error & e) { | ||||||
|         throw ParseError(format("%1%, in `%2%'") % e.msg() % path); |         throw ParseError(format("%1%, in `%2%'") % e.msg() % path); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
| /*************************************************************
 | /*************************************************************
 | ||||||
|  * Constants |  * Constants | ||||||
|  *************************************************************/ |  *************************************************************/ | ||||||
|  | @ -895,18 +896,21 @@ static Expr prim_isList(EvalState & state, const ATermVector & args) | ||||||
|     ATermList list; |     ATermList list; | ||||||
|     return makeBool(matchList(evalExpr(state, args[0]), list)); |     return makeBool(matchList(evalExpr(state, args[0]), list)); | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /* Return the first element of a list. */ | /* Return the first element of a list. */ | ||||||
| static Expr prim_head(EvalState & state, const ATermVector & args) | static void prim_head(EvalState & state, Value * * args, Value & v) | ||||||
| { | { | ||||||
|     ATermList list = evalList(state, args[0]); |     state.forceList(*args[0]); | ||||||
|     if (ATisEmpty(list)) |     if (args[0]->list.length == 0) | ||||||
|         throw Error("`head' called on an empty list"); |         throw Error("`head' called on an empty list"); | ||||||
|     return evalExpr(state, ATgetFirst(list)); |     state.forceValue(args[0]->list.elems[0]); | ||||||
|  |     v = args[0]->list.elems[0]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
| /* Return a list consisting of everything but the the first element of
 | /* Return a list consisting of everything but the the first element of
 | ||||||
|    a list. */ |    a list. */ | ||||||
| static Expr prim_tail(EvalState & state, const ATermVector & args) | static Expr prim_tail(EvalState & state, const ATermVector & args) | ||||||
|  | @ -938,6 +942,7 @@ static Expr prim_length(EvalState & state, const ATermVector & args) | ||||||
|     ATermList list = evalList(state, args[0]); |     ATermList list = evalList(state, args[0]); | ||||||
|     return makeInt(ATgetLength(list)); |     return makeInt(ATgetLength(list)); | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /*************************************************************
 | /*************************************************************
 | ||||||
|  | @ -945,14 +950,13 @@ static Expr prim_length(EvalState & state, const ATermVector & args) | ||||||
|  *************************************************************/ |  *************************************************************/ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static Expr prim_add(EvalState & state, const ATermVector & args) | static void prim_add(EvalState & state, Value * * args, Value & v) | ||||||
| { | { | ||||||
|     int i1 = evalInt(state, args[0]); |     mkInt(v, state.forceInt(*args[0]) + state.forceInt(*args[1])); | ||||||
|     int i2 = evalInt(state, args[1]); |  | ||||||
|     return makeInt(i1 + i2); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
| static Expr prim_sub(EvalState & state, const ATermVector & args) | static Expr prim_sub(EvalState & state, const ATermVector & args) | ||||||
| { | { | ||||||
|     int i1 = evalInt(state, args[0]); |     int i1 = evalInt(state, args[0]); | ||||||
|  | @ -1102,6 +1106,7 @@ static Expr prim_compareVersions(EvalState & state, const ATermVector & args) | ||||||
|     int d = compareVersions(version1, version2); |     int d = compareVersions(version1, version2); | ||||||
|     return makeInt(d); |     return makeInt(d); | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /*************************************************************
 | /*************************************************************
 | ||||||
|  | @ -1109,14 +1114,31 @@ static Expr prim_compareVersions(EvalState & state, const ATermVector & args) | ||||||
|  *************************************************************/ |  *************************************************************/ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void EvalState::addPrimOps() | void EvalState::createBaseEnv() | ||||||
| { | { | ||||||
|     addPrimOp("builtins", 0, prim_builtins); |     baseEnv.up = 0; | ||||||
| 
 | 
 | ||||||
|  |     {   Value & v = baseEnv.bindings[toATerm("builtins")]; | ||||||
|  |         v.type = tAttrs; | ||||||
|  |         v.attrs = new Bindings; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /* Add global constants such as `true' to the base environment. */ | ||||||
|  |     {   Value & v = baseEnv.bindings[toATerm("true")]; | ||||||
|  |         mkBool(v, true); | ||||||
|  |     } | ||||||
|  |     {   Value & v = baseEnv.bindings[toATerm("false")]; | ||||||
|  |         mkBool(v, false); | ||||||
|  |     } | ||||||
|  |     {   Value & v = baseEnv.bindings[toATerm("null")]; | ||||||
|  |         v.type = tNull; | ||||||
|  |     } | ||||||
|  |     {   Value & v = (*baseEnv.bindings[toATerm("builtins")].attrs)[toATerm("currentSystem")]; | ||||||
|  |         mkString(v, thisSystem.c_str()); // !!! copy string
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | #if 0    
 | ||||||
|     // Constants
 |     // Constants
 | ||||||
|     addPrimOp("true", 0, prim_true); |  | ||||||
|     addPrimOp("false", 0, prim_false); |  | ||||||
|     addPrimOp("null", 0, prim_null); |  | ||||||
|     addPrimOp("__currentSystem", 0, prim_currentSystem); |     addPrimOp("__currentSystem", 0, prim_currentSystem); | ||||||
|     addPrimOp("__currentTime", 0, prim_currentTime); |     addPrimOp("__currentTime", 0, prim_currentTime); | ||||||
| 
 | 
 | ||||||
|  | @ -1135,7 +1157,6 @@ void EvalState::addPrimOps() | ||||||
|     addPrimOp("__getEnv", 1, prim_getEnv); |     addPrimOp("__getEnv", 1, prim_getEnv); | ||||||
|     addPrimOp("__trace", 2, prim_trace); |     addPrimOp("__trace", 2, prim_trace); | ||||||
|      |      | ||||||
|      |  | ||||||
|     // Expr <-> String
 |     // Expr <-> String
 | ||||||
|     addPrimOp("__exprToString", 1, prim_exprToString); |     addPrimOp("__exprToString", 1, prim_exprToString); | ||||||
|     addPrimOp("__stringToExpr", 1, prim_stringToExpr); |     addPrimOp("__stringToExpr", 1, prim_stringToExpr); | ||||||
|  | @ -1169,13 +1190,17 @@ void EvalState::addPrimOps() | ||||||
| 
 | 
 | ||||||
|     // Lists
 |     // Lists
 | ||||||
|     addPrimOp("__isList", 1, prim_isList); |     addPrimOp("__isList", 1, prim_isList); | ||||||
|  | #endif | ||||||
|     addPrimOp("__head", 1, prim_head); |     addPrimOp("__head", 1, prim_head); | ||||||
|  | #if 0 | ||||||
|     addPrimOp("__tail", 1, prim_tail); |     addPrimOp("__tail", 1, prim_tail); | ||||||
|     addPrimOp("map", 2, prim_map); |     addPrimOp("map", 2, prim_map); | ||||||
|     addPrimOp("__length", 1, prim_length); |     addPrimOp("__length", 1, prim_length); | ||||||
|  | #endif | ||||||
|      |      | ||||||
|     // Integer arithmetic
 |     // Integer arithmetic
 | ||||||
|     addPrimOp("__add", 2, prim_add); |     addPrimOp("__add", 2, prim_add); | ||||||
|  | #if 0 | ||||||
|     addPrimOp("__sub", 2, prim_sub); |     addPrimOp("__sub", 2, prim_sub); | ||||||
|     addPrimOp("__mul", 2, prim_mul); |     addPrimOp("__mul", 2, prim_mul); | ||||||
|     addPrimOp("__div", 2, prim_div); |     addPrimOp("__div", 2, prim_div); | ||||||
|  | @ -1191,6 +1216,7 @@ void EvalState::addPrimOps() | ||||||
|     // Versions
 |     // Versions
 | ||||||
|     addPrimOp("__parseDrvName", 1, prim_parseDrvName); |     addPrimOp("__parseDrvName", 1, prim_parseDrvName); | ||||||
|     addPrimOp("__compareVersions", 2, prim_compareVersions); |     addPrimOp("__compareVersions", 2, prim_compareVersions); | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -37,6 +37,7 @@ static int rootNr = 0; | ||||||
| static bool indirectRoot = false; | static bool indirectRoot = false; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
| static void printResult(EvalState & state, Expr e, | static void printResult(EvalState & state, Expr e, | ||||||
|     bool evalOnly, bool xmlOutput, const ATermMap & autoArgs) |     bool evalOnly, bool xmlOutput, const ATermMap & autoArgs) | ||||||
| { | { | ||||||
|  | @ -63,21 +64,28 @@ static void printResult(EvalState & state, Expr e, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void processExpr(EvalState & state, const Strings & attrPaths, | void processExpr(EvalState & state, const Strings & attrPaths, | ||||||
|     bool parseOnly, bool strict, const ATermMap & autoArgs, |     bool parseOnly, bool strict, const ATermMap & autoArgs, | ||||||
|     bool evalOnly, bool xmlOutput, Expr e) |     bool evalOnly, bool xmlOutput, Expr e) | ||||||
| { | { | ||||||
|  |     Value v; | ||||||
|  |     state.strictEval(e, v); | ||||||
|  |     std::cout << v << std::endl; | ||||||
|  |      | ||||||
|  | #if 0 | ||||||
|     for (Strings::const_iterator i = attrPaths.begin(); i != attrPaths.end(); ++i) { |     for (Strings::const_iterator i = attrPaths.begin(); i != attrPaths.end(); ++i) { | ||||||
|         Expr e2 = findAlongAttrPath(state, *i, autoArgs, e); |         Expr e2 = findAlongAttrPath(state, *i, autoArgs, e); | ||||||
|         if (!parseOnly) |         if (!parseOnly) | ||||||
|             if (strict) |             if (strict) | ||||||
|                 e2 = strictEvalExpr(state, e2); |                 e2 = state.strictEval(e2); | ||||||
|             else |             else | ||||||
|                 e2 = evalExpr(state, e2); |                 e2 = evalExpr(state, e2); | ||||||
|         printResult(state, e2, evalOnly, xmlOutput, autoArgs); |         printResult(state, e2, evalOnly, xmlOutput, autoArgs); | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue