* Remove a lot of dead code.

This commit is contained in:
Eelco Dolstra 2010-04-08 11:41:19 +00:00
parent 7e048eddf5
commit b7b3dd55f9
4 changed files with 10 additions and 763 deletions

View File

@ -27,7 +27,14 @@ std::ostream & operator << (std::ostream & str, Value & v)
str << (v.boolean ? "true" : "false");
break;
case tString:
str << "\"" << v.string.s << "\""; // !!! escaping
str << "\"";
for (const char * i = v.string.s; *i; i++)
if (*i == '\"' || *i == '\\') str << "\\" << *i;
else if (*i == '\n') str << "\\n";
else if (*i == '\r') str << "\\r";
else if (*i == '\t') str << "\\t";
else str << *i;
str << "\"";
break;
case tPath:
str << v.path; // !!! escaping?
@ -930,497 +937,6 @@ bool EvalState::eqValues(Value & v1, Value & v2)
}
#if 0
/* Pattern-match `pat' against `arg'. The result is a set of
substitutions (`subs') and a set of recursive substitutions
(`subsRecursive'). The latter can refer to the variables bound by
both `subs' and `subsRecursive'. */
static void patternMatch(EvalState & state,
Pattern pat, Expr arg, ATermMap & subs, ATermMap & subsRecursive)
{
ATerm name;
ATermList formals;
ATermBool ellipsis;
if (matchVarPat(pat, name))
subs.set(name, arg);
else if (matchAttrsPat(pat, formals, ellipsis, name)) {
arg = evalExpr(state, arg);
if (name != sNoAlias) subs.set(name, arg);
/* Get the actual arguments. */
ATermMap attrs;
queryAllAttrs(arg, attrs);
unsigned int nrAttrs = attrs.size();
/* For each formal argument, get the actual argument. If
there is no matching actual argument but the formal
argument has a default, use the default. */
unsigned int attrsUsed = 0;
for (ATermIterator i(formals); i; ++i) {
Expr name, def;
DefaultValue def2;
if (!matchFormal(*i, name, def2)) abort(); /* can't happen */
Expr value = attrs[name];
if (value == 0) {
if (!matchDefaultValue(def2, def)) def = 0;
if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
% aterm2String(name));
subsRecursive.set(name, def);
} else {
attrsUsed++;
attrs.remove(name);
subs.set(name, value);
}
}
/* Check that each actual argument is listed as a formal
argument (unless the attribute match specifies a `...'). */
if (ellipsis == eFalse && attrsUsed != nrAttrs)
throw TypeError(format("the function does not expect an argument named `%1%'")
% aterm2String(attrs.begin()->key));
}
else abort();
}
/* Substitute an argument set into the body of a function. */
static Expr substArgs(EvalState & state,
Expr body, Pattern pat, Expr arg)
{
ATermMap subs(16), subsRecursive(16);
patternMatch(state, pat, arg, subs, subsRecursive);
/* If we used any default values, make a recursive attribute set
out of the (argument-name, value) tuples. This is so that we
can support default values that refer to each other, e.g. ({x,
y ? x + x}: y) {x = "foo";} evaluates to "foofoo". */
if (subsRecursive.size() != 0) {
ATermList recAttrs = ATempty;
foreach (ATermMap::const_iterator, i, subs)
recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
foreach (ATermMap::const_iterator, i, subsRecursive)
recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
Expr rec = makeRec(recAttrs, ATempty);
foreach (ATermMap::const_iterator, i, subsRecursive)
subs.set(i->key, makeSelect(rec, i->key));
}
return substitute(Substitution(0, &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;}'. */
LocalNoInline(ATerm expandRec(EvalState & state, ATerm e, ATermList rbnds, ATermList nrbnds))
{
ATerm name;
Expr e2;
Pos pos;
Expr eOverrides = 0;
/* Create the substitution list. */
ATermMap subs(ATgetLength(rbnds) + ATgetLength(nrbnds));
for (ATermIterator i(rbnds); i; ++i) {
if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
subs.set(name, makeSelect(e, name));
}
for (ATermIterator i(nrbnds); i; ++i) {
if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
if (name == sOverrides) eOverrides = e2;
subs.set(name, e2);
}
/* If the rec contains an attribute called `__overrides', then
evaluate it, and add the attributes in that set to the rec.
This allows overriding of recursive attributes, which is
otherwise not possible. (You can use the // operator to
replace an attribute, but other attributes in the rec will
still reference the original value, because that value has been
substituted into the bodies of the other attributes. Hence we
need __overrides.) */
ATermMap overrides;
if (eOverrides) {
eOverrides = evalExpr(state, eOverrides);
queryAllAttrs(eOverrides, overrides, false);
foreach (ATermMap::const_iterator, i, overrides)
subs.set(i->key, i->value);
}
Substitution subs_(0, &subs);
/* Create the non-recursive set. */
ATermMap as(ATgetLength(rbnds) + ATgetLength(nrbnds));
for (ATermIterator i(rbnds); i; ++i) {
if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
as.set(name, makeAttrRHS(substitute(subs_, e2), pos));
}
if (eOverrides)
foreach (ATermMap::const_iterator, i, overrides)
as.set(i->key, makeAttrRHS(i->value, makeNoPos()));
/* Copy the non-recursive bindings. !!! inefficient */
for (ATermIterator i(nrbnds); i; ++i) {
if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
as.set(name, makeAttrRHS(e2, pos));
}
return makeAttrs(as);
}
static void flattenList(EvalState & state, Expr e, ATermList & result)
{
ATermList es;
e = evalExpr(state, e);
if (matchList(e, es))
for (ATermIterator i(es); i; ++i)
flattenList(state, *i, result);
else
result = ATinsert(result, e);
}
ATermList flattenList(EvalState & state, Expr e)
{
ATermList result = ATempty;
flattenList(state, e, result);
return ATreverse(result);
}
/* Evaluation of various language constructs. These have been taken
out of evalExpr2 to reduce stack space usage. (GCC is really dumb
about stack space: it just adds up all the local variables and
temporaries of every scope into one huge stack frame. This is
really bad for deeply recursive functions.) */
LocalNoInline(Expr evalVar(EvalState & state, ATerm name))
{
ATerm primOp = state.primOps.get(name);
if (!primOp)
throw EvalError(format("impossible: undefined variable `%1%'") % aterm2String(name));
int arity;
ATermBlob fun;
if (!matchPrimOpDef(primOp, arity, fun)) abort();
if (arity == 0)
/* !!! backtrace for primop call */
return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector());
else
return makePrimOp(arity, fun, ATempty);
}
LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
{
Pattern pat;
ATerm pos;
Expr body;
/* Evaluate the left-hand side. */
fun = evalExpr(state, fun);
/* Is it a primop or a function? */
int arity;
ATermBlob funBlob;
ATermList args;
if (matchPrimOp(fun, arity, funBlob, args)) {
args = ATinsert(args, arg);
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;
/* !!! backtrace for primop call */
return ((PrimOp) ATgetBlobData(funBlob))
(state, args2);
} else
/* Need more arguments, so propagate the primop. */
return makePrimOp(arity, funBlob, args);
}
else if (matchFunction(fun, pat, body, pos)) {
try {
return evalExpr(state, substArgs(state, body, pat, arg));
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the function at %1%:\n",
showPos(pos));
throw;
}
}
else throwTypeError(
"attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
showType(fun));
}
LocalNoInline(Expr evalWith(EvalState & state, Expr defs, Expr body, ATerm pos))
{
ATermMap attrs;
try {
defs = evalExpr(state, defs);
queryAllAttrs(defs, attrs);
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the `with' definitions at %1%:\n",
showPos(pos));
throw;
}
try {
body = substitute(Substitution(0, &attrs), body);
checkVarDefs(state.primOps, body);
return evalExpr(state, body);
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the `with' body at %1%:\n",
showPos(pos));
throw;
}
}
/* Implementation of the `==' and `!=' operators. */
LocalNoInline(bool areEqual(EvalState & state, Expr e1, Expr e2))
{
e1 = evalExpr(state, e1);
e2 = evalExpr(state, e2);
/* We cannot test functions/primops for equality, and we currently
don't support testing equality between attribute sets or lists
- that would have to be a deep equality test to be sound. */
AFun sym1 = ATgetAFun(e1);
AFun sym2 = ATgetAFun(e2);
if (sym1 != sym2) return false;
/* Functions are incomparable. */
if (sym1 == symFunction || sym1 == symPrimOp) return false;
if (!state.allowUnsafeEquality && sym1 == symAttrs)
throw EvalError("comparison of attribute sets is not implemented");
/* !!! This allows comparisons of infinite data structures to
succeed, such as `let x = [x]; in x == x'. This is
undesirable, since equivalent (?) terms such as `let x = [x]; y
= [y]; in x == y' don't terminate. */
if (e1 == e2) return true;
if (sym1 == symList) {
ATermList es1; matchList(e1, es1);
ATermList es2; matchList(e2, es2);
if (ATgetLength(es1) != ATgetLength(es2)) return false;
ATermIterator i(es1), j(es2);
while (*i) {
if (!areEqual(state, *i, *j)) return false;
++i; ++j;
}
return true;
}
return false;
}
Expr evalExpr2(EvalState & state, Expr e)
{
/* When changing this function, make sure that you don't cause a
(large) increase in stack consumption! */
char x;
if (&x < deepestStack) deepestStack = &x;
Expr e1, e2, e3;
ATerm name, pos;
AFun sym = ATgetAFun(e);
/* Normal forms. */
if (sym == symStr ||
sym == symPath ||
sym == symNull ||
sym == symInt ||
sym == symBool ||
sym == symFunction ||
sym == symAttrs ||
sym == symList ||
sym == symPrimOp)
return e;
/* The `Closed' constructor is just a way to prevent substitutions
into expressions not containing free variables. */
if (matchClosed(e, e1))
return evalExpr(state, e1);
/* Any encountered variables must be primops (since undefined
variables are detected after parsing). */
if (matchVar(e, name)) return evalVar(state, name);
/* Function application. */
if (matchCall(e, e1, e2)) return evalCall(state, e1, e2);
/* Attribute selection. */
if (matchSelect(e, e1, name)) return evalSelect(state, e1, name);
/* Mutually recursive sets. */
ATermList rbnds, nrbnds;
if (matchRec(e, rbnds, nrbnds))
return expandRec(state, e, rbnds, nrbnds);
/* Conditionals. */
if (matchIf(e, e1, e2, e3))
return evalExpr(state, evalBool(state, e1) ? e2 : e3);
/* Assertions. */
if (matchAssert(e, e1, e2, pos)) return evalAssert(state, e1, e2, pos);
/* Withs. */
if (matchWith(e, e1, e2, pos)) return evalWith(state, e1, e2, pos);
/* Generic equality/inequality. Note that the behaviour on
composite data (lists, attribute sets) and functions is
undefined, since the subterms of those terms are not evaluated.
However, we don't want to make (==) strict, because that would
make operations like `big_derivation == null' very slow (unless
we were to evaluate them side-by-side). */
if (matchOpEq(e, e1, e2)) return makeBool(areEqual(state, e1, e2));
if (matchOpNEq(e, e1, e2)) return makeBool(!areEqual(state, e1, e2));
/* Negation. */
if (matchOpNot(e, e1))
return makeBool(!evalBool(state, e1));
/* Implication. */
if (matchOpImpl(e, e1, e2))
return makeBool(!evalBool(state, e1) || evalBool(state, e2));
/* Conjunction (logical AND). */
if (matchOpAnd(e, e1, e2))
return makeBool(evalBool(state, e1) && evalBool(state, e2));
/* Disjunction (logical OR). */
if (matchOpOr(e, e1, e2))
return makeBool(evalBool(state, e1) || evalBool(state, e2));
/* Attribute set update (//). */
if (matchOpUpdate(e, e1, e2))
return updateAttrs(evalExpr(state, e1), evalExpr(state, e2));
/* Attribute existence test (?). */
if (matchOpHasAttr(e, e1, name)) return evalHasAttr(state, e1, name);
/* String or path concatenation. */
if (sym == symOpPlus || sym == symConcatStrings)
return evalPlusConcat(state, e);
/* Backwards compatability: subpath operator (~). */
if (matchSubPath(e, e1, e2)) return evalSubPath(state, e1, e2);
/* List concatenation. */
if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2);
/* Barf. */
abort();
}
Expr evalExpr(EvalState & state, Expr e)
{
checkInterrupt();
#if 0
startNest(nest, lvlVomit,
format("evaluating expression: %1%") % e);
#endif
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 == makeBlackHole())
throwEvalError("infinite recursion encountered");
state.nrCached++;
return nf;
}
/* Otherwise, evaluate and memoize. */
state.normalForms.set(e, makeBlackHole());
try {
nf = evalExpr2(state, e);
} catch (Error & err) {
state.normalForms.remove(e);
throw;
}
state.normalForms.set(e, nf);
return nf;
}
static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs);
static Expr strictEvalExpr_(EvalState & state, Expr e, ATermMap & nfs)
{
e = evalExpr(state, e);
ATermList as;
if (matchAttrs(e, as)) {
ATermList as2 = ATempty;
for (ATermIterator i(as); i; ++i) {
ATerm name; Expr e; ATerm pos;
if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
as2 = ATinsert(as2, makeBind(name, strictEvalExpr(state, e, nfs), pos));
}
return makeAttrs(ATreverse(as2));
}
ATermList es;
if (matchList(e, es)) {
ATermList es2 = ATempty;
for (ATermIterator i(es); i; ++i)
es2 = ATinsert(es2, strictEvalExpr(state, *i, nfs));
return makeList(ATreverse(es2));
}
return e;
}
static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs)
{
Expr nf = nfs.get(e);
if (nf) return nf;
nf = strictEvalExpr_(state, e, nfs);
nfs.set(e, nf);
return nf;
}
Expr strictEvalExpr(EvalState & state, Expr e)
{
ATermMap strictNormalForms;
return strictEvalExpr(state, e, strictNormalForms);
}
#endif
void EvalState::printStats()
{
char x;

View File

@ -52,50 +52,6 @@ ATerm bottomupRewrite(TermFun & f, ATerm e)
}
void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos)
{
ATermList bnds;
if (!matchAttrs(e, bnds))
throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
for (ATermIterator i(bnds); i; ++i) {
ATerm name;
Expr e;
ATerm pos;
if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
attrs.set(name, withPos ? makeAttrRHS(e, pos) : e);
}
}
Expr queryAttr(Expr e, const string & name)
{
ATerm dummy;
return queryAttr(e, name, dummy);
}
Expr queryAttr(Expr e, const string & name, ATerm & pos)
{
ATermList bnds;
if (!matchAttrs(e, bnds))
throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
for (ATermIterator i(bnds); i; ++i) {
ATerm name2, pos2;
Expr e;
if (!matchBind(*i, name2, e, pos2))
abort(); /* can't happen */
if (aterm2String(name2) == name) {
pos = pos2;
return e;
}
}
return 0;
}
Expr makeAttrs(const ATermMap & attrs)
{
ATermList bnds = ATempty;
@ -131,88 +87,6 @@ static void varsBoundByPattern(ATermMap & map, Pattern pat)
}
Expr substitute(const Substitution & subs, Expr e)
{
checkInterrupt();
//if (subs.size() == 0) return e;
ATerm name, pos, e2;
/* As an optimisation, don't substitute in subterms known to be
closed. */
if (matchClosed(e, e2)) return e;
if (matchVar(e, name)) {
Expr sub = subs.lookup(name);
if (sub == makeRemoved()) sub = 0;
Expr wrapped;
/* Add a "closed" wrapper around terms that aren't already
closed. The check is necessary to prevent repeated
wrapping, e.g., closed(closed(closed(...))), which kills
caching. */
return sub ? (matchClosed(sub, wrapped) ? sub : makeClosed(sub)) : e;
}
/* In case of a function, filter out all variables bound by this
function. */
Pattern pat;
ATerm body;
if (matchFunction(e, pat, body, pos)) {
ATermMap map(16);
varsBoundByPattern(map, pat);
Substitution subs2(&subs, &map);
return makeFunction(
(Pattern) substitute(subs2, (Expr) pat),
substitute(subs2, body), pos);
}
/* Idem for a mutually recursive attribute set. */
ATermList rbnds, nrbnds;
if (matchRec(e, rbnds, nrbnds)) {
ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
for (ATermIterator i(rbnds); i; ++i)
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
else abort(); /* can't happen */
for (ATermIterator i(nrbnds); i; ++i)
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
else abort(); /* can't happen */
return makeRec(
(ATermList) substitute(Substitution(&subs, &map), (ATerm) rbnds),
(ATermList) substitute(subs, (ATerm) nrbnds));
}
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] = substitute(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);
for (unsigned int j = 0; i; ++i, ++j)
es[j] = substitute(subs, *i);
ATermList out = ATempty;
for (unsigned int j = len; j; --j)
out = ATinsert(out, es[j - 1]);
return (ATerm) out;
}
return e;
}
/* We use memoisation to prevent exponential complexity on heavily
shared ATerms (remember, an ATerm is a graph, not a tree!). Note
that using an STL set is fine here wrt to ATerm garbage collection
@ -287,51 +161,6 @@ void checkVarDefs(const ATermMap & defs, Expr e)
}
struct Canonicalise : TermFun
{
ATerm operator () (ATerm e)
{
/* Remove position info. */
ATerm path;
int line, column;
if (matchPos(e, path, line, column))
return makeNoPos();
/* Sort attribute sets. */
ATermList _;
if (matchAttrs(e, _)) {
ATermMap attrs;
queryAllAttrs(e, attrs);
StringSet names;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
names.insert(aterm2String(i->key));
ATermList attrs2 = ATempty;
for (StringSet::reverse_iterator i = names.rbegin(); i != names.rend(); ++i)
attrs2 = ATinsert(attrs2,
makeBind(toATerm(*i), attrs.get(toATerm(*i)), makeNoPos()));
return makeAttrs(attrs2);
}
return e;
}
};
Expr canonicaliseExpr(Expr e)
{
Canonicalise canonicalise;
return bottomupRewrite(canonicalise, e);
}
Expr makeBool(bool b)
{
return b ? eTrue : eFalse;
}
bool matchStr(Expr e, string & s, PathSet & context)
{
ATermList l;
@ -354,50 +183,4 @@ Expr makeStr(const string & s, const PathSet & context)
}
string showType(Expr e)
{
ATerm t1, t2;
ATermList l1;
ATermBlob b1;
int i1;
Pattern p1;
if (matchStr(e, t1, l1)) return "a string";
if (matchPath(e, t1)) return "a path";
if (matchNull(e)) return "null";
if (matchInt(e, i1)) return "an integer";
if (matchBool(e, t1)) return "a boolean";
if (matchFunction(e, p1, t1, t2)) return "a function";
if (matchAttrs(e, l1)) return "an attribute set";
if (matchList(e, l1)) return "a list";
if (matchPrimOp(e, i1, b1, l1)) return "a partially applied built-in function";
return "an unknown type";
}
string showValue(Expr e)
{
PathSet context;
string s;
ATerm s2;
int i;
if (matchStr(e, s, context)) {
string u;
for (string::iterator i = s.begin(); i != s.end(); ++i)
if (*i == '\"' || *i == '\\') u += "\\" + *i;
else if (*i == '\n') u += "\\n";
else if (*i == '\r') u += "\\r";
else if (*i == '\t') u += "\\t";
else u += *i;
return "\"" + u + "\"";
}
if (matchPath(e, s2)) return aterm2String(s2);
if (matchNull(e)) return "null";
if (matchInt(e, i)) return (format("%1%") % i).str();
if (e == eTrue) return "true";
if (e == eFalse) return "false";
/* !!! incomplete */
return "<unknown>";
}
}

View File

@ -34,34 +34,10 @@ typedef ATerm ATermBool;
typedef vector<ATerm> ATermVector;
/* A substitution is a linked list of ATermMaps that map names 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
scope. */
struct Substitution
{
ATermMap * map;
const Substitution * prev;
Substitution(const Substitution * prev, ATermMap * map)
{
this->prev = prev;
this->map = map;
}
Expr lookup(Expr name) const
{
Expr x;
for (const Substitution * s(this); s; s = s->prev)
if ((x = s->map->get(name))) return x;
return 0;
}
};
/* Show a position. */
string showPos(ATerm pos);
/* Generic bottomup traversal over ATerms. The traversal first
recursively descends into subterms, and then applies the given term
function to the resulting term. */
@ -73,37 +49,15 @@ struct TermFun
ATerm bottomupRewrite(TermFun & f, ATerm e);
/* Query all attributes in an attribute set expression. The
expression must be in normal form. */
void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos = false);
/* Query a specific attribute from an attribute set expression. The
expression must be in normal form. */
Expr queryAttr(Expr e, const string & name);
Expr queryAttr(Expr e, const string & name, ATerm & pos);
/* Create an attribute set expression from an Attrs value. */
Expr makeAttrs(const ATermMap & attrs);
/* Perform a set of substitutions on an expression. */
Expr substitute(const Substitution & subs, Expr e);
/* Check whether all variables are defined in the given expression.
Throw an exception if this isn't the case. */
void checkVarDefs(const ATermMap & def, Expr e);
/* Canonicalise a Nix expression by sorting attributes and removing
location information. */
Expr canonicaliseExpr(Expr e);
/* Create an expression representing a boolean. */
Expr makeBool(bool b);
/* Manipulation of Str() nodes. Note: matchStr() does not clear
context! */
bool matchStr(Expr e, string & s, PathSet & context);
@ -111,12 +65,6 @@ bool matchStr(Expr e, string & s, PathSet & context);
Expr makeStr(const string & s, const PathSet & context = PathSet());
/* Showing types, values. */
string showType(Expr e);
string showValue(Expr e);
}

View File

@ -42,7 +42,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
bool evalOnly, bool xmlOutput, Expr e)
{
if (parseOnly)
std::cout << format("%1%\n") % canonicaliseExpr(e);
std::cout << format("%1%\n");
else
foreach (Strings::const_iterator, i, attrPaths) {
Value v;