about summary refs log tree commit diff
path: root/src/libexpr/eval.cc
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2004-04-05T22·27+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2004-04-05T22·27+0000
commit59b94ee18ac0cba5c7b261ee72550a4d3db0acb5 (patch)
tree9dbe6721699439cda3ce68ac86acbe38e9839e66 /src/libexpr/eval.cc
parenta520b1cbc3327dfb8e3c6f503dfd0bd41e0a6d55 (diff)
* When something goes wrong in the evaluation of a Nix expression,
  print a nice backtrace of the stack, rather than vomiting a gigantic
  (and useless) aterm on the screen.  Example:

    error: while evaluating file `.../pkgs/system/test.nix':
    while evaluating attribute `subversion' at `.../pkgs/system/all-packages-generic.nix', line 533:
    while evaluating function at `.../pkgs/applications/version-management/subversion/default.nix', line 1:
    assertion failed at `.../pkgs/applications/version-management/subversion/default.nix', line 13

  Since the Nix expression language is lazy, the trace may be
  misleading.  The purpose is to provide a hint as to the location of
  the problem.    
  

Diffstat (limited to 'src/libexpr/eval.cc')
-rw-r--r--src/libexpr/eval.cc84
1 files changed, 55 insertions, 29 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 5ae4d6de8edc..af0ab913cae1 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -77,16 +77,16 @@ static Expr substArgs(Expr body, ATermList formals, Expr arg)
         Expr key = *i;
         Expr cur = subs.get(key);
         if (!cur)
-            throw badTerm(format("function has no formal argument `%1%'")
-                % aterm2String(key), arg);
+            throw Error(format("unexpected function argument `%1%'")
+                % aterm2String(key));
         subs.set(key, args.get(key));
     }
 
     /* Check that all arguments are defined. */
     for (ATermIterator i(subs.keys()); i; ++i)
         if (subs.get(*i) == undefined)
-            throw badTerm(format("formal argument `%1%' missing")
-                % aterm2String(*i), arg);
+            throw Error(format("required function argument `%1%' missing")
+                % aterm2String(*i));
     
     return substitute(subs, body);
 }
@@ -119,16 +119,18 @@ ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds)
     /* Create the non-recursive set. */
     ATermMap as;
     for (ATermIterator i(rbnds); i; ++i) {
-        if (!(atMatch(m, *i) >> "Bind" >> name >> e2))
+        ATerm pos;
+        if (!(atMatch(m, *i) >> "Bind" >> name >> e2 >> pos))
             abort(); /* can't happen */
-        as.set(name, substitute(subs, e2));
+        as.set(name, ATmake("(<term>, <term>)", substitute(subs, e2), pos));
     }
 
     /* Copy the non-recursive bindings.  !!! inefficient */
     for (ATermIterator i(nrbnds); i; ++i) {
-        if (!(atMatch(m, *i) >> "Bind" >> name >> e2))
+        ATerm pos;
+        if (!(atMatch(m, *i) >> "Bind" >> name >> e2 >> pos))
             abort(); /* can't happen */
-        as.set(name, e2);
+        as.set(name, ATmake("(<term>, <term>)", e2, pos));
     }
 
     return makeAttrs(as);
@@ -140,8 +142,8 @@ static Expr updateAttrs(Expr e1, Expr e2)
     /* Note: e1 and e2 should be in normal form. */
 
     ATermMap attrs;
-    queryAllAttrs(e1, attrs);
-    queryAllAttrs(e2, attrs);
+    queryAllAttrs(e1, attrs, true);
+    queryAllAttrs(e2, attrs, true);
 
     return makeAttrs(attrs);
 }
@@ -153,7 +155,7 @@ string evalString(EvalState & state, Expr e)
     ATMatcher m;
     string s;
     if (!(atMatch(m, e) >> "Str" >> s))
-        throw badTerm("string expected", e);
+        throw Error("string expected");
     return s;
 }
 
@@ -164,7 +166,7 @@ Path evalPath(EvalState & state, Expr e)
     ATMatcher m;
     string s;
     if (!(atMatch(m, e) >> "Path" >> s))
-        throw badTerm("path expected", e);
+        throw Error("path expected");
     return s;
 }
 
@@ -175,7 +177,7 @@ bool evalBool(EvalState & state, Expr e)
     ATMatcher m;
     if (atMatch(m, e) >> "Bool" >> "True") return true;
     else if (atMatch(m, e) >> "Bool" >> "False") return false;
-    else throw badTerm("expecting a boolean", e);
+    else throw Error("boolean expected");
 }
 
 
@@ -183,7 +185,7 @@ Expr evalExpr2(EvalState & state, Expr e)
 {
     ATMatcher m;
     Expr e1, e2, e3, e4;
-    ATerm name;
+    ATerm name, pos;
 
     /* Normal forms. */
     string cons;
@@ -219,6 +221,7 @@ Expr evalExpr2(EvalState & state, Expr e)
     if (atMatch(m, e) >> "Call" >> e1 >> e2) {
 
         ATermList formals;
+        ATerm pos;
         
         /* Evaluate the left-hand side. */
         e1 = evalExpr(state, e1);
@@ -229,25 +232,42 @@ Expr evalExpr2(EvalState & state, Expr e)
             if (primOp) return primOp(state, e2); else abort();
         }
 
-        else if (atMatch(m, e1) >> "Function" >> formals >> e4)
-            return evalExpr(state, 
-                substArgs(e4, formals, evalExpr(state, e2)));
+        else if (atMatch(m, e1) >> "Function" >> formals >> e4 >> pos) {
+            e2 = evalExpr(state, e2);
+            try {
+                return evalExpr(state, substArgs(e4, formals, e2));
+            } catch (Error & e) {
+                throw Error(format("while evaluating function at %1%:\n%2%")
+                    % showPos(pos) % e.msg());
+            }
+        }
         
-        else if (atMatch(m, e1) >> "Function1" >> name >> e4) {
-            ATermMap subs;
-            subs.set(name, e2);
-            return evalExpr(state, substitute(subs, e4));
+        else if (atMatch(m, e1) >> "Function1" >> name >> e4 >> pos) {
+            try {
+                ATermMap subs;
+                subs.set(name, e2);
+                return evalExpr(state, substitute(subs, e4));
+            } catch (Error & e) {
+                throw Error(format("while evaluating function at %1%:\n%2%")
+                    % showPos(pos) % e.msg());
+            }
         }
         
-        else throw badTerm("expecting a function or primop", e1);
+        else throw Error("function or primop expected in function call");
     }
 
     /* Attribute selection. */
     string s1;
     if (atMatch(m, e) >> "Select" >> e1 >> s1) {
-        Expr a = queryAttr(evalExpr(state, e1), s1);
-        if (!a) throw badTerm(format("missing attribute `%1%'") % s1, e);
-        return evalExpr(state, a);
+        ATerm pos;
+        Expr a = queryAttr(evalExpr(state, e1), s1, pos);
+        if (!a) throw Error(format("attribute `%1%' missing") % s1);
+        try {
+            return evalExpr(state, a);
+        } catch (Error & e) {
+            throw Error(format("while evaluating attribute `%1%' at %2%:\n%3%")
+                % s1 % showPos(pos) % e.msg());
+        }
     }
 
     /* Mutually recursive sets. */
@@ -264,8 +284,9 @@ Expr evalExpr2(EvalState & state, Expr e)
     }
 
     /* Assertions. */
-    if (atMatch(m, e) >> "Assert" >> e1 >> e2) {
-        if (!evalBool(state, e1)) throw badTerm("guard failed", e);
+    if (atMatch(m, e) >> "Assert" >> e1 >> e2 >> pos) {
+        if (!evalBool(state, e1))
+            throw Error(format("assertion failed at %1%") % showPos(pos));
         return evalExpr(state, e2);
     }
 
@@ -323,7 +344,7 @@ Expr evalExpr(EvalState & state, Expr e)
     Expr nf = state.normalForms.get(e);
     if (nf) {
         if (nf == state.blackHole)
-            throw badTerm("infinite recursion", e);
+            throw Error("infinite recursion encountered");
         state.nrCached++;
         return nf;
     }
@@ -340,7 +361,12 @@ Expr evalFile(EvalState & state, const Path & path)
 {
     startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
     Expr e = parseExprFromFile(state, path);
-    return evalExpr(state, e);
+    try {
+        return evalExpr(state, e);
+    } catch (Error & e) {
+        throw Error(format("while evaluating file `%1%':\n%2%")
+            % path % e.msg());
+    }
 }