about summary refs log tree commit diff
path: root/src/libnix
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnix')
-rw-r--r--src/libnix/Makefile.am9
-rw-r--r--src/libnix/aterm.cc93
-rw-r--r--src/libnix/aterm.hh55
-rw-r--r--src/libnix/expr.cc52
-rw-r--r--src/libnix/expr.hh8
-rw-r--r--src/libnix/normalise.cc2
-rw-r--r--src/libnix/references.hh6
-rw-r--r--src/libnix/test-aterm.cc66
8 files changed, 247 insertions, 44 deletions
diff --git a/src/libnix/Makefile.am b/src/libnix/Makefile.am
index b890ba8c0548..7671b1613d19 100644
--- a/src/libnix/Makefile.am
+++ b/src/libnix/Makefile.am
@@ -2,8 +2,15 @@ noinst_LIBRARIES = libnix.a
 
 libnix_a_SOURCES = util.cc hash.cc archive.cc md5.c \
  store.cc expr.cc normalise.cc exec.cc \
- globals.cc db.cc references.cc pathlocks.cc
+ globals.cc db.cc references.cc pathlocks.cc aterm.cc
 
 AM_CXXFLAGS = -DSYSTEM=\"@host@\" -Wall -I.. -I../../externals/inst/include
 
 EXTRA_DIST = *.hh *.h test-builder-*.sh
+
+check_PROGRAMS = test-aterm
+
+test_aterm_SOURCES = test-aterm.cc
+test_aterm_LDADD = libnix.a $(LDADD) ../boost/format/libformat.a \
+ -L../../externals/inst/lib -ldb_cxx -lATerm
+
diff --git a/src/libnix/aterm.cc b/src/libnix/aterm.cc
new file mode 100644
index 000000000000..de7c359521a7
--- /dev/null
+++ b/src/libnix/aterm.cc
@@ -0,0 +1,93 @@
+#include "aterm.hh"
+
+
+string atPrint(ATerm t)
+{
+    if (!t) throw Error("attempt to print null aterm");
+    char * s = ATwriteToString(t);
+    if (!s) throw Error("cannot print term");
+    return s;
+}
+
+
+ostream & operator << (ostream & stream, ATerm e)
+{
+    return stream << atPrint(e);
+}
+
+
+ATMatcher & atMatch(ATMatcher & pos, ATerm t)
+{
+    pos.t = t;
+    pos.pos = ATMatcher::funPos;
+    return pos;
+}
+
+
+static inline bool failed(const ATMatcher & pos)
+{
+    return pos.pos == ATMatcher::failPos;
+}
+
+
+static inline ATMatcher & fail(ATMatcher & pos)
+{
+    pos.pos = ATMatcher::failPos;
+    return pos;
+}
+
+
+ATMatcher & operator >> (ATMatcher & pos, ATerm & out)
+{
+    out = 0;
+    if (failed(pos)) return pos;
+    if (pos.pos == ATMatcher::funPos || 
+        ATgetType(pos.t) != AT_APPL ||
+        pos.pos >= (int) ATgetArity(ATgetAFun(pos.t)))
+        return fail(pos);
+    out = ATgetArgument(pos.t, pos.pos);
+    pos.pos++;
+    return pos;
+}
+
+
+ATMatcher & operator >> (ATMatcher & pos, string & out)
+{
+    out = "";
+    if (pos.pos == ATMatcher::funPos) {
+        if (ATgetType(pos.t) != AT_APPL) return fail(pos);
+        out = ATgetName(ATgetAFun(pos.t));
+        pos.pos = 0;
+    } else {
+        ATerm t;
+        pos = pos >> t;
+        if (failed(pos)) return pos;
+        if (ATgetType(t) != AT_APPL ||
+            ATgetArity(ATgetAFun(t)) != 0)
+            return fail(pos);
+        out = ATgetName(ATgetAFun(t));
+    }
+    return pos;
+}
+
+
+ATMatcher & operator >> (ATMatcher & pos, const string & s)
+{
+    string s2;
+    pos = pos >> s2;
+    if (failed(pos)) return pos;
+    if (s != s2) return fail(pos);
+    return pos;
+}
+
+
+ATMatcher & operator >> (ATMatcher & pos, ATermList & out)
+{
+    out = 0;
+    ATerm t;
+    pos = pos >> t;
+    if (failed(pos)) return pos;
+    if (ATgetType(t) != AT_LIST) return fail(pos);
+    out = (ATermList) t;
+    return pos;
+}
diff --git a/src/libnix/aterm.hh b/src/libnix/aterm.hh
new file mode 100644
index 000000000000..1e4ee80eeab5
--- /dev/null
+++ b/src/libnix/aterm.hh
@@ -0,0 +1,55 @@
+#ifndef __ATERM_H
+#define __ATERM_H
+
+extern "C" {
+#include <aterm2.h>
+}
+
+#include "util.hh"
+
+
+/* Print an ATerm. */
+string atPrint(ATerm t);
+
+/* Write an ATerm to an output stream. */
+ostream & operator << (ostream & stream, ATerm e);
+
+/* Type-safe matching. */
+
+struct ATMatcher 
+{
+    ATerm t;
+    int pos;
+    const static int failPos = -2;
+    const static int funPos = -1;
+
+    ATMatcher() : t(0), pos(failPos)
+    {
+    }
+
+    operator bool() const
+    {
+        return pos != failPos;
+    }
+};
+
+/* Initiate matching of a term. */
+ATMatcher & atMatch(ATMatcher & pos, ATerm t);
+
+/* Get the next argument of an application. */
+ATMatcher & operator >> (ATMatcher & pos, ATerm & out);
+
+/* Get the name of the function symbol of an applicatin, or the next
+   argument of an application as a string. */
+ATMatcher & operator >> (ATMatcher & pos, string & out);
+
+/* Like the previous, but check that the string is equal to the given
+   string. */
+ATMatcher & operator >> (ATMatcher & pos, const string & s);
+
+/* Get the next argument of an application, and verify that it is a
+   list. */
+ATMatcher & operator >> (ATMatcher & pos, ATermList & out);
+
+
+#endif /* !__ATERM_H */
diff --git a/src/libnix/expr.cc b/src/libnix/expr.cc
index 9bbe80ab4c88..67fa69f72fab 100644
--- a/src/libnix/expr.cc
+++ b/src/libnix/expr.cc
@@ -3,14 +3,6 @@
 #include "store.hh"
 
 
-string printTerm(ATerm t)
-{
-    char * s = ATwriteToString(t);
-    if (!s) throw Error("cannot print term");
-    return s;
-}
-
-
 Error badTerm(const format & f, ATerm t)
 {
     char * s = ATwriteToString(t);
@@ -26,7 +18,7 @@ Error badTerm(const format & f, ATerm t)
 
 Hash hashTerm(ATerm t)
 {
-    return hashString(printTerm(t));
+    return hashString(atPrint(t));
 }
 
 
@@ -50,10 +42,11 @@ Path writeTerm(ATerm t, const string & suffix)
 
 static void parsePaths(ATermList paths, PathSet & out)
 {
+    ATMatcher m;
     while (!ATisEmpty(paths)) {
-        char * s;
+        string s;
         ATerm t = ATgetFirst(paths);
-        if (!ATmatch(t, "<str>", &s))
+        if (!(atMatch(m, t) >> s))
             throw badTerm("not a path", t);
         out.insert(s);
         paths = ATgetNext(paths);
@@ -91,21 +84,22 @@ static void checkClosure(const Closure & closure)
 static bool parseClosure(ATerm t, Closure & closure)
 {
     ATermList roots, elems;
-    
-    if (!ATmatch(t, "Closure([<list>], [<list>])", &roots, &elems))
+    ATMatcher m;
+
+    if (!(atMatch(m, t) >> "Closure" >> roots >> elems))
         return false;
 
     parsePaths(roots, closure.roots);
 
     while (!ATisEmpty(elems)) {
-        char * s1;
+        string path;
         ATermList refs;
         ATerm t = ATgetFirst(elems);
-        if (!ATmatch(t, "(<str>, [<list>])", &s1, &refs))
+        if (!(atMatch(m, t) >> "" >> path >> refs))
             throw badTerm("not a closure element", t);
         ClosureElem elem;
         parsePaths(refs, elem.refs);
-        closure.elems[s1] = elem;
+        closure.elems[path] = elem;
         elems = ATgetNext(elems);
     }
 
@@ -116,19 +110,13 @@ static bool parseClosure(ATerm t, Closure & closure)
 
 static bool parseDerivation(ATerm t, Derivation & derivation)
 {
+    ATMatcher m;
     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;
-    }
+    string builder, platform;
+
+    if (!(atMatch(m, t) >> "Derive" >> outs >> ins >> platform
+            >> builder >> args >> bnds))
+        return false;
 
     parsePaths(outs, derivation.outputs);
     parsePaths(ins, derivation.inputs);
@@ -137,18 +125,18 @@ static bool parseDerivation(ATerm t, Derivation & derivation)
     derivation.platform = platform;
     
     while (!ATisEmpty(args)) {
-        char * s;
+        string s;
         ATerm arg = ATgetFirst(args);
-        if (!ATmatch(arg, "<str>", &s))
+        if (!(atMatch(m, arg) >> s))
             throw badTerm("string expected", arg);
         derivation.args.push_back(s);
         args = ATgetNext(args);
     }
 
     while (!ATisEmpty(bnds)) {
-        char * s1, * s2;
+        string s1, s2;
         ATerm bnd = ATgetFirst(bnds);
-        if (!ATmatch(bnd, "(<str>, <str>)", &s1, &s2))
+        if (!(atMatch(m, bnd) >> "" >> s1 >> s2))
             throw badTerm("tuple of strings expected", bnd);
         derivation.env[s1] = s2;
         bnds = ATgetNext(bnds);
diff --git a/src/libnix/expr.hh b/src/libnix/expr.hh
index 7d0420935f9d..f5abf9af0d3a 100644
--- a/src/libnix/expr.hh
+++ b/src/libnix/expr.hh
@@ -1,10 +1,7 @@
 #ifndef __FSTATE_H
 #define __FSTATE_H
 
-extern "C" {
-#include <aterm2.h>
-}
-
+#include "aterm.hh"
 #include "store.hh"
 
 
@@ -43,9 +40,6 @@ struct NixExpr
 };
 
 
-/* Return a canonical textual representation of an expression. */
-string printTerm(ATerm t);
-
 /* Throw an exception with an error message containing the given
    aterm. */
 Error badTerm(const format & f, ATerm t);
diff --git a/src/libnix/normalise.cc b/src/libnix/normalise.cc
index 49f86cc6fe18..cb2bf4f5b183 100644
--- a/src/libnix/normalise.cc
+++ b/src/libnix/normalise.cc
@@ -239,7 +239,7 @@ Path normaliseNixExpr(const Path & _nePath, PathSet pending)
     /* Write the normal form.  This does not have to occur in the
        transaction below because writing terms is idem-potent. */
     ATerm nfTerm = unparseNixExpr(nf);
-    printMsg(lvlVomit, format("normal form: %1%") % printTerm(nfTerm));
+    printMsg(lvlVomit, format("normal form: %1%") % atPrint(nfTerm));
     Path nfPath = writeTerm(nfTerm, "-s");
 
     /* Register each outpat path, and register the normal form.  This
diff --git a/src/libnix/references.hh b/src/libnix/references.hh
index d009453d6a00..ada23a883302 100644
--- a/src/libnix/references.hh
+++ b/src/libnix/references.hh
@@ -1,5 +1,5 @@
-#ifndef __VALUES_H
-#define __VALUES_H
+#ifndef __REFERENCES_H
+#define __REFERENCES_H
 
 #include "util.hh"
 
@@ -7,4 +7,4 @@
 Strings filterReferences(const Path & path, const Strings & refs);
 
 
-#endif /* !__VALUES_H */
+#endif /* !__REFERENCES_H */
diff --git a/src/libnix/test-aterm.cc b/src/libnix/test-aterm.cc
new file mode 100644
index 000000000000..325639ca4f30
--- /dev/null
+++ b/src/libnix/test-aterm.cc
@@ -0,0 +1,66 @@
+#include "aterm.hh"
+#include <iostream>
+
+
+void runTests()
+{
+    verbosity = lvlDebug;
+
+    ATMatcher pos;
+
+    ATerm t = ATmake("Call(Foo, Bar, \"xyz\")");
+    
+    debug(format("term: %1%") % t);
+
+    string fun, arg3;
+    ATerm lhs, rhs;
+
+    if (!(atMatch(pos, t) >> "Call" >> lhs >> rhs >> arg3))
+        throw Error("should succeed");
+    if (arg3 != "xyz") throw Error("bad 1");
+
+    if (!(atMatch(pos, t) >> fun >> lhs >> rhs >> arg3))
+        throw Error("should succeed");
+    if (fun != "Call") throw Error("bad 2");
+    if (arg3 != "xyz") throw Error("bad 3");
+
+    if (!(atMatch(pos, t) >> fun >> lhs >> rhs >> "xyz"))
+        throw Error("should succeed");
+
+    if (atMatch(pos, t) >> fun >> lhs >> rhs >> "abc")
+        throw Error("should fail");
+
+    if (atMatch(pos, t) >> "Call" >> lhs >> rhs >> "abc")
+        throw Error("should fail");
+
+    t = ATmake("X([A, B, C], \"abc\")");
+
+    ATerm t1, t2, t3;
+    if (atMatch(pos, t) >> "X" >> t1 >> t2 >> t3)
+        throw Error("should fail");
+    if (!(atMatch(pos, t) >> "X" >> t1 >> t2))
+        throw Error("should succeed");
+    ATermList ts;
+    if (!(atMatch(pos, t) >> "X" >> ts >> t2))
+        throw Error("should succeed");
+    if (ATgetLength(ts) != 3)
+        throw Error("bad");
+    if (atMatch(pos, t) >> "X" >> t1 >> ts)
+        throw Error("should fail");
+}
+
+
+int main(int argc, char * * argv)
+{
+    ATerm bottomOfStack;
+    ATinit(argc, argv, &bottomOfStack);
+
+    try {
+        runTests();
+    } catch (Error & e) {
+        printMsg(lvlError, format("error: %1%") % e.msg());
+        return 1;
+    }
+
+    return 0;
+}