about summary refs log tree commit diff
path: root/src/libexpr
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2010-04-07T13·55+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2010-04-07T13·55+0000
commitfc92244ba81d884e099d467a3b82fbdcbff7fc40 (patch)
treefe66575aab172941946eab41f3974961fe00eb1b /src/libexpr
parenta353aef0b157e7c628fd18640bd6c45215f3e606 (diff)
* Implemented the primops necessary for generating the NixOS manual.
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/eval-test.cc5
-rw-r--r--src/libexpr/eval.cc52
-rw-r--r--src/libexpr/eval.hh21
-rw-r--r--src/libexpr/expr-to-xml.cc147
-rw-r--r--src/libexpr/expr-to-xml.hh5
-rw-r--r--src/libexpr/get-drvs.cc7
-rw-r--r--src/libexpr/primops.cc64
7 files changed, 157 insertions, 144 deletions
diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc
index 3399aedc4c8c..eac95094fd1f 100644
--- a/src/libexpr/eval-test.cc
+++ b/src/libexpr/eval-test.cc
@@ -16,7 +16,8 @@ void doTest(string s)
     Expr e = parseExprFromString(state, s, absPath("."));
     printMsg(lvlError, format(">>>>> %1%") % e);
     Value v;
-    state.strictEval(e, v);
+    state.eval(e, v);
+    state.strictForceValue(v);
     printMsg(lvlError, format("result: %1%") % v);
 }
 
@@ -76,6 +77,8 @@ void run(Strings args)
     doTest("let x = 1; as = rec { inherit x; y = as.x; }; in as.y");
     doTest("let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y");
     doTest("let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x");
+    doTest("builtins.toXML 123");
+    doTest("builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }");
 }
 
 
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 5f6ab2655aff..0d18c552275a 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -302,6 +302,8 @@ void EvalState::eval(Env & env, Expr e, Value & v)
     
     //debug(format("eval: %1%") % e);
 
+    checkInterrupt();
+
     nrEvaluated++;
 
     Sym name;
@@ -639,28 +641,6 @@ bool EvalState::evalBool(Env & env, Expr e)
 }
 
 
-void EvalState::strictEval(Env & env, Expr e, Value & v)
-{
-    eval(env, e, v);
-    
-    if (v.type == tAttrs) {
-        foreach (Bindings::iterator, i, *v.attrs)
-            forceValue(i->second);
-    }
-    
-    else if (v.type == tList) {
-        for (unsigned int n = 0; n < v.list.length; ++n)
-            forceValue(v.list.elems[n]);
-    }
-}
-
-
-void EvalState::strictEval(Expr e, Value & v)
-{
-    strictEval(baseEnv, e, v);
-}
-
-
 void EvalState::forceValue(Value & v)
 {
     if (v.type == tThunk) {
@@ -678,6 +658,22 @@ void EvalState::forceValue(Value & v)
 }
 
 
+void EvalState::strictForceValue(Value & v)
+{
+    forceValue(v);
+    
+    if (v.type == tAttrs) {
+        foreach (Bindings::iterator, i, *v.attrs)
+            strictForceValue(i->second);
+    }
+    
+    else if (v.type == tList) {
+        for (unsigned int n = 0; n < v.list.length; ++n)
+            strictForceValue(v.list.elems[n]);
+    }
+}
+
+
 int EvalState::forceInt(Value & v)
 {
     forceValue(v);
@@ -750,6 +746,14 @@ string EvalState::forceStringNoCtx(Value & v)
 }
 
 
+bool EvalState::isDerivation(Value & v)
+{
+    if (v.type != tAttrs) return false;
+    Bindings::iterator i = v.attrs->find(toATerm("type"));
+    return i != v.attrs->end() && forceStringNoCtx(i->second) == "derivation";
+}
+
+
 string EvalState::coerceToString(Value & v, PathSet & context,
     bool coerceMore, bool copyToStore)
 {
@@ -769,7 +773,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
 
         if (!copyToStore) return path;
         
-        if (isDerivation(path))
+        if (nix::isDerivation(path))
             throw EvalError(format("file names are not allowed to end in `%1%'")
                 % drvExtension);
 
@@ -1415,5 +1419,5 @@ void EvalState::printStats()
         printATermMapStats();
 }
 
- 
+
 }
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index ae6b2106f487..a42b9ebebaba 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -169,17 +169,16 @@ public:
        type. */
     bool evalBool(Env & env, Expr e);
 
-    /* Evaluate an expression, and recursively evaluate list elements
-       and attributes. */
-    void strictEval(Expr e, Value & v);
-    void strictEval(Env & env, Expr e, Value & v);
-
     /* If `v' is a thunk, enter it and overwrite `v' with the result
        of the evaluation of the thunk.  If `v' is a delayed function
        application, call the function and overwrite `v' with the
        result.  Otherwise, this is a no-op. */
     void forceValue(Value & v);
 
+    /* Force a value, then recursively force list elements and
+       attributes. */
+    void strictForceValue(Value & v);
+
     /* Force `v', and then verify that it has the expected type. */
     int forceInt(Value & v);
     bool forceBool(Value & v);
@@ -190,6 +189,10 @@ public:
     string forceString(Value & v, PathSet & context);
     string forceStringNoCtx(Value & v);
 
+    /* Return true iff the value `v' denotes a derivation (i.e. a
+       set with attribute `type = "derivation"'). */
+    bool isDerivation(Value & v);
+
     /* String coercion.  Converts strings, paths and derivations to a
        string.  If `coerceMore' is set, also converts nulls, integers,
        booleans and lists to a string.  If `copyToStore' is set,
@@ -219,10 +222,10 @@ private:
        elements and attributes are compared recursively. */
     bool eqValues(Value & v1, Value & v2);
 
-    void callFunction(Value & fun, Value & arg, Value & v);
-
 public:
     
+    void callFunction(Value & fun, Value & arg, Value & v);
+
     /* Allocation primitives. */
     Value * allocValues(unsigned int count);
     Env & allocEnv();
@@ -237,6 +240,10 @@ public:
 };
 
 
+/* Return a string representing the type of the value `v'. */
+string showType(Value & v);
+
+
 #if 0
 /* Evaluate an expression to normal form. */
 Expr evalExpr(EvalState & state, Expr e);
diff --git a/src/libexpr/expr-to-xml.cc b/src/libexpr/expr-to-xml.cc
index 9b3062804e29..ad68739c39c0 100644
--- a/src/libexpr/expr-to-xml.cc
+++ b/src/libexpr/expr-to-xml.cc
@@ -18,24 +18,19 @@ static XMLAttrs singletonAttrs(const string & name, const string & value)
 }
 
 
-/* set<Expr> is safe because all the expressions are also reachable
-   from the stack, therefore can't be garbage-collected. */
-typedef set<Expr> ExprSet;
+static void printValueAsXML(EvalState & state, bool strict, Value & v,
+    XMLWriter & doc, PathSet & context, PathSet & drvsSeen);
 
 
-static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
-    ExprSet & drvsSeen);
-
-
-static void showAttrs(const ATermMap & attrs, XMLWriter & doc,
-    PathSet & context, ExprSet & drvsSeen)
+static void showAttrs(EvalState & state, bool strict, Bindings & attrs,
+    XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
 {
     StringSet names;
-    for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
-        names.insert(aterm2String(i->key));
-    for (StringSet::iterator i = names.begin(); i != names.end(); ++i) {
+    foreach (Bindings::iterator, i, attrs)
+        names.insert(aterm2String(i->first));
+    foreach (StringSet::iterator, i, names) {
         XMLOpenElement _(doc, "attr", singletonAttrs("name", *i));
-        printTermAsXML(attrs.get(toATerm(*i)), doc, context, drvsSeen);
+        printValueAsXML(state, strict, attrs[toATerm(*i)], doc, context, drvsSeen);
     }
 }
 
@@ -61,91 +56,93 @@ static void printPatternAsXML(Pattern pat, XMLWriter & doc)
 }
 
 
-static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
-    ExprSet & drvsSeen)
+static void printValueAsXML(EvalState & state, bool strict, Value & v,
+    XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
 {
-    XMLAttrs attrs;
-    string s;
-    ATerm s2;
-    int i;
-    ATermList as, es;
-    ATerm pat, body, pos;
-
     checkInterrupt();
 
-    if (matchStr(e, s, context)) /* !!! show the context? */
-        doc.writeEmptyElement("string", singletonAttrs("value", s));
-
-    else if (matchPath(e, s2))
-        doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s2)));
-
-    else if (matchNull(e))
-        doc.writeEmptyElement("null");
+    if (strict) state.forceValue(v);
+        
+    switch (v.type) {
 
-    else if (matchInt(e, i))
-        doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % i).str()));
+        case tInt:
+            doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str()));
+            break;
 
-    else if (e == eTrue)
-        doc.writeEmptyElement("bool", singletonAttrs("value", "true"));
+        case tBool:
+            doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
+            break;
 
-    else if (e == eFalse)
-        doc.writeEmptyElement("bool", singletonAttrs("value", "false"));
+        case tString:
+            /* !!! show the context? */
+            doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
+            break;
 
-    else if (matchAttrs(e, as)) {
-        ATermMap attrs;
-        queryAllAttrs(e, attrs);
+        case tPath:
+            doc.writeEmptyElement("path", singletonAttrs("value", v.path));
+            break;
 
-        Expr a = attrs.get(toATerm("type"));
-        if (a && matchStr(a, s, context) && s == "derivation") {
+        case tNull:
+            doc.writeEmptyElement("null");
+            break;
 
-            XMLAttrs xmlAttrs;
-            Path outPath, drvPath;
+        case tAttrs:
+            if (state.isDerivation(v)) {
+                XMLAttrs xmlAttrs;
             
-            a = attrs.get(toATerm("drvPath"));
-            if (matchStr(a, drvPath, context))
-                xmlAttrs["drvPath"] = drvPath;
-        
-            a = attrs.get(toATerm("outPath"));
-            if (matchStr(a, outPath, context))
-                xmlAttrs["outPath"] = outPath;
+                Bindings::iterator a = v.attrs->find(toATerm("derivation"));
+
+                Path drvPath;
+                a = v.attrs->find(toATerm("drvPath"));
+                if (a != v.attrs->end() && a->second.type == tString)
+                    xmlAttrs["drvPath"] = drvPath = a->second.string.s;
         
-            XMLOpenElement _(doc, "derivation", xmlAttrs);
+                a = v.attrs->find(toATerm("outPath"));
+                if (a != v.attrs->end() && a->second.type == tString)
+                    xmlAttrs["outPath"] = a->second.string.s;
+
+                XMLOpenElement _(doc, "derivation", xmlAttrs);
+
+                if (drvPath != "" && drvsSeen.find(drvPath) == drvsSeen.end()) {
+                    drvsSeen.insert(drvPath);
+                    showAttrs(state, strict, *v.attrs, doc, context, drvsSeen);
+                } else
+                    doc.writeEmptyElement("repeated");
+            }
+
+            else {
+                XMLOpenElement _(doc, "attrs");
+                showAttrs(state, strict, *v.attrs, doc, context, drvsSeen);
+            }
+            
+            break;
 
-            if (drvsSeen.find(e) == drvsSeen.end()) {
-                drvsSeen.insert(e);
-                showAttrs(attrs, doc, context, drvsSeen);
-            } else
-                doc.writeEmptyElement("repeated");
+        case tList: {
+            XMLOpenElement _(doc, "list");
+            for (unsigned int n = 0; n < v.list.length; ++n)
+                printValueAsXML(state, strict, v.list.elems[n], doc, context, drvsSeen);
+            break;
         }
 
-        else {
-            XMLOpenElement _(doc, "attrs");
-            showAttrs(attrs, doc, context, drvsSeen);
+        case tLambda: {
+            XMLOpenElement _(doc, "function");
+            printPatternAsXML(v.lambda.pat, doc);
+            break;
         }
-    }
 
-    else if (matchList(e, es)) {
-        XMLOpenElement _(doc, "list");
-        for (ATermIterator i(es); i; ++i)
-            printTermAsXML(*i, doc, context, drvsSeen);
+        default:
+            doc.writeEmptyElement("unevaluated");
     }
-
-    else if (matchFunction(e, pat, body, pos)) {
-        XMLOpenElement _(doc, "function");
-        printPatternAsXML(pat, doc);
-    }
-
-    else
-        doc.writeEmptyElement("unevaluated");
 }
 
 
-void printTermAsXML(Expr e, std::ostream & out, PathSet & context)
+void printValueAsXML(EvalState & state, bool strict,
+    Value & v, std::ostream & out, PathSet & context)
 {
     XMLWriter doc(true, out);
     XMLOpenElement root(doc, "expr");
-    ExprSet drvsSeen;    
-    printTermAsXML(e, doc, context, drvsSeen);
+    PathSet drvsSeen;    
+    printValueAsXML(state, strict, v, doc, context, drvsSeen);
 }
 
  
diff --git a/src/libexpr/expr-to-xml.hh b/src/libexpr/expr-to-xml.hh
index 36b8e40424d8..8f094185c4cc 100644
--- a/src/libexpr/expr-to-xml.hh
+++ b/src/libexpr/expr-to-xml.hh
@@ -5,11 +5,12 @@
 #include <map>
 
 #include "nixexpr.hh"
-#include "aterm.hh"
+#include "eval.hh"
 
 namespace nix {
 
-void printTermAsXML(Expr e, std::ostream & out, PathSet & context);
+void printValueAsXML(EvalState & state, bool strict,
+    Value & v, std::ostream & out, PathSet & context);
     
 }
 
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index f02392bed5d8..6e651d77f0cf 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -105,10 +105,7 @@ static bool getDerivation(EvalState & state, Value & v,
 {
     try {
         state.forceValue(v);
-        if (v.type != tAttrs) return true;
-
-        Bindings::iterator i = v.attrs->find(toATerm("type"));
-        if (i == v.attrs->end() || state.forceStringNoCtx(i->second) != "derivation") return true;
+        if (!state.isDerivation(v)) return true;
 
         /* Remove spurious duplicates (e.g., an attribute set like
            `rec { x = derivation {...}; y = x;}'. */
@@ -117,7 +114,7 @@ static bool getDerivation(EvalState & state, Value & v,
 
         DrvInfo drv;
     
-        i = v.attrs->find(toATerm("name"));
+        Bindings::iterator i = v.attrs->find(toATerm("name"));
         /* !!! We really would like to have a decent back trace here. */
         if (i == v.attrs->end()) throw TypeError("derivation name missing");
         drv.name = state.forceStringNoCtx(i->second);
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 98a31dc3a565..5fa2418b5f90 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -538,7 +538,6 @@ static void prim_readFile(EvalState & state, Value * * args, Value & v)
  *************************************************************/
 
 
-#if 0
 /* Convert the argument (which can be any Nix expression) to an XML
    representation returned in a string.  Not all Nix expressions can
    be sensibly or completely represented (e.g., functions). */
@@ -546,10 +545,9 @@ static void prim_toXML(EvalState & state, Value * * args, Value & v)
 {
     std::ostringstream out;
     PathSet context;
-    printTermAsXML(strictEvalExpr(state, args[0]), out, context);
-    return makeStr(out.str(), context);
+    printValueAsXML(state, true, *args[0], out, context);
+    mkString(v, out.str(), context);
 }
-#endif
 
 
 /* Store a string in the Nix store as a source file that can be used
@@ -582,13 +580,12 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v)
 }
 
 
-#if 0
 struct FilterFromExpr : PathFilter
 {
     EvalState & state;
-    Expr filter;
+    Value & filter;
     
-    FilterFromExpr(EvalState & state, Expr filter)
+    FilterFromExpr(EvalState & state, Value & filter)
         : state(state), filter(filter)
     {
     }
@@ -599,17 +596,25 @@ struct FilterFromExpr : PathFilter
         if (lstat(path.c_str(), &st))
             throw SysError(format("getting attributes of path `%1%'") % path);
 
-        Expr call =
-            makeCall(
-                makeCall(filter, makeStr(path)),
-                makeStr(
-                    S_ISREG(st.st_mode) ? "regular" :
-                    S_ISDIR(st.st_mode) ? "directory" :
-                    S_ISLNK(st.st_mode) ? "symlink" :
-                    "unknown" /* not supported, will fail! */
-                    ));
-                
-        return evalBool(state, call);
+        /* Call the filter function.  The first argument is the path,
+           the second is a string indicating the type of the file. */
+        Value arg1;
+        mkString(arg1, path);
+
+        Value fun2;
+        state.callFunction(filter, arg1, fun2);
+
+        Value arg2;
+        mkString(arg2, 
+            S_ISREG(st.st_mode) ? "regular" :
+            S_ISDIR(st.st_mode) ? "directory" :
+            S_ISLNK(st.st_mode) ? "symlink" :
+            "unknown" /* not supported, will fail! */);
+        
+        Value res;
+        state.callFunction(fun2, arg2, res);
+
+        return state.forceBool(res);
     }
 };
 
@@ -617,19 +622,22 @@ struct FilterFromExpr : PathFilter
 static void prim_filterSource(EvalState & state, Value * * args, Value & v)
 {
     PathSet context;
-    Path path = coerceToPath(state, args[1], context);
+    Path path = state.coerceToPath(*args[1], context);
     if (!context.empty())
         throw EvalError(format("string `%1%' cannot refer to other paths") % path);
 
-    FilterFromExpr filter(state, args[0]);
+    state.forceValue(*args[0]);
+    if (args[0]->type != tLambda)
+        throw TypeError(format("first argument in call to `filterSource' is not a function but %1%") % showType(*args[0]));
+
+    FilterFromExpr filter(state, *args[0]);
 
     Path dstPath = readOnlyMode
         ? computeStorePathForPath(path, true, htSHA256, filter).first
         : store->addToStore(path, true, htSHA256, filter);
 
-    return makeStr(dstPath, singleton<PathSet>(dstPath));
+    mkString(v, dstPath, singleton<PathSet>(dstPath));
 }
-#endif
 
 
 /*************************************************************
@@ -927,15 +935,15 @@ static void prim_stringLength(EvalState & state, Value * * args, Value & v)
 }
 
 
-#if 0
 static void prim_unsafeDiscardStringContext(EvalState & state, Value * * args, Value & v)
 {
     PathSet context;
-    string s = coerceToString(state, args[0], context);
-    return makeStr(s, PathSet());
+    string s = state.coerceToString(*args[0], context);
+    mkString(v, s, PathSet());
 }
 
 
+#if 0
 /* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
    builder without causing the derivation to be built (for instance,
    in the derivation that builds NARs in nix-push, when doing
@@ -1053,13 +1061,9 @@ void EvalState::createBaseEnv()
     addPrimOp("__readFile", 1, prim_readFile);
 
     // Creating files
-#if 0
     addPrimOp("__toXML", 1, prim_toXML);
-#endif
     addPrimOp("__toFile", 2, prim_toFile);
-#if 0
     addPrimOp("__filterSource", 2, prim_filterSource);
-#endif
 
     // Attribute sets
     addPrimOp("__attrNames", 1, prim_attrNames);
@@ -1091,8 +1095,8 @@ void EvalState::createBaseEnv()
     addPrimOp("toString", 1, prim_toString);
     addPrimOp("__substring", 3, prim_substring);
     addPrimOp("__stringLength", 1, prim_stringLength);
-#if 0    
     addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
+#if 0    
     addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
 #endif