about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2010-03-23T17·30+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2010-03-23T17·30+0000
commit0910ae95680011915211769577a4219384b695af (patch)
tree6ac783010c72409723ba137e0086c3f42933a7aa /src
parent90039e0863469da7892830b122ccab017b29e2c8 (diff)
* Start of an evaluator that uses call-by-need (with thunk updating)
  instead of (memoised) call-by-name.

Diffstat (limited to 'src')
-rw-r--r--src/libexpr/Makefile.am8
-rw-r--r--src/libexpr/eval-test.cc191
-rw-r--r--src/libexpr/parser.hh2
3 files changed, 200 insertions, 1 deletions
diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am
index e16600fbf199..c5f487769b2c 100644
--- a/src/libexpr/Makefile.am
+++ b/src/libexpr/Makefile.am
@@ -49,3 +49,11 @@ nix.tbl: nix.sdf
 
 test.ast: test.nix nix.tbl
 	sglri -p nix.tbl -i test.nix -o test.ast
+
+
+# Testing.
+bin_PROGRAMS = eval-test
+
+eval_test_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \
+ ../libstore/libstore.la ../libutil/libutil.la \
+ ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@
diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc
new file mode 100644
index 000000000000..c4e205682a3a
--- /dev/null
+++ b/src/libexpr/eval-test.cc
@@ -0,0 +1,191 @@
+#include "nixexpr.hh"
+#include "parser.hh"
+#include "hash.hh"
+#include "util.hh"
+#include "nixexpr-ast.hh"
+
+#include <cstdlib>
+
+using namespace nix;
+
+
+typedef struct Env_ * Env;
+typedef struct Value_ * Value;
+
+typedef std::map<string, Value> Bindings;
+
+
+struct Env_
+{
+    Env up;
+    Bindings bindings;
+};
+
+
+typedef enum {
+    tInt = 1,
+    tAttrs,
+    tThunk
+} ValueType;
+
+
+struct Value_
+{
+    ValueType type;
+    union 
+    {
+        int integer;
+        Bindings * attrs;
+        struct {
+            Env env;
+            Expr expr;
+        } thunk;
+    };
+};
+
+
+std::ostream & operator << (std::ostream & str, Value_ & v)
+{
+    switch (v.type) {
+    case tInt:
+        str << v.integer;
+        break;
+    case tAttrs:
+        str << "{ ";
+        foreach (Bindings::iterator, i, *v.attrs) {
+            str << i->first << " = " << *i->second << "; ";
+        }
+        str << "}";
+        break;
+    case tThunk:
+        str << "<CODE>";
+        break;
+    default:
+        abort();
+    }
+    return str;
+}
+
+
+Value eval(Env env, Expr e);
+
+
+void forceValue(Value v)
+{
+    if (v->type != tThunk) return;
+    Value v2 = eval(v->thunk.env, v->thunk.expr);
+    *v = *v2; // !!! slightly inefficient
+}
+
+
+Value lookupVar(Env env, const string & name)
+{
+    for ( ; env; env = env->up) {
+        Value v = env->bindings[name];
+        if (v) return v;
+    }
+    throw Error("undefined variable");
+}
+
+
+Value eval(Env env, Expr e)
+{
+    printMsg(lvlError, format("eval: %1%") % e);
+
+    ATerm name;
+    if (matchVar(e, name)) {
+        Value v = lookupVar(env, aterm2String(name));
+        forceValue(v);
+        return v;
+    }
+
+    int n;
+    if (matchInt(e, n)) {
+        Value v = new Value_;
+        v->type = tInt;
+        v->integer = n;
+        return v;
+    }
+
+    ATermList es;
+    if (matchAttrs(e, es)) {
+        Value v = new Value_;
+        v->type = tAttrs;
+        v->attrs = new Bindings;
+        ATerm e2, pos;
+        for (ATermIterator i(es); i; ++i) {
+            if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
+            Value v2 = new Value_;
+            v2->type = tThunk;
+            v2->thunk.env = env;
+            v2->thunk.expr = e2;
+            (*v->attrs)[aterm2String(name)] = v2;
+        }
+        return v;
+    }
+
+    ATermList rbnds, nrbnds;
+    if (matchRec(e, rbnds, nrbnds)) {
+        Env env2 = new Env_;
+        env2->up = env;
+        
+        Value v = new Value_;
+        v->type = tAttrs;
+        v->attrs = &env2->bindings;
+        ATerm name, e2, pos;
+        for (ATermIterator i(rbnds); i; ++i) {
+            if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
+            Value v2 = new Value_;
+            v2->type = tThunk;
+            v2->thunk.env = env2;
+            v2->thunk.expr = e2;
+            env2->bindings[aterm2String(name)] = v2;
+        }
+        return v;
+    }
+
+    ATerm e2;
+    if (matchSelect(e, e2, name)) {
+        Value v = eval(env, e2);
+        if (v->type != tAttrs) throw TypeError("expected attribute set");
+        Value v2 = (*v->attrs)[aterm2String(name)];
+        if (!v2) throw TypeError("attribute not found");
+        forceValue(v2);
+        return v2;
+    }
+
+    abort();
+}
+
+
+void doTest(string s)
+{
+    EvalState state;
+    Expr e = parseExprFromString(state, s, "/");
+    printMsg(lvlError, format("%1%") % e);
+    Value v = eval(0, e);
+    printMsg(lvlError, format("result: %1%") % *v);
+}
+
+
+void run(Strings args)
+{
+    printMsg(lvlError, format("size of value: %1% bytes") % sizeof(Value_));
+    
+    doTest("123");
+    doTest("{ x = 1; y = 2; }");
+    doTest("{ x = 1; y = 2; }.y");
+    doTest("rec { x = 1; y = x; }.y");
+    //Expr e = parseExprFromString(state, "let x = \"a\"; in x + \"b\"", "/");
+    //Expr e = parseExprFromString(state, "(x: x + \"b\") \"a\"", "/");
+    //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/");
+    //Expr e = parseExprFromString(state, "\"a\" + \"b\"", "/");
+}
+
+
+void printHelp()
+{
+}
+
+
+string programId = "eval-test";
diff --git a/src/libexpr/parser.hh b/src/libexpr/parser.hh
index 334822b5e0e4..3f430eb32539 100644
--- a/src/libexpr/parser.hh
+++ b/src/libexpr/parser.hh
@@ -8,7 +8,7 @@ namespace nix {
 
 
 /* Parse a Nix expression from the specified file.  If `path' refers
-   to a directory, the "/default.nix" is appended. */
+   to a directory, then "/default.nix" is appended. */
 Expr parseExprFromFile(EvalState & state, Path path);
 
 /* Parse a Nix expression from the specified string. */