* Allow function argument default values to refer to other arguments
of the function. Implements NIX-45.
This commit is contained in:
		
							parent
							
								
									310e605995
								
							
						
					
					
						commit
						5cabd47394
					
				
					 5 changed files with 51 additions and 39 deletions
				
			
		|  | @ -24,49 +24,54 @@ void EvalState::addPrimOp(const string & name, | ||||||
| /* Substitute an argument set into the body of a function. */ | /* Substitute an argument set into the body of a function. */ | ||||||
| static Expr substArgs(Expr body, ATermList formals, Expr arg) | static Expr substArgs(Expr body, ATermList formals, Expr arg) | ||||||
| { | { | ||||||
|     ATermMap subs(ATgetLength(formals) * 2); |     unsigned int nrFormals = ATgetLength(formals); | ||||||
|  |     ATermMap subs(nrFormals); | ||||||
| 
 | 
 | ||||||
|     /* ({x ? E1; y ? E2, z}: E3) {x = E4; z = E5;}
 |     /* Get the actual arguments and put them in the substitution. */ | ||||||
| 
 |  | ||||||
|        => let {x = E4; y = E2; z = E5; body = E3; } |  | ||||||
| 
 |  | ||||||
|        => subst(E3, s) |  | ||||||
|           s = { |  | ||||||
|             R = rec {x = E4; y = E2; z = E5} |  | ||||||
|             x -> R.x |  | ||||||
|             y -> R.y |  | ||||||
|             z -> R.z |  | ||||||
|           } |  | ||||||
|     */ |  | ||||||
|      |  | ||||||
|     /* Get the formal arguments. */ |  | ||||||
|     for (ATermIterator i(formals); i; ++i) { |  | ||||||
|         Expr name, def; |  | ||||||
|         if (matchNoDefFormal(*i, name)) |  | ||||||
|             subs.set(name, makeUndefined()); |  | ||||||
|         else if (matchDefFormal(*i, name, def)) |  | ||||||
|             subs.set(name, def); |  | ||||||
|         else abort(); /* can't happen */ |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Get the actual arguments, and check that they match with the
 |  | ||||||
|        formals. */ |  | ||||||
|     ATermMap args(128); /* !!! fix */ |     ATermMap args(128); /* !!! fix */ | ||||||
|     queryAllAttrs(arg, args); |     queryAllAttrs(arg, args); | ||||||
|     for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i) { |     for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i) | ||||||
|         Expr cur = subs.get(i->key); |  | ||||||
|         if (!subs.get(i->key)) |  | ||||||
|             throw Error(format("unexpected function argument `%1%'") |  | ||||||
|                 % aterm2String(i->key)); |  | ||||||
|         subs.set(i->key, i->value); |         subs.set(i->key, i->value); | ||||||
|  |      | ||||||
|  |     /* Get the formal arguments. */ | ||||||
|  |     ATermVector defsUsed; | ||||||
|  |     ATermList recAttrs = ATempty; | ||||||
|  |     for (ATermIterator i(formals); i; ++i) { | ||||||
|  |         Expr name, def = 0; | ||||||
|  |         if (!matchNoDefFormal(*i, name) && !matchDefFormal(*i, name, def)) | ||||||
|  |             abort(); /* can't happen */ | ||||||
|  |         if (subs[name] == 0) { | ||||||
|  |             if (def == 0) throw Error(format("required function argument `%1%' missing") | ||||||
|  |                 % aterm2String(name)); | ||||||
|  |             defsUsed.push_back(name); | ||||||
|  |             recAttrs = ATinsert(recAttrs, makeBind(name, def, makeNoPos())); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* Check that all arguments are defined. */ |     /* Make a recursive attribute set out of the (argument-name,
 | ||||||
|     for (ATermMap::const_iterator i = subs.begin(); i != subs.end(); ++i) |        value) tuples.  This is so that we can support default | ||||||
|         if (i->value == makeUndefined()) |        parameters that refer to each other, e.g.  ({x, y ? x + x}: y) | ||||||
|             throw Error(format("required function argument `%1%' missing") |        {x = "foo";} evaluates to "foofoo". */ | ||||||
|                 % aterm2String(i->key)); |     if (defsUsed.size() != 0) { | ||||||
|  |         for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i) | ||||||
|  |             recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos())); | ||||||
|  |         Expr rec = makeRec(recAttrs, ATempty); | ||||||
|  |         for (ATermVector::iterator i = defsUsed.begin(); i != defsUsed.end(); ++i) | ||||||
|  |             subs.set(*i, makeSelect(rec, *i)); | ||||||
|  |     } | ||||||
|      |      | ||||||
|  |     if (subs.size() != nrFormals) { | ||||||
|  |         /* One or more actual arguments were not declared as formal
 | ||||||
|  |            arguments.  Find out which. */ | ||||||
|  |         for (ATermIterator i(formals); i; ++i) { | ||||||
|  |             Expr name, def; | ||||||
|  |             matchNoDefFormal(*i, name) || matchDefFormal(*i, name, def); | ||||||
|  |             subs.remove(name); | ||||||
|  |         } | ||||||
|  |         throw Error(format("unexpected function argument `%1%'") | ||||||
|  |             % aterm2String(subs.begin()->key)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return substitute(Substitution(0, &subs), body); |     return substitute(Substitution(0, &subs), body); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -217,17 +217,17 @@ unsigned int ATermMap::size() | ||||||
| 
 | 
 | ||||||
| void printATermMapStats() | void printATermMapStats() | ||||||
| { | { | ||||||
|     cout << "RESIZES: " << nrResizes << " " |     cerr << "RESIZES: " << nrResizes << " " | ||||||
|          << sizeTotalAlloc << " " |          << sizeTotalAlloc << " " | ||||||
|          << sizeCurAlloc << " " |          << sizeCurAlloc << " " | ||||||
|          << sizeMaxAlloc << endl; |          << sizeMaxAlloc << endl; | ||||||
|          |          | ||||||
|     cout << "SET: " |     cerr << "SET: " | ||||||
|          << nrItemsSet << " " |          << nrItemsSet << " " | ||||||
|          << nrSetProbes << " " |          << nrSetProbes << " " | ||||||
|          << (double) nrSetProbes / nrItemsSet << endl; |          << (double) nrSetProbes / nrItemsSet << endl; | ||||||
| 
 | 
 | ||||||
|     cout << "GET: " |     cerr << "GET: " | ||||||
|          << nrItemsGet << " " |          << nrItemsGet << " " | ||||||
|          << nrGetProbes << " " |          << nrGetProbes << " " | ||||||
|          << (double) nrGetProbes / nrItemsGet << endl; |          << (double) nrGetProbes / nrItemsGet << endl; | ||||||
|  |  | ||||||
|  | @ -50,6 +50,11 @@ public: | ||||||
| 
 | 
 | ||||||
|     ATerm get(ATerm key) const; |     ATerm get(ATerm key) const; | ||||||
| 
 | 
 | ||||||
|  |     ATerm operator [](ATerm key) const | ||||||
|  |     { | ||||||
|  |         return get(key); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void remove(ATerm key); |     void remove(ATerm key); | ||||||
| 
 | 
 | ||||||
|     unsigned int size(); |     unsigned int size(); | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								tests/lang/eval-fail-missing-arg.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-fail-missing-arg.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | ({x, y, z}: x + y + z) {x = "foo"; z = "bar";} | ||||||
							
								
								
									
										1
									
								
								tests/lang/eval-fail-undeclared-arg.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-fail-undeclared-arg.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | ({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";} | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue