aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2006-05-08 12:52:47 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2006-05-08 12:52:47 +0000
commit5cabd47394a5bb3076f3f5b5a98425665cddef23 (patch)
treeb334853dc567f3b8c5fc56dcad1321478b6e1722 /src
parent310e605995dc104bee29d330ac135e3e2bb82f97 (diff)
* Allow function argument default values to refer to other arguments
of the function. Implements NIX-45.
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/eval.cc75
-rw-r--r--src/libutil/aterm-map.cc6
-rw-r--r--src/libutil/aterm-map.hh5
3 files changed, 48 insertions, 38 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index ee17c996c..667961cd5 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 60092382a..f40046437 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 6d13d7f9e..078617a7d 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();