Compare commits
10 Commits
eval-cache
...
sharing-ha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a10da8466f | ||
|
|
e23d134b85 | ||
|
|
c3a79daaf3 | ||
|
|
74ce938e18 | ||
|
|
981afe821c | ||
|
|
cd9d10d4e3 | ||
|
|
c1179badd5 | ||
|
|
3d14ed9270 | ||
|
|
8e0488370d | ||
|
|
b57f8bd38d |
@@ -1,3 +1,5 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "parser.hh"
|
#include "parser.hh"
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
@@ -13,6 +15,19 @@
|
|||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
int cacheTerms;
|
||||||
|
|
||||||
|
bool shortCircuit;
|
||||||
|
bool closedTerms; // don't substitute under terms known to be closed
|
||||||
|
bool substCache; // memoization of the term substitution function
|
||||||
|
bool posInfo; // attach position info to functions, assertions, attributes
|
||||||
|
|
||||||
|
#define maxActiveCalls 4096
|
||||||
|
|
||||||
|
ATerm activeCalls[maxActiveCalls];
|
||||||
|
unsigned int activeCallsCount = 0;
|
||||||
|
|
||||||
|
|
||||||
EvalState::EvalState()
|
EvalState::EvalState()
|
||||||
@@ -23,6 +38,15 @@ EvalState::EvalState()
|
|||||||
initNixExprHelpers();
|
initNixExprHelpers();
|
||||||
|
|
||||||
addPrimOps();
|
addPrimOps();
|
||||||
|
|
||||||
|
if (!string2Int(getEnv("NIX_TERM_CACHE"), cacheTerms)) cacheTerms = 1;
|
||||||
|
shortCircuit = getEnv("NIX_SHORT_CIRCUIT", "0") == "1";
|
||||||
|
strictMode = getEnv("NIX_STRICT", "0") == "1";
|
||||||
|
closedTerms = getEnv("NIX_CLOSED_TERMS", "1") == "1";
|
||||||
|
substCache = getEnv("NIX_SUBST_CACHE", "1") == "1";
|
||||||
|
posInfo = getEnv("NIX_POS_INFO", "1") == "1";
|
||||||
|
|
||||||
|
ATprotectMemory(activeCalls, maxActiveCalls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -69,6 +93,19 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Expr speculativeEval(EvalState & state, Expr e)
|
||||||
|
{
|
||||||
|
if (!state.strictMode) return e;
|
||||||
|
try {
|
||||||
|
return evalExpr(state, e);
|
||||||
|
} catch (EvalError & err) {
|
||||||
|
/* ignore, pass the original arg and depend on
|
||||||
|
laziness */
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Substitute an argument set into the body of a function. */
|
/* Substitute an argument set into the body of a function. */
|
||||||
static Expr substArgs(EvalState & state,
|
static Expr substArgs(EvalState & state,
|
||||||
Expr body, ATermList formals, Expr arg)
|
Expr body, ATermList formals, Expr arg)
|
||||||
@@ -80,7 +117,7 @@ static Expr substArgs(EvalState & state,
|
|||||||
ATermMap args;
|
ATermMap args;
|
||||||
queryAllAttrs(arg, args);
|
queryAllAttrs(arg, args);
|
||||||
for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
|
for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
|
||||||
subs.set(i->key, i->value);
|
subs.set(i->key, speculativeEval(state, i->value));
|
||||||
|
|
||||||
/* Get the formal arguments. */
|
/* Get the formal arguments. */
|
||||||
ATermVector defsUsed;
|
ATermVector defsUsed;
|
||||||
@@ -389,7 +426,7 @@ Expr autoCallFunction(Expr e, const ATermMap & args)
|
|||||||
Expr name, def, value; ATerm values, def2;
|
Expr name, def, value; ATerm values, def2;
|
||||||
if (!matchFormal(*i, name, values, def2)) abort();
|
if (!matchFormal(*i, name, values, def2)) abort();
|
||||||
if ((value = args.get(name)))
|
if ((value = args.get(name)))
|
||||||
actualArgs.set(name, makeAttrRHS(value, makeNoPos()));
|
actualArgs.set(name, makeAttrRHS(allocCell(value), makeNoPos()));
|
||||||
else if (!matchDefaultValue(def2, def))
|
else if (!matchDefaultValue(def2, def))
|
||||||
throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')")
|
throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')")
|
||||||
% aterm2String(name));
|
% aterm2String(name));
|
||||||
@@ -457,7 +494,7 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
|
|||||||
else if (matchFunction(fun, formals, body, pos)) {
|
else if (matchFunction(fun, formals, body, pos)) {
|
||||||
arg = evalExpr(state, arg);
|
arg = evalExpr(state, arg);
|
||||||
try {
|
try {
|
||||||
return evalExpr(state, substArgs(state, body, formals, arg));
|
return evalExpr(state, substArgs(state, allocCells(body), formals, arg));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
||||||
showPos(pos));
|
showPos(pos));
|
||||||
@@ -467,9 +504,10 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
|
|||||||
|
|
||||||
else if (matchFunction1(fun, name, body, pos)) {
|
else if (matchFunction1(fun, name, body, pos)) {
|
||||||
try {
|
try {
|
||||||
|
arg = speculativeEval(state, arg);
|
||||||
ATermMap subs(1);
|
ATermMap subs(1);
|
||||||
subs.set(name, arg);
|
subs.set(name, allocCell(arg));
|
||||||
return evalExpr(state, substitute(Substitution(0, &subs), body));
|
return evalExpr(state, substitute(Substitution(0, &subs), allocCells(body)));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
||||||
showPos(pos));
|
showPos(pos));
|
||||||
@@ -624,6 +662,10 @@ Expr evalExpr2(EvalState & state, Expr e)
|
|||||||
|
|
||||||
Expr e1, e2, e3;
|
Expr e1, e2, e3;
|
||||||
ATerm name, pos;
|
ATerm name, pos;
|
||||||
|
|
||||||
|
int bla;
|
||||||
|
if (matchCell(e, bla, e1)) e = e1;
|
||||||
|
|
||||||
AFun sym = ATgetAFun(e);
|
AFun sym = ATgetAFun(e);
|
||||||
|
|
||||||
/* Normal forms. */
|
/* Normal forms. */
|
||||||
@@ -715,41 +757,141 @@ Expr evalExpr2(EvalState & state, Expr e)
|
|||||||
if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2);
|
if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2);
|
||||||
|
|
||||||
/* Barf. */
|
/* Barf. */
|
||||||
|
//printMsg(lvlError, format("%1%") % e);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ShortCircuit
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int fnord;
|
||||||
|
|
||||||
|
|
||||||
|
void maybeShortCircuit(EvalState & state, Expr e, Expr nf)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < activeCallsCount; ++i) {
|
||||||
|
Expr fun, arg;
|
||||||
|
if (!matchCall(activeCalls[i], fun, arg)) abort();
|
||||||
|
if (arg == e) {
|
||||||
|
//printMsg(lvlError, format("blaat"));
|
||||||
|
//printMsg(lvlError, format("blaat %1% %2% %3%") % fun % arg % e);
|
||||||
|
Expr res = state.normalForms.get(makeCall(fun, nf));
|
||||||
|
if (res) {
|
||||||
|
fnord++;
|
||||||
|
//printMsg(lvlError, format("blaat"));
|
||||||
|
throw ShortCircuit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr evalExpr(EvalState & state, Expr e)
|
Expr evalExpr(EvalState & state, Expr e)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
#if 0
|
#if 1
|
||||||
startNest(nest, lvlVomit,
|
startNest(nest, lvlVomit,
|
||||||
format("evaluating expression: %1%") % e);
|
format("evaluating expression: %1%") % e);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
state.nrEvaluated++;
|
state.nrEvaluated++;
|
||||||
|
|
||||||
|
if (cacheTerms == 0) return evalExpr2(state, e);
|
||||||
|
|
||||||
|
if (cacheTerms == 2) {
|
||||||
|
int pseudoAddr;
|
||||||
|
Expr e2;
|
||||||
|
if (!matchCell(e, pseudoAddr, e2)) return evalExpr2(state, e);
|
||||||
|
}
|
||||||
|
|
||||||
/* Consult the memo table to quickly get the normal form of
|
/* Consult the memo table to quickly get the normal form of
|
||||||
previously evaluated expressions. */
|
previously evaluated expressions. */
|
||||||
Expr nf = state.normalForms.get(e);
|
Expr nf = state.normalForms.get(e);
|
||||||
if (nf) {
|
if (nf) {
|
||||||
if (nf == makeBlackHole())
|
//if (nf == makeBlackHole())
|
||||||
throwEvalError("infinite recursion encountered");
|
// throwEvalError("infinite recursion encountered");
|
||||||
state.nrCached++;
|
state.nrCached++;
|
||||||
return nf;
|
return nf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise, evaluate and memoize. */
|
Expr fun, arg;
|
||||||
state.normalForms.set(e, makeBlackHole());
|
if (shortCircuit && matchCall(e, fun, arg)) {
|
||||||
try {
|
|
||||||
nf = evalExpr2(state, e);
|
#if 0
|
||||||
} catch (Error & err) {
|
Expr arg2 = state.normalForms.get(arg);
|
||||||
state.normalForms.remove(e);
|
if (arg2) { /* the evaluated argument is now known */
|
||||||
throw;
|
//printMsg(lvlError, "foo");
|
||||||
|
/* do we know the result of the same function called
|
||||||
|
with the evaluated argument? */
|
||||||
|
Expr res = state.normalForms.get(makeCall(fun, arg2));
|
||||||
|
if (res) { /* woohoo! */
|
||||||
|
printMsg(lvlError, "dingdong");
|
||||||
|
state.normalForms.set(e, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
assert(activeCallsCount < maxActiveCalls);
|
||||||
|
activeCalls[activeCallsCount++] = e;
|
||||||
|
|
||||||
|
//state.normalForms.set(e, makeBlackHole());
|
||||||
|
try {
|
||||||
|
nf = evalExpr2(state, e);
|
||||||
|
}
|
||||||
|
catch (ShortCircuit & exception) {
|
||||||
|
//printMsg(lvlError, "catch!");
|
||||||
|
Expr arg2 = state.normalForms.get(arg);
|
||||||
|
if (arg2) { /* the evaluated argument is now known */
|
||||||
|
/* do we know the result of the same function called
|
||||||
|
with the evaluated argument? */
|
||||||
|
Expr res = state.normalForms.get(makeCall(fun, arg2));
|
||||||
|
if (res) { /* woohoo! */
|
||||||
|
//printMsg(lvlError, "woohoo!");
|
||||||
|
//printMsg(lvlError, format("woohoo! %1% %2% %3% %4%") % fun % arg % arg2 % res);
|
||||||
|
activeCallsCount--;
|
||||||
|
state.normalForms.set(e, res);
|
||||||
|
maybeShortCircuit(state, e, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activeCallsCount--;
|
||||||
|
state.normalForms.remove(e);
|
||||||
|
throw; /* not for us */
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
activeCallsCount--;
|
||||||
|
state.normalForms.remove(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
activeCallsCount--;
|
||||||
|
state.normalForms.set(e, nf);
|
||||||
|
Expr arg2 = state.normalForms.get(arg);
|
||||||
|
if (arg2) state.normalForms.set(makeCall(fun, arg2), nf);
|
||||||
|
maybeShortCircuit(state, e, nf);
|
||||||
|
return nf;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
/* Otherwise, evaluate and memoize. */
|
||||||
|
//state.normalForms.set(e, makeBlackHole());
|
||||||
|
try {
|
||||||
|
nf = evalExpr2(state, e);
|
||||||
|
} catch (...) {
|
||||||
|
state.normalForms.remove(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
state.normalForms.set(e, nf);
|
||||||
|
if (shortCircuit) maybeShortCircuit(state, e, nf);
|
||||||
|
return nf;
|
||||||
|
|
||||||
}
|
}
|
||||||
state.normalForms.set(e, nf);
|
|
||||||
return nf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -845,16 +987,24 @@ extern "C" {
|
|||||||
unsigned long AT_calcAllocatedSize();
|
unsigned long AT_calcAllocatedSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int substs = 0;
|
||||||
|
unsigned int substsCached = 0;
|
||||||
|
|
||||||
|
|
||||||
void printEvalStats(EvalState & state)
|
void printEvalStats(EvalState & state)
|
||||||
{
|
{
|
||||||
char x;
|
char x;
|
||||||
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
|
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
|
||||||
|
printMsg(lvlError, format("FNORD %1%") % fnord);
|
||||||
printMsg(showStats ? lvlInfo : lvlDebug,
|
printMsg(showStats ? lvlInfo : lvlDebug,
|
||||||
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space")
|
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space, %6% substitutions (%7% cached)")
|
||||||
% state.nrEvaluated % state.nrCached
|
% state.nrEvaluated % state.nrCached
|
||||||
% ((float) state.nrCached / (float) state.nrEvaluated * 100)
|
% ((float) state.nrCached / (float) state.nrEvaluated * 100)
|
||||||
% AT_calcAllocatedSize()
|
% AT_calcAllocatedSize()
|
||||||
% (&x - deepestStack));
|
% (&x - deepestStack)
|
||||||
|
% substs
|
||||||
|
% substsCached);
|
||||||
if (showStats)
|
if (showStats)
|
||||||
printATermMapStats();
|
printATermMapStats();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ struct EvalState;
|
|||||||
typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
|
typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
|
||||||
|
|
||||||
|
|
||||||
|
extern int cacheTerms; // 0 = don't, 1 = do, 2 = "cell" terms only
|
||||||
|
|
||||||
|
|
||||||
struct EvalState
|
struct EvalState
|
||||||
{
|
{
|
||||||
ATermMap normalForms;
|
ATermMap normalForms;
|
||||||
@@ -38,6 +41,10 @@ struct EvalState
|
|||||||
unsigned int nrEvaluated;
|
unsigned int nrEvaluated;
|
||||||
unsigned int nrCached;
|
unsigned int nrCached;
|
||||||
|
|
||||||
|
bool strictMode;
|
||||||
|
|
||||||
|
ATermMap parsings; /* path -> expr mapping */
|
||||||
|
|
||||||
EvalState();
|
EvalState();
|
||||||
|
|
||||||
void addPrimOps();
|
void addPrimOps();
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ Inherit | Expr ATermList Pos | ATerm |
|
|||||||
|
|
||||||
Scope | | Expr |
|
Scope | | Expr |
|
||||||
|
|
||||||
|
Cell | int Expr | Expr |
|
||||||
|
|
||||||
Formal | string ValidValues DefaultValue | ATerm |
|
Formal | string ValidValues DefaultValue | ATerm |
|
||||||
|
|
||||||
ValidValues | ATermList | ValidValues |
|
ValidValues | ATermList | ValidValues |
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "aterm.hh"
|
#include "aterm.hh"
|
||||||
|
#include "eval.hh" // !!! urgh
|
||||||
|
|
||||||
#include "nixexpr-ast.hh"
|
#include "nixexpr-ast.hh"
|
||||||
#include "nixexpr-ast.cc"
|
#include "nixexpr-ast.cc"
|
||||||
@@ -108,7 +109,16 @@ Expr makeAttrs(const ATermMap & attrs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr substitute(const Substitution & subs, Expr e)
|
extern unsigned int substs;
|
||||||
|
extern unsigned int substsCached;
|
||||||
|
extern bool closedTerms;
|
||||||
|
extern bool substCache;
|
||||||
|
|
||||||
|
|
||||||
|
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e);
|
||||||
|
|
||||||
|
|
||||||
|
static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
@@ -116,19 +126,20 @@ Expr substitute(const Substitution & subs, Expr e)
|
|||||||
|
|
||||||
ATerm name, pos, e2;
|
ATerm name, pos, e2;
|
||||||
|
|
||||||
|
substs++;
|
||||||
|
|
||||||
/* As an optimisation, don't substitute in subterms known to be
|
/* As an optimisation, don't substitute in subterms known to be
|
||||||
closed. */
|
closed. */
|
||||||
if (matchClosed(e, e2)) return e;
|
if (closedTerms && matchClosed(e, e2)) return e;
|
||||||
|
|
||||||
if (matchVar(e, name)) {
|
if (matchVar(e, name)) {
|
||||||
Expr sub = subs.lookup(name);
|
Expr sub = subs.lookup(name);
|
||||||
if (sub == makeRemoved()) sub = 0;
|
|
||||||
Expr wrapped;
|
Expr wrapped;
|
||||||
/* Add a "closed" wrapper around terms that aren't already
|
/* Add a "closed" wrapper around terms that aren't already
|
||||||
closed. The check is necessary to prevent repeated
|
closed. The check is necessary to prevent repeated
|
||||||
wrapping, e.g., closed(closed(closed(...))), which kills
|
wrapping, e.g., closed(closed(closed(...))), which kills
|
||||||
caching. */
|
caching. */
|
||||||
return sub ? (matchClosed(sub, wrapped) ? sub : makeClosed(sub)) : e;
|
return sub ? ((!closedTerms || matchClosed(sub, wrapped)) ? sub : makeClosed(sub)) : e;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In case of a function, filter out all variables bound by this
|
/* In case of a function, filter out all variables bound by this
|
||||||
@@ -140,18 +151,30 @@ Expr substitute(const Substitution & subs, Expr e)
|
|||||||
for (ATermIterator i(formals); i; ++i) {
|
for (ATermIterator i(formals); i; ++i) {
|
||||||
ATerm d1, d2;
|
ATerm d1, d2;
|
||||||
if (!matchFormal(*i, name, d1, d2)) abort();
|
if (!matchFormal(*i, name, d1, d2)) abort();
|
||||||
map.set(name, makeRemoved());
|
if (subs.lookup(name))
|
||||||
|
map.set(name, constRemoved);
|
||||||
|
}
|
||||||
|
if (map.size() == 0)
|
||||||
|
return makeFunction(
|
||||||
|
(ATermList) substitute(done, subs, (ATerm) formals),
|
||||||
|
substitute(done, subs, body), pos);
|
||||||
|
else {
|
||||||
|
Substitution subs2(&subs, &map);
|
||||||
|
ATermMap done2(128);
|
||||||
|
return makeFunction(
|
||||||
|
(ATermList) substitute(done2, subs2, (ATerm) formals),
|
||||||
|
substitute(done2, subs2, body), pos);
|
||||||
}
|
}
|
||||||
Substitution subs2(&subs, &map);
|
|
||||||
return makeFunction(
|
|
||||||
(ATermList) substitute(subs2, (ATerm) formals),
|
|
||||||
substitute(subs2, body), pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchFunction1(e, name, body, pos)) {
|
if (matchFunction1(e, name, body, pos)) {
|
||||||
ATermMap map(1);
|
if (subs.lookup(name)) {
|
||||||
map.set(name, makeRemoved());
|
ATermMap map(1);
|
||||||
return makeFunction1(name, substitute(Substitution(&subs, &map), body), pos);
|
map.set(name, constRemoved);
|
||||||
|
ATermMap done2(128);
|
||||||
|
return makeFunction1(name, substitute(done2, Substitution(&subs, &map), body), pos);
|
||||||
|
} else
|
||||||
|
return makeFunction1(name, substitute(done, subs, body), pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Idem for a mutually recursive attribute set. */
|
/* Idem for a mutually recursive attribute set. */
|
||||||
@@ -159,14 +182,21 @@ Expr substitute(const Substitution & subs, Expr e)
|
|||||||
if (matchRec(e, rbnds, nrbnds)) {
|
if (matchRec(e, rbnds, nrbnds)) {
|
||||||
ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
|
ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
|
||||||
for (ATermIterator i(rbnds); i; ++i)
|
for (ATermIterator i(rbnds); i; ++i)
|
||||||
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
|
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
|
||||||
else abort(); /* can't happen */
|
map.set(name, constRemoved);
|
||||||
for (ATermIterator i(nrbnds); i; ++i)
|
for (ATermIterator i(nrbnds); i; ++i)
|
||||||
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
|
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
|
||||||
else abort(); /* can't happen */
|
map.set(name, constRemoved);
|
||||||
return makeRec(
|
if (map.size() == 0)
|
||||||
(ATermList) substitute(Substitution(&subs, &map), (ATerm) rbnds),
|
return makeRec(
|
||||||
(ATermList) substitute(subs, (ATerm) nrbnds));
|
(ATermList) substitute(done, subs, (ATerm) rbnds),
|
||||||
|
(ATermList) substitute(done, subs, (ATerm) nrbnds));
|
||||||
|
else {
|
||||||
|
ATermMap done2(128);
|
||||||
|
return makeRec(
|
||||||
|
(ATermList) substitute(done2, Substitution(&subs, &map), (ATerm) rbnds),
|
||||||
|
(ATermList) substitute(done, subs, (ATerm) nrbnds));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ATgetType(e) == AT_APPL) {
|
if (ATgetType(e) == AT_APPL) {
|
||||||
@@ -177,7 +207,73 @@ Expr substitute(const Substitution & subs, Expr e)
|
|||||||
|
|
||||||
for (int i = 0; i < arity; ++i) {
|
for (int i = 0; i < arity; ++i) {
|
||||||
ATerm arg = ATgetArgument(e, i);
|
ATerm arg = ATgetArgument(e, i);
|
||||||
args[i] = substitute(subs, arg);
|
args[i] = substitute(done, subs, arg);
|
||||||
|
if (args[i] != arg) changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed ? (ATerm) ATmakeApplArray(fun, args) : e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ATgetType(e) == AT_LIST) {
|
||||||
|
unsigned int len = ATgetLength((ATermList) e);
|
||||||
|
ATerm es[len];
|
||||||
|
ATermIterator i((ATermList) e);
|
||||||
|
bool changed = false;
|
||||||
|
for (unsigned int j = 0; i; ++i, ++j) {
|
||||||
|
es[j] = substitute(done, subs, *i);
|
||||||
|
if (es[j] != *i) changed = true;
|
||||||
|
}
|
||||||
|
if (!changed) return e;
|
||||||
|
ATermList out = ATempty;
|
||||||
|
for (unsigned int j = len; j; --j)
|
||||||
|
out = ATinsert(out, es[j - 1]);
|
||||||
|
return (ATerm) out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e)
|
||||||
|
{
|
||||||
|
Expr res = done[e];
|
||||||
|
if (substCache && res) {
|
||||||
|
substsCached++;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
res = substitute2(done, subs, e);
|
||||||
|
done.set(e, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Expr substitute(const Substitution & subs, Expr e)
|
||||||
|
{
|
||||||
|
ATermMap done(256);
|
||||||
|
return substitute(done, subs, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Expr allocCells(Expr e)
|
||||||
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
|
ATerm e2;
|
||||||
|
if (matchClosed(e, e2)) return e;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
if (matchCell(e, i, e2))
|
||||||
|
return allocCell(allocCells(e2));
|
||||||
|
|
||||||
|
if (ATgetType(e) == AT_APPL) {
|
||||||
|
AFun fun = ATgetAFun(e);
|
||||||
|
int arity = ATgetArity(fun);
|
||||||
|
ATerm args[arity];
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < arity; ++i) {
|
||||||
|
ATerm arg = ATgetArgument(e, i);
|
||||||
|
args[i] = allocCells(arg);
|
||||||
if (args[i] != arg) changed = true;
|
if (args[i] != arg) changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +285,7 @@ Expr substitute(const Substitution & subs, Expr e)
|
|||||||
ATerm es[len];
|
ATerm es[len];
|
||||||
ATermIterator i((ATermList) e);
|
ATermIterator i((ATermList) e);
|
||||||
for (unsigned int j = 0; i; ++i, ++j)
|
for (unsigned int j = 0; i; ++i, ++j)
|
||||||
es[j] = substitute(subs, *i);
|
es[j] = allocCells(*i);
|
||||||
ATermList out = ATempty;
|
ATermList out = ATempty;
|
||||||
for (unsigned int j = len; j; --j)
|
for (unsigned int j = len; j; --j)
|
||||||
out = ATinsert(out, es[j - 1]);
|
out = ATinsert(out, es[j - 1]);
|
||||||
@@ -399,5 +495,18 @@ string showValue(Expr e)
|
|||||||
return "<unknown>";
|
return "<unknown>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned int cellCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
Expr allocCell(Expr e)
|
||||||
|
{
|
||||||
|
if (cacheTerms != 2) return e;
|
||||||
|
int i;
|
||||||
|
Expr e2;
|
||||||
|
if (matchCell(e, i, e2)) return e;
|
||||||
|
return makeCell(cellCount++, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ typedef ATerm Pos;
|
|||||||
typedef vector<ATerm> ATermVector;
|
typedef vector<ATerm> ATermVector;
|
||||||
|
|
||||||
|
|
||||||
|
extern Expr constRemoved;
|
||||||
|
|
||||||
|
|
||||||
/* A substitution is a linked list of ATermMaps that map names to
|
/* A substitution is a linked list of ATermMaps that map names to
|
||||||
identifiers. We use a list of ATermMaps rather than a single to
|
identifiers. We use a list of ATermMaps rather than a single to
|
||||||
make it easy to grow or shrink a substitution when entering a
|
make it easy to grow or shrink a substitution when entering a
|
||||||
@@ -53,7 +56,8 @@ struct Substitution
|
|||||||
{
|
{
|
||||||
Expr x;
|
Expr x;
|
||||||
for (const Substitution * s(this); s; s = s->prev)
|
for (const Substitution * s(this); s; s = s->prev)
|
||||||
if ((x = s->map->get(name))) return x;
|
if ((x = s->map->get(name)))
|
||||||
|
return x == constRemoved ? 0 : x;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -116,6 +120,11 @@ string showType(Expr e);
|
|||||||
|
|
||||||
string showValue(Expr e);
|
string showValue(Expr e);
|
||||||
|
|
||||||
|
|
||||||
|
Expr allocCell(Expr e); // make an updateable cell (for simulating conventional laziness)
|
||||||
|
|
||||||
|
Expr allocCells(Expr e); // re-allocate all cells in e
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ static Expr fixAttrs(int recursive, ATermList as)
|
|||||||
bool fromScope = matchScope(src);
|
bool fromScope = matchScope(src);
|
||||||
for (ATermIterator j(names); j; ++j) {
|
for (ATermIterator j(names); j; ++j) {
|
||||||
Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j);
|
Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j);
|
||||||
*is = ATinsert(*is, makeBind(*j, rhs, pos));
|
*is = ATinsert(*is, makeBind(*j, allocCell(rhs), pos));
|
||||||
}
|
}
|
||||||
} else bs = ATinsert(bs, *i);
|
} else bs = ATinsert(bs, *i);
|
||||||
}
|
}
|
||||||
@@ -70,11 +70,13 @@ static Expr fixAttrs(int recursive, ATermList as)
|
|||||||
|
|
||||||
void backToString(yyscan_t scanner);
|
void backToString(yyscan_t scanner);
|
||||||
|
|
||||||
|
|
||||||
|
extern bool posInfo;
|
||||||
|
|
||||||
static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
|
static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
|
||||||
{
|
{
|
||||||
return makePos(toATerm(data->path),
|
return posInfo ? makePos(toATerm(data->path),
|
||||||
loc->first_line, loc->first_column);
|
loc->first_line, loc->first_column) : makeNoPos();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CUR_POS makeCurPos(yylocp, data)
|
#define CUR_POS makeCurPos(yylocp, data)
|
||||||
@@ -226,7 +228,7 @@ binds
|
|||||||
|
|
||||||
bind
|
bind
|
||||||
: ID '=' expr ';'
|
: ID '=' expr ';'
|
||||||
{ $$ = makeBind($1, $3, CUR_POS); }
|
{ $$ = makeBind($1, allocCell($3), CUR_POS); }
|
||||||
| INHERIT inheritsrc ids ';'
|
| INHERIT inheritsrc ids ';'
|
||||||
{ $$ = makeInherit($2, $3, CUR_POS); }
|
{ $$ = makeInherit($2, $3, CUR_POS); }
|
||||||
;
|
;
|
||||||
@@ -383,8 +385,13 @@ Expr parseExprFromFile(EvalState & state, Path path)
|
|||||||
if (S_ISDIR(st.st_mode))
|
if (S_ISDIR(st.st_mode))
|
||||||
path = canonPath(path + "/default.nix");
|
path = canonPath(path + "/default.nix");
|
||||||
|
|
||||||
|
Expr cached = state.parsings.get(toATerm(path));
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
/* Read and parse the input file. */
|
/* Read and parse the input file. */
|
||||||
return parse(state, readFile(path).c_str(), path, dirOf(path));
|
cached = parse(state, readFile(path).c_str(), path, dirOf(path));
|
||||||
|
state.parsings.set(toATerm(path), cached);
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -564,7 +564,7 @@ static Expr prim_derivationLazy(EvalState & state, const ATermVector & args)
|
|||||||
attrs.set(toATerm("type"),
|
attrs.set(toATerm("type"),
|
||||||
makeAttrRHS(makeStr("derivation"), makeNoPos()));
|
makeAttrRHS(makeStr("derivation"), makeNoPos()));
|
||||||
|
|
||||||
Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs);
|
Expr drvStrict = allocCell(makeCall(makeVar(toATerm("derivation!")), eAttrs));
|
||||||
|
|
||||||
attrs.set(toATerm("outPath"),
|
attrs.set(toATerm("outPath"),
|
||||||
makeAttrRHS(makeSelect(drvStrict, toATerm("outPath")), makeNoPos()));
|
makeAttrRHS(makeSelect(drvStrict, toATerm("outPath")), makeNoPos()));
|
||||||
@@ -773,7 +773,7 @@ static Expr prim_listToAttrs(EvalState & state, const ATermVector & args)
|
|||||||
Expr e = evalExpr(state, makeSelect(evaledExpr, toATerm("name")));
|
Expr e = evalExpr(state, makeSelect(evaledExpr, toATerm("name")));
|
||||||
string attr = evalStringNoCtx(state,e);
|
string attr = evalStringNoCtx(state,e);
|
||||||
Expr r = makeSelect(evaledExpr, toATerm("value"));
|
Expr r = makeSelect(evaledExpr, toATerm("value"));
|
||||||
res.set(toATerm(attr), makeAttrRHS(r, makeNoPos()));
|
res.set(toATerm(attr), makeAttrRHS(allocCell(r), makeNoPos()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw TypeError(format("list element in `listToAttrs' is %s, expected a set { name = \"<name>\"; value = <value>; }")
|
throw TypeError(format("list element in `listToAttrs' is %s, expected a set { name = \"<name>\"; value = <value>; }")
|
||||||
@@ -927,6 +927,17 @@ static Expr prim_stringLength(EvalState & state, const ATermVector & args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************
|
||||||
|
* Strictness
|
||||||
|
*************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
static Expr prim_strict(EvalState & state, const ATermVector & args)
|
||||||
|
{
|
||||||
|
return evalExpr(state, makeCall(args[0], evalExpr(state, args[1])));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*************************************************************
|
/*************************************************************
|
||||||
* Primop registration
|
* Primop registration
|
||||||
*************************************************************/
|
*************************************************************/
|
||||||
@@ -993,6 +1004,9 @@ void EvalState::addPrimOps()
|
|||||||
addPrimOp("toString", 1, prim_toString);
|
addPrimOp("toString", 1, prim_toString);
|
||||||
addPrimOp("__substring", 3, prim_substring);
|
addPrimOp("__substring", 3, prim_substring);
|
||||||
addPrimOp("__stringLength", 1, prim_stringLength);
|
addPrimOp("__stringLength", 1, prim_stringLength);
|
||||||
|
|
||||||
|
// Strictness
|
||||||
|
addPrimOp("strict", 2, prim_strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user