about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libexpr/Makefile.am2
-rw-r--r--src/libexpr/eval.cc81
-rw-r--r--src/libexpr/eval.hh15
-rw-r--r--src/libexpr/nixexpr.hh6
-rw-r--r--src/libexpr/parser.cc2
-rw-r--r--src/libexpr/primops.cc62
-rw-r--r--src/libexpr/primops.hh38
7 files changed, 102 insertions, 104 deletions
diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am
index f3ffb6a035f5..45145a6bdb18 100644
--- a/src/libexpr/Makefile.am
+++ b/src/libexpr/Makefile.am
@@ -1,7 +1,7 @@
 noinst_LIBRARIES = libexpr.a
 
 libexpr_a_SOURCES = nixexpr.cc nixexpr.hh parser.cc parser.hh \
- eval.cc eval.hh primops.cc primops.hh \
+ eval.cc eval.hh primops.cc \
  lexer-tab.c lexer-tab.h parser-tab.c parser-tab.h
 
 EXTRA_DIST = lexer.l parser.y
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index af0ab913cae1..37677d3cb093 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1,20 +1,5 @@
 #include "eval.hh"
 #include "parser.hh"
-#include "primops.hh"
-
-
-static void addPrimOp(ATermMap & map, const string & name, void * f)
-{
-    map.set(name, (ATerm) ATmakeBlob(0, f));
-}
-
-
-static void * lookupPrimOp(ATermMap & map, ATerm name)
-{
-    ATermBlob b = (ATermBlob) map.get(name);
-    if (!b) return 0;
-    return ATgetBlobData(b);
-}
 
 
 EvalState::EvalState()
@@ -25,30 +10,15 @@ EvalState::EvalState()
     
     nrEvaluated = nrCached = 0;
 
-    addPrimOp0("true", primTrue);
-    addPrimOp0("false", primFalse);
-    addPrimOp0("null", primNull);
-
-    addPrimOp1("import", primImport);
-    addPrimOp1("derivation", primDerivation);
-    addPrimOp1("baseNameOf", primBaseNameOf);
-    addPrimOp1("toString", primToString);
-    addPrimOp1("isNull", primIsNull);
-
-    primOpsAll.add(primOps0);
-    primOpsAll.add(primOps1);
-}
-
-
-void EvalState::addPrimOp0(const string & name, PrimOp0 primOp)
-{
-    addPrimOp(primOps0, name, (void *) primOp);
+    addPrimOps();
 }
 
 
-void EvalState::addPrimOp1(const string & name, PrimOp1 primOp)
+void EvalState::addPrimOp(const string & name,
+    unsigned int arity, PrimOp primOp)
 {
-    addPrimOp(primOps1, name, (void *) primOp);
+    primOps.set(name, ATmake("(<int>, <term>)",
+        arity, ATmakeBlob(0, (void *) primOp)));
 }
 
 
@@ -200,7 +170,8 @@ Expr evalExpr2(EvalState & state, Expr e)
          cons == "Function" ||
          cons == "Function1" ||
          cons == "Attrs" ||
-         cons == "List"))
+         cons == "List" ||
+         cons == "PrimOp"))
         return e;
 
     /* The `Closed' constructor is just a way to prevent substitutions
@@ -208,13 +179,21 @@ Expr evalExpr2(EvalState & state, Expr e)
     if (atMatch(m, e) >> "Closed" >> e1)
         return evalExpr(state, e1);
 
-    /* Any encountered variables must be undeclared or primops. */
+    /* Any encountered variables must be primops (since undefined
+       variables are detected after parsing). */
     if (atMatch(m, e) >> "Var" >> name) {
-        PrimOp0 primOp = (PrimOp0) lookupPrimOp(state.primOps0, name);
-        if (primOp)
-            return primOp(state);
+        ATerm primOp = state.primOps.get(name);
+        if (!primOp)
+            throw Error(format("impossible: undefined variable `%1%'") % name);
+        int arity;
+        ATerm fun;
+        if (!(atMatch(m, primOp) >> "" >> arity >> fun)) abort();
+        if (arity == 0)
+            return ((PrimOp) ATgetBlobData((ATermBlob) fun))
+                (state, ATermVector());
         else
-            return e;
+            return ATmake("PrimOp(<int>, <term>, <term>)",
+                arity, fun, ATempty);
     }
 
     /* Function application. */
@@ -227,9 +206,23 @@ Expr evalExpr2(EvalState & state, Expr e)
         e1 = evalExpr(state, e1);
 
         /* Is it a primop or a function? */
-        if (atMatch(m, e1) >> "Var" >> name) {
-            PrimOp1 primOp = (PrimOp1) lookupPrimOp(state.primOps1, name);
-            if (primOp) return primOp(state, e2); else abort();
+        int arity;
+        ATerm fun;
+        ATermList args;
+        if (atMatch(m, e1) >> "PrimOp" >> arity >> fun >> args) {
+            args = ATinsert(args, e2);
+            if (ATgetLength(args) == arity) {
+                /* Put the arguments in a vector in reverse (i.e.,
+                   actual) order. */
+                ATermVector args2(arity);
+                for (ATermIterator i(args); i; ++i)
+                    args2[--arity] = *i;
+                return ((PrimOp) ATgetBlobData((ATermBlob) fun))
+                    (state, args2);
+            } else
+                /* Need more arguments, so propagate the primop. */
+                return ATmake("PrimOp(<int>, <term>, <list>)",
+                    arity, fun, args);
         }
 
         else if (atMatch(m, e1) >> "Function" >> formals >> e4 >> pos) {
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 34ac467f1d80..b51a5b07988c 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -12,16 +12,16 @@ typedef map<Path, PathSet> DrvPaths;
 typedef map<Path, Hash> DrvHashes;
 
 struct EvalState;
-typedef Expr (* PrimOp0) (EvalState &);
-typedef Expr (* PrimOp1) (EvalState &, Expr arg);
+
+/* Note: using a ATermVector is safe here, since when we call a primop
+   we also have an ATermList on the stack. */
+typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
 
 
 struct EvalState 
 {
     ATermMap normalForms;
-    ATermMap primOps0; /* nullary primops */
-    ATermMap primOps1; /* unary primops */
-    ATermMap primOpsAll;
+    ATermMap primOps;
     DrvPaths drvPaths;
     DrvHashes drvHashes; /* normalised derivation hashes */
     Expr blackHole;
@@ -31,8 +31,9 @@ struct EvalState
 
     EvalState();
 
-    void addPrimOp0(const string & name, PrimOp0 primOp);
-    void addPrimOp1(const string & name, PrimOp1 primOp);
+    void addPrimOps();
+    void addPrimOp(const string & name,
+        unsigned int arity, PrimOp primOp);
 };
 
 
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index ecd2534088f6..30145c87b344 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -47,6 +47,12 @@ private:
 };
 
 
+/* A STL vector of ATerms.  Should be used with great care since it's
+   stored on the heap, and the elements are therefore not roots to the
+   ATerm garbage collector. */
+typedef vector<ATerm> ATermVector;
+
+
 /* Convert a string to an ATerm (i.e., a quoted nullary function
    applicaton). */
 ATerm string2ATerm(const string & s);
diff --git a/src/libexpr/parser.cc b/src/libexpr/parser.cc
index 2e561c95ea29..90e008473048 100644
--- a/src/libexpr/parser.cc
+++ b/src/libexpr/parser.cc
@@ -95,7 +95,7 @@ static Expr parse(EvalState & state,
     if (res) throw Error(data.error);
 
     try {
-        checkVarDefs(state.primOpsAll, data.result);
+        checkVarDefs(state.primOps, data.result);
     } catch (Error & e) {
         throw Error(format("%1%, in `%2%'") % e.msg() % path);
     }
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index b0a0a276152f..e97c636b8194 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -1,13 +1,15 @@
-#include "primops.hh"
 #include "normalise.hh"
+#include "eval.hh"
 #include "globals.hh"
 
 
-Expr primImport(EvalState & state, Expr arg)
+/* Load and evaluate an expression from path specified by the
+   argument. */ 
+static Expr primImport(EvalState & state, const ATermVector & args)
 {
     ATMatcher m;
     string path;
-    if (!(atMatch(m, arg) >> "Path" >> path))
+    if (!(atMatch(m, args[0]) >> "Path" >> path))
         throw Error("path expected");
     return evalFile(state, path);
 }
@@ -158,11 +160,19 @@ static string concatStrings(const Strings & ss)
 }
 
 
-Expr primDerivation(EvalState & state, Expr args)
+/* Construct (as a unobservable side effect) a Nix derivation
+   expression that performs the derivation described by the argument
+   set.  Returns the original set extended with the following
+   attributes: `outPath' containing the primary output path of the
+   derivation; `drvPath' containing the path of the Nix expression;
+   and `type' set to `derivation' to indicate that this is a
+   derivation. */
+static Expr primDerivation(EvalState & state, const ATermVector & _args)
 {
     startNest(nest, lvlVomit, "evaluating derivation");
 
     ATermMap attrs;
+    Expr args = _args[0];
     args = evalExpr(state, args);
     queryAllAttrs(args, attrs, true);
 
@@ -251,16 +261,19 @@ Expr primDerivation(EvalState & state, Expr args)
 }
 
 
-Expr primBaseNameOf(EvalState & state, Expr arg)
+/* Return the base name of the given string, i.e., everything
+   following the last slash. */
+static Expr primBaseNameOf(EvalState & state, const ATermVector & args)
 {
-    string s = evalString(state, arg);
+    string s = evalString(state, args[0]);
     return ATmake("Str(<str>)", baseNameOf(s).c_str());
 }
 
 
-Expr primToString(EvalState & state, Expr arg)
+/* Convert the argument (which can be a path or a uri) to a string. */
+static Expr primToString(EvalState & state, const ATermVector & args)
 {
-    arg = evalExpr(state, arg);
+    Expr arg = evalExpr(state, args[0]);
     ATMatcher m;
     string s;
     if (atMatch(m, arg) >> "Str" >> s ||
@@ -271,27 +284,50 @@ Expr primToString(EvalState & state, Expr arg)
 }
 
 
-Expr primTrue(EvalState & state)
+/* Boolean constructors. */
+static Expr primTrue(EvalState & state, const ATermVector & args)
 {
     return ATmake("Bool(True)");
 }
 
 
-Expr primFalse(EvalState & state)
+static Expr primFalse(EvalState & state, const ATermVector & args)
 {
     return ATmake("Bool(False)");
 }
 
 
-Expr primNull(EvalState & state)
+/* Return the null value. */
+Expr primNull(EvalState & state, const ATermVector & args)
 {
     return ATmake("Null");
 }
 
 
-Expr primIsNull(EvalState & state, Expr arg)
+/* Determine whether the argument is the null value. */
+Expr primIsNull(EvalState & state, const ATermVector & args)
 {
-    arg = evalExpr(state, arg);
+    Expr arg = evalExpr(state, args[0]);
     ATMatcher m;
     return makeBool(atMatch(m, arg) >> "Null");
 }
+
+
+/* Apply a function to every element of a list. */
+Expr primMap(EvalState & state, Expr fun, Expr list)
+{
+}
+
+
+void EvalState::addPrimOps()
+{
+    addPrimOp("true", 0, primTrue);
+    addPrimOp("false", 0, primFalse);
+    addPrimOp("null", 0, primNull);
+
+    addPrimOp("import", 1, primImport);
+    addPrimOp("derivation", 1, primDerivation);
+    addPrimOp("baseNameOf", 1, primBaseNameOf);
+    addPrimOp("toString", 1, primToString);
+    addPrimOp("isNull", 1, primIsNull);
+}
diff --git a/src/libexpr/primops.hh b/src/libexpr/primops.hh
deleted file mode 100644
index 6b9722dac38e..000000000000
--- a/src/libexpr/primops.hh
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef __PRIMOPS_H
-#define __PRIMOPS_H
-
-#include "eval.hh"
-
-
-/* Load and evaluate an expression from path specified by the
-   argument. */ 
-Expr primImport(EvalState & state, Expr arg);
-
-/* Construct (as a unobservable side effect) a Nix derivation
-   expression that performs the derivation described by the argument
-   set.  Returns the original set extended with the following
-   attributes: `outPath' containing the primary output path of the
-   derivation; `drvPath' containing the path of the Nix expression;
-   and `type' set to `derivation' to indicate that this is a
-   derivation. */
-Expr primDerivation(EvalState & state, Expr args);
-
-/* Return the base name of the given string, i.e., everything
-   following the last slash. */
-Expr primBaseNameOf(EvalState & state, Expr arg);
-
-/* Convert the argument (which can be a path or a uri) to a string. */
-Expr primToString(EvalState & state, Expr arg);
-
-/* Boolean constructors. */
-Expr primTrue(EvalState & state);
-Expr primFalse(EvalState & state);
-
-/* Return the null value. */
-Expr primNull(EvalState & state);
-
-/* Determine whether the argument is the null value. */
-Expr primIsNull(EvalState & state, Expr arg);
-
-
-#endif /* !__PRIMOPS_H */