* Implemented `map'.
This commit is contained in:
		
							parent
							
								
									d78a05ab40
								
							
						
					
					
						commit
						5b72d8a749
					
				
					 4 changed files with 139 additions and 108 deletions
				
			
		|  | @ -68,6 +68,8 @@ void run(Strings args) | ||||||
|     doTest("let x = x; in if true || x then 1 else 2"); |     doTest("let x = x; in if true || x then 1 else 2"); | ||||||
|     doTest("/etc/passwd"); |     doTest("/etc/passwd"); | ||||||
|     doTest("import ./foo.nix"); |     doTest("import ./foo.nix"); | ||||||
|  |     doTest("map (x: __add 1 x) [ 1 2 3 ]"); | ||||||
|  |     doTest("map (__add 1) [ 1 2 3 ]"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -328,106 +328,9 @@ void EvalState::eval(Env & env, Expr e, Value & v) | ||||||
|     Expr fun, arg; |     Expr fun, arg; | ||||||
|     if (matchCall(e, fun, arg)) { |     if (matchCall(e, fun, arg)) { | ||||||
|         eval(env, fun, v); |         eval(env, fun, v); | ||||||
| 
 |         Value vArg; | ||||||
|         if (v.type == tPrimOp || v.type == tPrimOpApp) { |         mkThunk(vArg, env, arg); // !!! should this be on the heap?
 | ||||||
|             unsigned int argsLeft = |         callFunction(v, vArg, v); | ||||||
|                 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; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -519,6 +422,103 @@ void EvalState::eval(Env & env, Expr e, Value & v) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | void EvalState::callFunction(Value & fun, Value & arg, Value & v) | ||||||
|  | { | ||||||
|  |     if (fun.type == tPrimOp || fun.type == tPrimOpApp) { | ||||||
|  |         unsigned int argsLeft = | ||||||
|  |             fun.type == tPrimOp ? fun.primOp.arity : fun.primOpApp.argsLeft; | ||||||
|  |         if (argsLeft == 1) { | ||||||
|  |             /* We have all the arguments, so call the primop.  First
 | ||||||
|  |                find the primop. */ | ||||||
|  |             Value * primOp = &fun; | ||||||
|  |             while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left; | ||||||
|  |             assert(primOp->type == tPrimOp); | ||||||
|  |             unsigned int arity = primOp->primOp.arity; | ||||||
|  |                  | ||||||
|  |             /* Put all the arguments in an array. */ | ||||||
|  |             Value * vArgs[arity]; | ||||||
|  |             unsigned int n = arity - 1; | ||||||
|  |             vArgs[n--] = &arg; | ||||||
|  |             for (Value * arg = &fun; 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] = fun; | ||||||
|  |             v2[1] = arg; | ||||||
|  |             v.type = tPrimOpApp; | ||||||
|  |             v.primOpApp.left = &v2[0]; | ||||||
|  |             v.primOpApp.right = &v2[1]; | ||||||
|  |             v.primOpApp.argsLeft = argsLeft - 1; | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     if (fun.type != tLambda) | ||||||
|  |         throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%", | ||||||
|  |             showType(fun)); | ||||||
|  | 
 | ||||||
|  |     Env & env2(allocEnv()); | ||||||
|  |     env2.up = fun.lambda.env; | ||||||
|  | 
 | ||||||
|  |     ATermList formals; ATerm ellipsis, name; | ||||||
|  | 
 | ||||||
|  |     if (matchVarPat(fun.lambda.pat, name)) { | ||||||
|  |         Value & vArg = env2.bindings[name]; | ||||||
|  |         nrValues++; | ||||||
|  |         vArg = arg; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     else if (matchAttrsPat(fun.lambda.pat, formals, ellipsis, name)) { | ||||||
|  |         forceAttrs(arg); | ||||||
|  |          | ||||||
|  |         if (name != sNoAlias) { | ||||||
|  |             env2.bindings[name] = arg; | ||||||
|  |             nrValues++; | ||||||
|  |         }                 | ||||||
|  | 
 | ||||||
|  |         /* 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 = arg.attrs->find(name); | ||||||
|  |                  | ||||||
|  |             Value & v = env2.bindings[name]; | ||||||
|  |             nrValues++; | ||||||
|  |                  | ||||||
|  |             if (j == arg.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 != arg.attrs->size()) | ||||||
|  |             throw TypeError("function called with unexpected argument"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     else abort(); | ||||||
|  |          | ||||||
|  |     eval(env2, fun.lambda.body, v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void EvalState::eval(Expr e, Value & v) | void EvalState::eval(Expr e, Value & v) | ||||||
| { | { | ||||||
|     eval(baseEnv, e, v); |     eval(baseEnv, e, v); | ||||||
|  | @ -567,6 +567,8 @@ void EvalState::forceValue(Value & v) | ||||||
|         forceValue(*v.val); |         forceValue(*v.val); | ||||||
|         v = *v.val; |         v = *v.val; | ||||||
|     } |     } | ||||||
|  |     else if (v.type == tApp) | ||||||
|  |         callFunction(*v.app.left, *v.app.right, v); | ||||||
|     else if (v.type == tBlackhole) |     else if (v.type == tBlackhole) | ||||||
|         throw EvalError("infinite recursion encountered"); |         throw EvalError("infinite recursion encountered"); | ||||||
| } | } | ||||||
|  | @ -597,6 +599,14 @@ void EvalState::forceList(Value & v) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | void EvalState::forceFunction(Value & v) | ||||||
|  | { | ||||||
|  |     forceValue(v); | ||||||
|  |     if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp) | ||||||
|  |         throw TypeError(format("value is %1% while a function was expected") % showType(v)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| string EvalState::coerceToString(Value & v, PathSet & context, | string EvalState::coerceToString(Value & v, PathSet & context, | ||||||
|     bool coerceMore, bool copyToStore) |     bool coerceMore, bool copyToStore) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -36,6 +36,7 @@ typedef enum { | ||||||
|     tAttrs, |     tAttrs, | ||||||
|     tList, |     tList, | ||||||
|     tThunk, |     tThunk, | ||||||
|  |     tApp, | ||||||
|     tLambda, |     tLambda, | ||||||
|     tCopy, |     tCopy, | ||||||
|     tBlackhole, |     tBlackhole, | ||||||
|  | @ -68,6 +69,9 @@ struct Value | ||||||
|             Env * env; |             Env * env; | ||||||
|             Expr expr; |             Expr expr; | ||||||
|         } thunk; |         } thunk; | ||||||
|  |         struct { | ||||||
|  |             Value * left, * right; | ||||||
|  |         } app; | ||||||
|         struct { |         struct { | ||||||
|             Env * env; |             Env * env; | ||||||
|             Pattern pat; |             Pattern pat; | ||||||
|  | @ -161,13 +165,16 @@ struct EvalState | ||||||
|     void strictEval(Env & env, 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
 |     /* 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. */ |        of the evaluation of the thunk.  If `v' is a delayed function | ||||||
|  |        application, call the function and overwrite `v' with the | ||||||
|  |        result.  Otherwise, this is a no-op. */ | ||||||
|     void forceValue(Value & v); |     void forceValue(Value & v); | ||||||
| 
 | 
 | ||||||
|     /* Force `v', and then verify that it has the expected type. */ |     /* Force `v', and then verify that it has the expected type. */ | ||||||
|     int forceInt(Value & v); |     int forceInt(Value & v); | ||||||
|     void forceAttrs(Value & v); |     void forceAttrs(Value & v); | ||||||
|     void forceList(Value & v); |     void forceList(Value & v); | ||||||
|  |     void forceFunction(Value & v); // either lambda or primop
 | ||||||
| 
 | 
 | ||||||
|     /* String coercion.  Converts strings, paths and derivations to a
 |     /* String coercion.  Converts strings, paths and derivations to a
 | ||||||
|        string.  If `coerceMore' is set, also converts nulls, integers, |        string.  If `coerceMore' is set, also converts nulls, integers, | ||||||
|  | @ -196,6 +203,10 @@ private: | ||||||
|        elements and attributes are compared recursively. */ |        elements and attributes are compared recursively. */ | ||||||
|     bool eqValues(Value & v1, Value & v2); |     bool eqValues(Value & v1, Value & v2); | ||||||
| 
 | 
 | ||||||
|  |     void callFunction(Value & fun, Value & arg, Value & v); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |      | ||||||
|     /* Allocation primitives. */ |     /* Allocation primitives. */ | ||||||
|     Value * allocValues(unsigned int count); |     Value * allocValues(unsigned int count); | ||||||
|     Env & allocEnv(); |     Env & allocEnv(); | ||||||
|  |  | ||||||
|  | @ -913,22 +913,28 @@ static Expr prim_tail(EvalState & state, const ATermVector & args) | ||||||
|         throw Error("`tail' called on an empty list"); |         throw Error("`tail' called on an empty list"); | ||||||
|     return makeList(ATgetNext(list)); |     return makeList(ATgetNext(list)); | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /* Apply a function to every element of a list. */ | /* Apply a function to every element of a list. */ | ||||||
| static Expr prim_map(EvalState & state, const ATermVector & args) | static void prim_map(EvalState & state, Value * * args, Value & v) | ||||||
| { | { | ||||||
|     Expr fun = evalExpr(state, args[0]); |     state.forceFunction(*args[0]); | ||||||
|     ATermList list = evalList(state, args[1]); |     state.forceList(*args[1]); | ||||||
| 
 | 
 | ||||||
|     ATermList res = ATempty; |     v.type = tList; | ||||||
|     for (ATermIterator i(list); i; ++i) |     v.list.length = args[1]->list.length; | ||||||
|         res = ATinsert(res, makeCall(fun, *i)); |     v.list.elems = state.allocValues(v.list.length); | ||||||
| 
 | 
 | ||||||
|     return makeList(ATreverse(res)); |     for (unsigned int n = 0; n < v.list.length; ++n) { | ||||||
|  |         v.list.elems[n].type = tApp; | ||||||
|  |         v.list.elems[n].app.left = args[0]; | ||||||
|  |         v.list.elems[n].app.right = &args[1]->list.elems[n]; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
| /* Return the length of a list.  This is an O(1) time operation. */ | /* Return the length of a list.  This is an O(1) time operation. */ | ||||||
| static Expr prim_length(EvalState & state, const ATermVector & args) | static Expr prim_length(EvalState & state, const ATermVector & args) | ||||||
| { | { | ||||||
|  | @ -1189,7 +1195,9 @@ void EvalState::createBaseEnv() | ||||||
|     addPrimOp("__head", 1, prim_head); |     addPrimOp("__head", 1, prim_head); | ||||||
| #if 0 | #if 0 | ||||||
|     addPrimOp("__tail", 1, prim_tail); |     addPrimOp("__tail", 1, prim_tail); | ||||||
|  | #endif | ||||||
|     addPrimOp("map", 2, prim_map); |     addPrimOp("map", 2, prim_map); | ||||||
|  | #if 0 | ||||||
|     addPrimOp("__length", 1, prim_length); |     addPrimOp("__length", 1, prim_length); | ||||||
| #endif | #endif | ||||||
|      |      | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue