about summary refs log tree commit diff
path: root/src/libnix/expr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnix/expr.cc')
-rw-r--r--src/libnix/expr.cc222
1 files changed, 222 insertions, 0 deletions
diff --git a/src/libnix/expr.cc b/src/libnix/expr.cc
new file mode 100644
index 000000000000..cead803425ba
--- /dev/null
+++ b/src/libnix/expr.cc
@@ -0,0 +1,222 @@
+#include "expr.hh"
+#include "globals.hh"
+#include "store.hh"
+
+
+string printTerm(ATerm t)
+{
+    char * s = ATwriteToString(t);
+    return s;
+}
+
+
+Error badTerm(const format & f, ATerm t)
+{
+    return Error(format("%1%, in `%2%'") % f.str() % printTerm(t));
+}
+
+
+Hash hashTerm(ATerm t)
+{
+    return hashString(printTerm(t));
+}
+
+
+Path writeTerm(ATerm t, const string & suffix)
+{
+    /* The id of a term is its hash. */
+    Hash h = hashTerm(t);
+
+    Path path = canonPath(nixStore + "/" + 
+        (string) h + suffix + ".nix");
+
+    if (!isValidPath(path)) {
+        char * s = ATwriteToString(t);
+        if (!s) throw Error(format("cannot write aterm to `%1%'") % path);
+        addTextToStore(path, string(s));
+    }
+    
+    return path;
+}
+
+
+static void parsePaths(ATermList paths, PathSet & out)
+{
+    while (!ATisEmpty(paths)) {
+        char * s;
+        ATerm t = ATgetFirst(paths);
+        if (!ATmatch(t, "<str>", &s))
+            throw badTerm("not a path", t);
+        out.insert(s);
+        paths = ATgetNext(paths);
+    }
+}
+
+
+static void checkClosure(const Closure & closure)
+{
+    if (closure.elems.size() == 0)
+        throw Error("empty closure");
+
+    PathSet decl;
+    for (ClosureElems::const_iterator i = closure.elems.begin();
+         i != closure.elems.end(); i++)
+        decl.insert(i->first);
+    
+    for (PathSet::const_iterator i = closure.roots.begin();
+         i != closure.roots.end(); i++)
+        if (decl.find(*i) == decl.end())
+            throw Error(format("undefined root path `%1%'") % *i);
+    
+    for (ClosureElems::const_iterator i = closure.elems.begin();
+         i != closure.elems.end(); i++)
+        for (PathSet::const_iterator j = i->second.refs.begin();
+             j != i->second.refs.end(); j++)
+            if (decl.find(*j) == decl.end())
+                throw Error(
+		    format("undefined path `%1%' referenced by `%2%'")
+		    % *j % i->first);
+}
+
+
+/* Parse a closure. */
+static bool parseClosure(ATerm t, Closure & closure)
+{
+    ATermList roots, elems;
+    
+    if (!ATmatch(t, "Closure([<list>], [<list>])", &roots, &elems))
+        return false;
+
+    parsePaths(roots, closure.roots);
+
+    while (!ATisEmpty(elems)) {
+        char * s1;
+        ATermList refs;
+        ATerm t = ATgetFirst(elems);
+        if (!ATmatch(t, "(<str>, [<list>])", &s1, &refs))
+            throw badTerm("not a closure element", t);
+        ClosureElem elem;
+        parsePaths(refs, elem.refs);
+        closure.elems[s1] = elem;
+        elems = ATgetNext(elems);
+    }
+
+    checkClosure(closure);
+    return true;
+}
+
+
+static bool parseDerivation(ATerm t, Derivation & derivation)
+{
+    ATermList outs, ins, args, bnds;
+    char * builder;
+    char * platform;
+
+    if (!ATmatch(t, "Derive([<list>], [<list>], <str>, <str>, [<list>], [<list>])",
+            &outs, &ins, &platform, &builder, &args, &bnds))
+    {
+        /* !!! compatibility -> remove eventually */
+        if (!ATmatch(t, "Derive([<list>], [<list>], <str>, <str>, [<list>])",
+                &outs, &ins, &builder, &platform, &bnds))
+            return false;
+        args = ATempty;
+    }
+
+    parsePaths(outs, derivation.outputs);
+    parsePaths(ins, derivation.inputs);
+
+    derivation.builder = builder;
+    derivation.platform = platform;
+    
+    while (!ATisEmpty(args)) {
+        char * s;
+        ATerm arg = ATgetFirst(args);
+        if (!ATmatch(arg, "<str>", &s))
+            throw badTerm("string expected", arg);
+        derivation.args.push_back(s);
+        args = ATgetNext(args);
+    }
+
+    while (!ATisEmpty(bnds)) {
+        char * s1, * s2;
+        ATerm bnd = ATgetFirst(bnds);
+        if (!ATmatch(bnd, "(<str>, <str>)", &s1, &s2))
+            throw badTerm("tuple of strings expected", bnd);
+        derivation.env[s1] = s2;
+        bnds = ATgetNext(bnds);
+    }
+
+    return true;
+}
+
+
+NixExpr parseNixExpr(ATerm t)
+{
+    NixExpr ne;
+    if (parseClosure(t, ne.closure))
+        ne.type = NixExpr::neClosure;
+    else if (parseDerivation(t, ne.derivation))
+        ne.type = NixExpr::neDerivation;
+    else throw badTerm("not a Nix expression", t);
+    return ne;
+}
+
+
+static ATermList unparsePaths(const PathSet & paths)
+{
+    ATermList l = ATempty;
+    for (PathSet::const_iterator i = paths.begin();
+         i != paths.end(); i++)
+        l = ATinsert(l, ATmake("<str>", i->c_str()));
+    return ATreverse(l);
+}
+
+
+static ATerm unparseClosure(const Closure & closure)
+{
+    ATermList roots = unparsePaths(closure.roots);
+    
+    ATermList elems = ATempty;
+    for (ClosureElems::const_iterator i = closure.elems.begin();
+         i != closure.elems.end(); i++)
+        elems = ATinsert(elems,
+            ATmake("(<str>, <term>)",
+                i->first.c_str(),
+                unparsePaths(i->second.refs)));
+
+    return ATmake("Closure(<term>, <term>)", roots, elems);
+}
+
+
+static ATerm unparseDerivation(const Derivation & derivation)
+{
+    ATermList args = ATempty;
+    for (Strings::const_iterator i = derivation.args.begin();
+         i != derivation.args.end(); i++)
+        args = ATinsert(args, ATmake("<str>", i->c_str()));
+
+    ATermList env = ATempty;
+    for (StringPairs::const_iterator i = derivation.env.begin();
+         i != derivation.env.end(); i++)
+        env = ATinsert(env,
+            ATmake("(<str>, <str>)", 
+                i->first.c_str(), i->second.c_str()));
+
+    return ATmake("Derive(<term>, <term>, <str>, <str>, <term>, <term>)",
+        unparsePaths(derivation.outputs),
+        unparsePaths(derivation.inputs),
+        derivation.platform.c_str(),
+        derivation.builder.c_str(),
+        ATreverse(args),
+        ATreverse(env));
+}
+
+
+ATerm unparseNixExpr(const NixExpr & ne)
+{
+    if (ne.type == NixExpr::neClosure)
+        return unparseClosure(ne.closure);
+    else if (ne.type == NixExpr::neDerivation)
+        return unparseDerivation(ne.derivation);
+    else abort();
+}