From c4f7ae4aa5fc7071cfa853ec5d75aaf00e7a97fc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 3 Feb 2004 14:45:34 +0000 Subject: [PATCH] * Verify that all variables in a Nix expression are defined. --- src/libexpr/eval.cc | 18 +++++----- src/libexpr/nixexpr.cc | 75 +++++++++++++++++++++++++++++++++++------- src/libexpr/nixexpr.hh | 4 +++ src/libexpr/parser.cc | 16 +++++++++ 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 820e934a63..bbb1393f83 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -64,30 +64,30 @@ ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds) /* Create the substitution list. */ ATermMap subs; for (ATermIterator i(rbnds); i; ++i) { - string s; + ATerm name; Expr e2; - if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) + if (!(atMatch(m, *i) >> "Bind" >> name >> e2)) abort(); /* can't happen */ - subs.set(s, ATmake("Select(, )", e, s.c_str())); + subs.set(name, ATmake("Select(, )", e, name)); } /* Create the non-recursive set. */ ATermMap as; for (ATermIterator i(rbnds); i; ++i) { - string s; + ATerm name; Expr e2; - if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) + if (!(atMatch(m, *i) >> "Bind" >> name >> e2)) abort(); /* can't happen */ - as.set(s, substitute(subs, e2)); + as.set(name, substitute(subs, e2)); } /* Copy the non-recursive bindings. !!! inefficient */ for (ATermIterator i(nrbnds); i; ++i) { - string s; + ATerm name; Expr e2; - if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) + if (!(atMatch(m, *i) >> "Bind" >> name >> e2)) abort(); /* can't happen */ - as.set(s, e2); + as.set(name, e2); } return makeAttrs(as); diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index b0f506e65d..92027a70eb 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -156,10 +156,10 @@ Expr substitute(const ATermMap & subs, Expr e) checkInterrupt(); ATMatcher m; - string s; + ATerm name; - if (atMatch(m, e) >> "Var" >> s) { - Expr sub = subs.get(s); + if (atMatch(m, e) >> "Var" >> name) { + Expr sub = subs.get(name); return sub ? sub : e; } @@ -170,11 +170,10 @@ Expr substitute(const ATermMap & subs, Expr e) if (atMatch(m, e) >> "Function" >> formals >> body) { ATermMap subs2(subs); for (ATermIterator i(formals); i; ++i) { - Expr def; - if (!(atMatch(m, *i) >> "NoDefFormal" >> s) && - !(atMatch(m, *i) >> "DefFormal" >> s >> def)) + if (!(atMatch(m, *i) >> "NoDefFormal" >> name) && + !(atMatch(m, *i) >> "DefFormal" >> name)) abort(); - subs2.remove(s); + subs2.remove(name); } return ATmake("Function(, )", formals, substitute(subs2, body)); @@ -184,12 +183,11 @@ Expr substitute(const ATermMap & subs, Expr e) ATermList rbnds, nrbnds; if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds) { ATermMap subs2(subs); - for (ATermIterator i(rbnds); i; ++i) { - Expr e; - if (!(atMatch(m, *i) >> "Bind" >> s >> e)) + for (ATermIterator i(rbnds); i; ++i) + if (atMatch(m, *i) >> "Bind" >> name) + subs2.remove(name); + else abort(); /* can't happen */ - subs2.remove(s); - } return ATmake("Rec(, )", substitute(subs2, (ATerm) rbnds), 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) { return b ? ATmake("Bool(True)") : ATmake("Bool(False)"); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 011c2900e1..26c29a2f38 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -68,6 +68,10 @@ Expr makeAttrs(const ATermMap & attrs); /* Perform a set of substitutions on an expression. */ 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. */ Expr makeBool(bool b); diff --git a/src/libexpr/parser.cc b/src/libexpr/parser.cc index 2574a55bd4..0a550fb355 100644 --- a/src/libexpr/parser.cc +++ b/src/libexpr/parser.cc @@ -81,6 +81,22 @@ static Expr parse(const char * text, const string & location, 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; }