* Verify that all variables in a Nix expression are defined.

This commit is contained in:
Eelco Dolstra 2004-02-03 14:45:34 +00:00
parent 1c9c0a5a46
commit c4f7ae4aa5
4 changed files with 92 additions and 21 deletions

View file

@ -64,30 +64,30 @@ ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds)
/* Create the substitution list. */ /* Create the substitution list. */
ATermMap subs; ATermMap subs;
for (ATermIterator i(rbnds); i; ++i) { for (ATermIterator i(rbnds); i; ++i) {
string s; ATerm name;
Expr e2; Expr e2;
if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) if (!(atMatch(m, *i) >> "Bind" >> name >> e2))
abort(); /* can't happen */ abort(); /* can't happen */
subs.set(s, ATmake("Select(<term>, <str>)", e, s.c_str())); subs.set(name, ATmake("Select(<term>, <term>)", e, name));
} }
/* Create the non-recursive set. */ /* Create the non-recursive set. */
ATermMap as; ATermMap as;
for (ATermIterator i(rbnds); i; ++i) { for (ATermIterator i(rbnds); i; ++i) {
string s; ATerm name;
Expr e2; Expr e2;
if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) if (!(atMatch(m, *i) >> "Bind" >> name >> e2))
abort(); /* can't happen */ abort(); /* can't happen */
as.set(s, substitute(subs, e2)); as.set(name, substitute(subs, e2));
} }
/* Copy the non-recursive bindings. !!! inefficient */ /* Copy the non-recursive bindings. !!! inefficient */
for (ATermIterator i(nrbnds); i; ++i) { for (ATermIterator i(nrbnds); i; ++i) {
string s; ATerm name;
Expr e2; Expr e2;
if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) if (!(atMatch(m, *i) >> "Bind" >> name >> e2))
abort(); /* can't happen */ abort(); /* can't happen */
as.set(s, e2); as.set(name, e2);
} }
return makeAttrs(as); return makeAttrs(as);

View file

@ -156,10 +156,10 @@ Expr substitute(const ATermMap & subs, Expr e)
checkInterrupt(); checkInterrupt();
ATMatcher m; ATMatcher m;
string s; ATerm name;
if (atMatch(m, e) >> "Var" >> s) { if (atMatch(m, e) >> "Var" >> name) {
Expr sub = subs.get(s); Expr sub = subs.get(name);
return sub ? sub : e; return sub ? sub : e;
} }
@ -170,11 +170,10 @@ Expr substitute(const ATermMap & subs, Expr e)
if (atMatch(m, e) >> "Function" >> formals >> body) { if (atMatch(m, e) >> "Function" >> formals >> body) {
ATermMap subs2(subs); ATermMap subs2(subs);
for (ATermIterator i(formals); i; ++i) { for (ATermIterator i(formals); i; ++i) {
Expr def; if (!(atMatch(m, *i) >> "NoDefFormal" >> name) &&
if (!(atMatch(m, *i) >> "NoDefFormal" >> s) && !(atMatch(m, *i) >> "DefFormal" >> name))
!(atMatch(m, *i) >> "DefFormal" >> s >> def))
abort(); abort();
subs2.remove(s); subs2.remove(name);
} }
return ATmake("Function(<term>, <term>)", formals, return ATmake("Function(<term>, <term>)", formals,
substitute(subs2, body)); substitute(subs2, body));
@ -184,12 +183,11 @@ Expr substitute(const ATermMap & subs, Expr e)
ATermList rbnds, nrbnds; ATermList rbnds, nrbnds;
if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds) { if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds) {
ATermMap subs2(subs); ATermMap subs2(subs);
for (ATermIterator i(rbnds); i; ++i) { for (ATermIterator i(rbnds); i; ++i)
Expr e; if (atMatch(m, *i) >> "Bind" >> name)
if (!(atMatch(m, *i) >> "Bind" >> s >> e)) subs2.remove(name);
else
abort(); /* can't happen */ abort(); /* can't happen */
subs2.remove(s);
}
return ATmake("Rec(<term>, <term>)", return ATmake("Rec(<term>, <term>)",
substitute(subs2, (ATerm) rbnds), substitute(subs2, (ATerm) rbnds),
substitute(subs, (ATerm) nrbnds)); substitute(subs, (ATerm) nrbnds));
@ -217,6 +215,59 @@ Expr substitute(const ATermMap & subs, Expr e)
} }
void checkVarDefs(const ATermMap & defs, Expr e)
{
ATMatcher m;
ATerm name;
ATermList formals;
ATerm body;
ATermList rbnds, nrbnds;
if (atMatch(m, e) >> "Var" >> name) {
if (!defs.get(name))
throw Error(format("undefined variable `%1%'")
% aterm2String(name));
return;
}
else if (atMatch(m, e) >> "Function" >> formals >> body) {
ATermMap defs2(defs);
for (ATermIterator i(formals); i; ++i) {
Expr deflt;
if (!(atMatch(m, *i) >> "NoDefFormal" >> name))
if (atMatch(m, *i) >> "DefFormal" >> name >> deflt)
checkVarDefs(defs, deflt);
else
abort();
defs2.set(name, (ATerm) ATempty);
}
return checkVarDefs(defs2, body);
}
else if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds) {
checkVarDefs(defs
, (ATerm) nrbnds);
ATermMap defs2(defs);
for (ATermIterator i(rbnds); i; ++i) {
if (!(atMatch(m, *i) >> "Bind" >> name))
abort(); /* can't happen */
defs2.set(name, (ATerm) ATempty);
}
checkVarDefs(defs2, (ATerm) rbnds);
}
else if (ATgetType(e) == AT_APPL) {
int arity = ATgetArity(ATgetAFun(e));
for (int i = 0; i < arity; ++i)
checkVarDefs(defs, ATgetArgument(e, i));
}
else if (ATgetType(e) == AT_LIST)
for (ATermIterator i((ATermList) e); i; ++i)
checkVarDefs(defs, *i);
}
Expr makeBool(bool b) Expr makeBool(bool b)
{ {
return b ? ATmake("Bool(True)") : ATmake("Bool(False)"); return b ? ATmake("Bool(True)") : ATmake("Bool(False)");

View file

@ -68,6 +68,10 @@ Expr makeAttrs(const ATermMap & attrs);
/* Perform a set of substitutions on an expression. */ /* Perform a set of substitutions on an expression. */
Expr substitute(const ATermMap & subs, Expr e); Expr substitute(const ATermMap & subs, Expr e);
/* Check whether all variables are defined in the given expression.
Throw an exception if this isn't the case. */
void checkVarDefs(const ATermMap & def, Expr e);
/* Create an expression representing a boolean. */ /* Create an expression representing a boolean. */
Expr makeBool(bool b); Expr makeBool(bool b);

View file

@ -81,6 +81,22 @@ static Expr parse(const char * text, const string & location,
if (res) throw Error(data.error); if (res) throw Error(data.error);
ATermMap primOps;
primOps.set("import", (ATerm) ATempty);
primOps.set("derivation", (ATerm) ATempty);
primOps.set("true", (ATerm) ATempty);
primOps.set("false", (ATerm) ATempty);
primOps.set("null", (ATerm) ATempty);
primOps.set("isNull", (ATerm) ATempty);
primOps.set("toString", (ATerm) ATempty);
primOps.set("baseNameOf", (ATerm) ATempty);
try {
checkVarDefs(primOps, data.result);
} catch (Error & e) {
throw Error(format("%1%, in %2%") % e.msg() % location);
}
return data.result; return data.result;
} }