From 0910ae95680011915211769577a4219384b695af Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Mar 2010 17:30:50 +0000 Subject: [PATCH] * Start of an evaluator that uses call-by-need (with thunk updating) instead of (memoised) call-by-name. --- src/libexpr/Makefile.am | 8 ++ src/libexpr/eval-test.cc | 191 +++++++++++++++++++++++++++++++++++++++ src/libexpr/parser.hh | 2 +- 3 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 src/libexpr/eval-test.cc diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am index e16600fbf1..c5f487769b 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 0000000000..c4e205682a --- /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 + +using namespace nix; + + +typedef struct Env_ * Env; +typedef struct Value_ * Value; + +typedef std::map 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 << ""; + 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 334822b5e0..3f430eb325 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. */