diff options
author | Eelco Dolstra <e.dolstra@tudelft.nl> | 2006-05-08T12·52+0000 |
---|---|---|
committer | Eelco Dolstra <e.dolstra@tudelft.nl> | 2006-05-08T12·52+0000 |
commit | 5cabd47394a5bb3076f3f5b5a98425665cddef23 (patch) | |
tree | b334853dc567f3b8c5fc56dcad1321478b6e1722 | |
parent | 310e605995dc104bee29d330ac135e3e2bb82f97 (diff) |
* Allow function argument default values to refer to other arguments
of the function. Implements NIX-45.
-rw-r--r-- | src/libexpr/eval.cc | 75 | ||||
-rw-r--r-- | src/libutil/aterm-map.cc | 6 | ||||
-rw-r--r-- | src/libutil/aterm-map.hh | 5 | ||||
-rw-r--r-- | tests/lang/eval-fail-missing-arg.nix | 1 | ||||
-rw-r--r-- | tests/lang/eval-fail-undeclared-arg.nix | 1 |
5 files changed, 50 insertions, 38 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ee17c996c6f9..667961cd506a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -24,49 +24,54 @@ void EvalState::addPrimOp(const string & name, /* Substitute an argument set into the body of a function. */ 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;} - - => 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 actual arguments and put them in the substitution. */ + ATermMap args(128); /* !!! fix */ + queryAllAttrs(arg, args); + for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i) + subs.set(i->key, i->value); /* Get the formal arguments. */ + ATermVector defsUsed; + ATermList recAttrs = ATempty; 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 */ + 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())); + } } - /* Get the actual arguments, and check that they match with the - formals. */ - ATermMap args(128); /* !!! fix */ - queryAllAttrs(arg, args); - 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); + /* Make a recursive attribute set out of the (argument-name, + value) tuples. This is so that we can support default + parameters that refer to each other, e.g. ({x, y ? x + x}: y) + {x = "foo";} evaluates to "foofoo". */ + 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)); } - - /* Check that all arguments are defined. */ - for (ATermMap::const_iterator i = subs.begin(); i != subs.end(); ++i) - if (i->value == makeUndefined()) - throw Error(format("required function argument `%1%' missing") - % aterm2String(i->key)); + 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); } diff --git a/src/libutil/aterm-map.cc b/src/libutil/aterm-map.cc index 60092382a2f3..f400464378f5 100644 --- a/src/libutil/aterm-map.cc +++ b/src/libutil/aterm-map.cc @@ -217,17 +217,17 @@ unsigned int ATermMap::size() void printATermMapStats() { - cout << "RESIZES: " << nrResizes << " " + cerr << "RESIZES: " << nrResizes << " " << sizeTotalAlloc << " " << sizeCurAlloc << " " << sizeMaxAlloc << endl; - cout << "SET: " + cerr << "SET: " << nrItemsSet << " " << nrSetProbes << " " << (double) nrSetProbes / nrItemsSet << endl; - cout << "GET: " + cerr << "GET: " << nrItemsGet << " " << nrGetProbes << " " << (double) nrGetProbes / nrItemsGet << endl; diff --git a/src/libutil/aterm-map.hh b/src/libutil/aterm-map.hh index 6d13d7f9e0d1..078617a7d673 100644 --- a/src/libutil/aterm-map.hh +++ b/src/libutil/aterm-map.hh @@ -50,6 +50,11 @@ public: ATerm get(ATerm key) const; + ATerm operator [](ATerm key) const + { + return get(key); + } + void remove(ATerm key); unsigned int size(); diff --git a/tests/lang/eval-fail-missing-arg.nix b/tests/lang/eval-fail-missing-arg.nix new file mode 100644 index 000000000000..c4be9797c534 --- /dev/null +++ b/tests/lang/eval-fail-missing-arg.nix @@ -0,0 +1 @@ +({x, y, z}: x + y + z) {x = "foo"; z = "bar";} diff --git a/tests/lang/eval-fail-undeclared-arg.nix b/tests/lang/eval-fail-undeclared-arg.nix new file mode 100644 index 000000000000..cafdf1636272 --- /dev/null +++ b/tests/lang/eval-fail-undeclared-arg.nix @@ -0,0 +1 @@ +({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";} |