#include "eval.hh" #include "expr.hh" #include "parser.hh" #include "primops.hh" EvalState::EvalState() : normalForms(32768, 75) { blackHole = ATmake("BlackHole()"); if (!blackHole) throw Error("cannot build black hole"); nrEvaluated = nrCached = 0; } Expr getAttr(EvalState & state, Expr e, const string & name) { } /* Substitute an argument set into the body of a function. */ static Expr substArgs(Expr body, ATermList formals, Expr arg) { ATermMap subs; Expr undefined = ATmake("Undefined"); /* Get the formal arguments. */ while (!ATisEmpty(formals)) { ATerm t = ATgetFirst(formals); Expr name, def; debug(printTerm(t)); if (ATmatch(t, "NoDefFormal()", &name)) subs.set(name, undefined); else if (ATmatch(t, "DefFormal(, )", &name, &def)) subs.set(name, def); else abort(); /* can't happen */ formals = ATgetNext(formals); } /* Get the actual arguments, and check that they match with the formals. */ ATermMap args; queryAllAttrs(arg, args); for (ATermList keys = args.keys(); !ATisEmpty(keys); keys = ATgetNext(keys)) { Expr key = ATgetFirst(keys); Expr cur = subs.get(key); if (!cur) throw badTerm(format("function has no formal argument `%1%'") % aterm2String(key), arg); subs.set(key, args.get(key)); } /* Check that all arguments are defined. */ for (ATermList keys = subs.keys(); !ATisEmpty(keys); keys = ATgetNext(keys)) { Expr key = ATgetFirst(keys); if (subs.get(key) == undefined) throw badTerm(format("formal argument `%1%' missing") % aterm2String(key), arg); } return substitute(subs, body); } /* Transform a mutually recursive set into a non-recursive set. Each attribute is transformed into an expression that has all references to attributes substituted with selection expressions on the original set. E.g., e = `rec {x = f x y, y = x}' becomes `{x = f (e.x) (e.y), y = e.x}'. */ ATerm expandRec(ATerm e, ATermList bnds) { /* Create the substitution list. */ ATermMap subs; ATermList bs = bnds; while (!ATisEmpty(bs)) { char * s; Expr e2; if (!ATmatch(ATgetFirst(bs), "Bind(, )", &s, &e2)) abort(); /* can't happen */ subs.set(s, ATmake("Select(, )", e, s)); bs = ATgetNext(bs); } /* Create the non-recursive set. */ ATermMap as; bs = bnds; while (!ATisEmpty(bs)) { char * s; Expr e2; if (!ATmatch(ATgetFirst(bs), "Bind(, )", &s, &e2)) abort(); /* can't happen */ as.set(s, substitute(subs, e2)); bs = ATgetNext(bs); } return makeAttrs(as); } string evalString(EvalState & state, Expr e) { e = evalExpr(state, e); char * s; if (!ATmatch(e, "Str()", &s)) throw badTerm("string expected", e); return s; } Path evalPath(EvalState & state, Expr e) { e = evalExpr(state, e); char * s; if (!ATmatch(e, "Path()", &s)) throw badTerm("path expected", e); return s; } bool evalBool(EvalState & state, Expr e) { e = evalExpr(state, e); if (ATmatch(e, "Bool(True)")) return true; else if (ATmatch(e, "Bool(False)")) return false; else throw badTerm("expecting a boolean", e); } Expr evalExpr2(EvalState & state, Expr e) { Expr e1, e2, e3, e4; char * s1; /* Normal forms. */ if (ATmatch(e, "Str()", &s1) || ATmatch(e, "Path()", &s1) || ATmatch(e, "Uri()", &s1) || ATmatch(e, "Bool()", &e1) || ATmatch(e, "Function([], )", &e1, &e2) || ATmatch(e, "Attrs([])", &e1) || ATmatch(e, "List([])", &e1)) return e; /* Any encountered variables must be undeclared or primops. */ if (ATmatch(e, "Var()", &s1)) { return e; } /* Function application. */ if (ATmatch(e, "Call(, )", &e1, &e2)) { /* Evaluate the left-hand side. */ e1 = evalExpr(state, e1); /* Is it a primop or a function? */ if (ATmatch(e1, "Var()", &s1)) { string primop(s1); if (primop == "import") return primImport(state, e2); if (primop == "derivation") return primDerivation(state, e2); if (primop == "toString") return primToString(state, e2); if (primop == "baseNameOf") return primBaseNameOf(state, e2); else throw badTerm("undefined variable/primop", e1); } else if (ATmatch(e1, "Function([], )", &e3, &e4)) { return evalExpr(state, substArgs(e4, (ATermList) e3, evalExpr(state, e2))); } else throw badTerm("expecting a function or primop", e1); } /* Attribute selection. */ if (ATmatch(e, "Select(, )", &e1, &s1)) { string name(s1); Expr a = queryAttr(evalExpr(state, e1), name); if (!a) throw badTerm(format("missing attribute `%1%'") % name, e); return evalExpr(state, a); } /* Mutually recursive sets. */ ATermList bnds; if (ATmatch(e, "Rec([])", &bnds)) return expandRec(e, (ATermList) bnds); /* Let expressions `let {..., body = ...}' are just desugared into `(rec {..., body = ...}).body'. */ if (ATmatch(e, "LetRec()", &e1)) return evalExpr(state, ATmake("Select(Rec(), \"body\")", e1)); /* Conditionals. */ if (ATmatch(e, "If(, , )", &e1, &e2, &e3)) { if (evalBool(state, e1)) return evalExpr(state, e2); else return evalExpr(state, e3); } /* Equality. Just strings for now. */ if (ATmatch(e, "OpEq(, )", &e1, &e2)) { string s1 = evalString(state, e1); string s2 = evalString(state, e2); return s1 == s2 ? ATmake("Bool(True)") : ATmake("Bool(False)"); } /* Barf. */ throw badTerm("invalid expression", e); } Expr evalExpr(EvalState & state, Expr e) { Nest nest(lvlVomit, format("evaluating expression: %1%") % printTerm(e)); state.nrEvaluated++; /* Consult the memo table to quickly get the normal form of previously evaluated expressions. */ Expr nf = state.normalForms.get(e); if (nf) { if (nf == state.blackHole) throw badTerm("infinite recursion", e); state.nrCached++; return nf; } /* Otherwise, evaluate and memoize. */ state.normalForms.set(e, state.blackHole); nf = evalExpr2(state, e); state.normalForms.set(e, nf); return nf; } Expr evalFile(EvalState & state, const Path & path) { Nest nest(lvlTalkative, format("evaluating file `%1%'") % path); Expr e = parseExprFromFile(path); return evalExpr(state, e); } void printEvalStats(EvalState & state) { debug(format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency") % state.nrEvaluated % state.nrCached % ((float) state.nrCached / (float) state.nrEvaluated * 100)); }