about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2004-02-03T14·45+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2004-02-03T14·45+0000
commitc4f7ae4aa5fc7071cfa853ec5d75aaf00e7a97fc (patch)
tree99334d6825236ffcab5a64239b95878e733e9ab5
parent1c9c0a5a46822be60c999f0196567c9e17cf5fa3 (diff)
* Verify that all variables in a Nix expression are defined.
-rw-r--r--src/libexpr/eval.cc18
-rw-r--r--src/libexpr/nixexpr.cc75
-rw-r--r--src/libexpr/nixexpr.hh4
-rw-r--r--src/libexpr/parser.cc16
4 files changed, 92 insertions, 21 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 820e934a635a..bbb1393f8388 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(<term>, <str>)", e, s.c_str()));
+        subs.set(name, ATmake("Select(<term>, <term>)", 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 b0f506e65d6f..92027a70ebd7 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(<term>, <term>)", 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(<term>, <term>)",
             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 011c2900e12a..26c29a2f38b1 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 2574a55bd49e..0a550fb35507 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;
 }