* 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,48 +24,53 @@ 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…
Reference in a new issue