1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-15 15:02:42 +01:00

* Sync with the trunk.

This commit is contained in:
Eelco Dolstra 2010-05-12 22:13:09 +00:00
commit aa45027818
109 changed files with 3350 additions and 4010 deletions

View file

@ -2,27 +2,25 @@ pkglib_LTLIBRARIES = libexpr.la
libexpr_la_SOURCES = \
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
get-drvs.cc attr-path.cc expr-to-xml.cc common-opts.cc \
get-drvs.cc attr-path.cc value-to-xml.cc common-opts.cc \
names.cc
pkginclude_HEADERS = \
nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \
get-drvs.hh attr-path.hh expr-to-xml.hh common-opts.hh \
names.hh nixexpr-ast.hh
get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
names.hh symbol-table.hh
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
../boost/format/libformat.la
BUILT_SOURCES = nixexpr-ast.cc nixexpr-ast.hh \
BUILT_SOURCES = \
parser-tab.hh lexer-tab.hh parser-tab.cc lexer-tab.cc
EXTRA_DIST = lexer.l parser.y nixexpr-ast.def nixexpr-ast.cc
EXTRA_DIST = lexer.l parser.y
AM_CXXFLAGS = \
-I$(srcdir)/.. ${aterm_include} \
-I$(srcdir)/.. \
-I$(srcdir)/../libutil -I$(srcdir)/../libstore
AM_CFLAGS = \
${aterm_include}
# Parser generation.
@ -34,15 +32,6 @@ lexer-tab.cc lexer-tab.hh: lexer.l
$(flex) --outfile lexer-tab.cc --header-file=lexer-tab.hh $(srcdir)/lexer.l
# ATerm helper function generation.
nixexpr-ast.cc nixexpr-ast.hh: ../aterm-helper.pl nixexpr-ast.def
$(perl) $(srcdir)/../aterm-helper.pl nixexpr-ast.hh nixexpr-ast.cc < $(srcdir)/nixexpr-ast.def
CLEANFILES =
# SDF stuff (not built by default).
nix.tbl: nix.sdf
sdf2table -m Nix -s -i nix.sdf -o nix.tbl

View file

@ -1,24 +1,12 @@
#include "attr-path.hh"
#include "nixexpr-ast.hh"
#include "util.hh"
#include "aterm.hh"
namespace nix {
bool isAttrs(EvalState & state, Expr e, ATermMap & attrs)
{
e = evalExpr(state, e);
ATermList dummy;
if (!matchAttrs(e, dummy)) return false;
queryAllAttrs(e, attrs, false);
return true;
}
Expr findAlongAttrPath(EvalState & state, const string & attrPath,
const ATermMap & autoArgs, Expr e)
void findAlongAttrPath(EvalState & state, const string & attrPath,
const Bindings & autoArgs, Expr * e, Value & v)
{
Strings tokens = tokenizeString(attrPath, ".");
@ -26,8 +14,10 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
Error(format("attribute selection path `%1%' does not match expression") % attrPath);
string curPath;
state.mkThunk_(v, e);
for (Strings::iterator i = tokens.begin(); i != tokens.end(); ++i) {
foreach (Strings::iterator, i, tokens) {
if (!curPath.empty()) curPath += ".";
curPath += *i;
@ -39,7 +29,10 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
if (string2Int(attr, attrIndex)) apType = apIndex;
/* Evaluate the expression. */
e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
Value vTmp;
state.autoCallFunction(autoArgs, v, vTmp);
v = vTmp;
state.forceValue(v);
/* It should evaluate to either an attribute set or an
expression, according to what is specified in the
@ -47,36 +40,31 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
if (apType == apAttr) {
ATermMap attrs;
if (!isAttrs(state, e, attrs))
if (v.type != tAttrs)
throw TypeError(
format("the expression selected by the selection path `%1%' should be an attribute set but is %2%")
% curPath % showType(e));
e = attrs.get(toATerm(attr));
if (!e)
throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
% curPath % showType(v));
Bindings::iterator a = v.attrs->find(state.symbols.create(attr));
if (a == v.attrs->end())
throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
v = a->second.value;
}
else if (apType == apIndex) {
ATermList es;
if (!matchList(e, es))
if (v.type != tList)
throw TypeError(
format("the expression selected by the selection path `%1%' should be a list but is %2%")
% curPath % showType(e));
% curPath % showType(v));
e = ATelementAt(es, attrIndex);
if (!e)
throw Error(format("list index %1% in selection path `%2%' not found") % attrIndex % curPath);
if (attrIndex >= v.list.length)
throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath);
v = *v.list.elems[attrIndex];
}
}
return e;
}

View file

@ -10,8 +10,8 @@
namespace nix {
Expr findAlongAttrPath(EvalState & state, const string & attrPath,
const ATermMap & autoArgs, Expr e);
void findAlongAttrPath(EvalState & state, const string & attrPath,
const Bindings & autoArgs, Expr * e, Value & v);
}

View file

@ -2,7 +2,6 @@
#include "../libmain/shared.hh"
#include "util.hh"
#include "parser.hh"
#include "aterm.hh"
namespace nix {
@ -10,7 +9,7 @@ namespace nix {
bool parseOptionArg(const string & arg, Strings::iterator & i,
const Strings::iterator & argsEnd, EvalState & state,
ATermMap & autoArgs)
Bindings & autoArgs)
{
if (arg != "--arg" && arg != "--argstr") return false;
@ -20,11 +19,13 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
string name = *i++;
if (i == argsEnd) throw error;
string value = *i++;
Expr e = arg == "--arg"
? parseExprFromString(state, value, absPath("."))
: makeStr(value);
autoArgs.set(toATerm(name), e);
Value & v(autoArgs[state.symbols.create(name)].value);
if (arg == "--arg")
state.mkThunk_( v, parseExprFromString(state, value, absPath(".")));
else
mkString(v, value);
return true;
}

View file

@ -9,7 +9,7 @@ namespace nix {
/* Some common option parsing between nix-env and nix-instantiate. */
bool parseOptionArg(const string & arg, Strings::iterator & i,
const Strings::iterator & argsEnd, EvalState & state,
ATermMap & autoArgs);
Bindings & autoArgs);
}

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@
#include <map>
#include "nixexpr.hh"
#include "symbol-table.hh"
typedef union _ATermList * ATermList;
@ -12,9 +13,157 @@ namespace nix {
class Hash;
class EvalState;
struct Env;
struct Value;
struct Attr;
typedef std::map<Symbol, Attr> Bindings;
typedef enum {
tInt = 1,
tBool,
tString,
tPath,
tNull,
tAttrs,
tList,
tThunk,
tApp,
tLambda,
tCopy,
tBlackhole,
tPrimOp,
tPrimOpApp,
} ValueType;
typedef void (* PrimOp) (EvalState & state, Value * * args, Value & v);
struct Value
{
ValueType type;
union
{
int integer;
bool boolean;
/* Strings in the evaluator carry a so-called `context' (the
ATermList) which is a list of strings representing store
paths. This is to allow users to write things like
"--with-freetype2-library=" + freetype + "/lib"
where `freetype' is a derivation (or a source to be copied
to the store). If we just concatenated the strings without
keeping track of the referenced store paths, then if the
string is used as a derivation attribute, the derivation
will not have the correct dependencies in its inputDrvs and
inputSrcs.
The semantics of the context is as follows: when a string
with context C is used as a derivation attribute, then the
derivations in C will be added to the inputDrvs of the
derivation, and the other store paths in C will be added to
the inputSrcs of the derivations.
For canonicity, the store paths should be in sorted order. */
struct {
const char * s;
const char * * context; // must be in sorted order
} string;
const char * path;
Bindings * attrs;
struct {
unsigned int length;
Value * * elems;
} list;
struct {
Env * env;
Expr * expr;
} thunk;
struct {
Value * left, * right;
} app;
struct {
Env * env;
ExprLambda * fun;
} lambda;
Value * val;
struct {
PrimOp fun;
char * name;
unsigned int arity;
} primOp;
struct {
Value * left, * right;
unsigned int argsLeft;
} primOpApp;
};
};
struct Env
{
Env * up;
unsigned int prevWith; // nr of levels up to next `with' environment
Value values[0];
};
struct Attr
{
Value value;
Pos * pos;
Attr() : pos(&noPos) { };
};
static inline void mkInt(Value & v, int n)
{
v.type = tInt;
v.integer = n;
}
static inline void mkBool(Value & v, bool b)
{
v.type = tBool;
v.boolean = b;
}
static inline void mkThunk(Value & v, Env & env, Expr * expr)
{
v.type = tThunk;
v.thunk.env = &env;
v.thunk.expr = expr;
}
static inline void mkCopy(Value & v, Value & src)
{
v.type = tCopy;
v.val = &src;
}
static inline void mkApp(Value & v, Value & left, Value & right)
{
v.type = tApp;
v.app.left = &left;
v.app.right = &right;
}
void mkString(Value & v, const char * s);
void mkString(Value & v, const string & s, const PathSet & context = PathSet());
void mkPath(Value & v, const char * s);
typedef std::map<Path, PathSet> DrvRoots;
typedef std::map<Path, Hash> DrvHashes;
/* Cache for calls to addToStore(); maps source paths to the store
@ -23,75 +172,153 @@ typedef std::map<Path, Path> SrcToStore;
struct EvalState;
/* Note: using a ATermVector is safe here, since when we call a primop
we also have an ATermList on the stack. */
typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
std::ostream & operator << (std::ostream & str, Value & v);
struct EvalState
class EvalState
{
ATermMap normalForms;
ATermMap primOps;
DrvRoots drvRoots;
public:
DrvHashes drvHashes; /* normalised derivation hashes */
SrcToStore srcToStore;
unsigned int nrEvaluated;
unsigned int nrCached;
SymbolTable symbols;
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sSystem;
private:
SrcToStore srcToStore;
bool allowUnsafeEquality;
EvalState();
std::map<Path, Expr *> parseTrees;
public:
EvalState();
~EvalState();
/* Evaluate an expression read from the given file to normal
form. */
void evalFile(const Path & path, Value & v);
/* Evaluate an expression to normal form, storing the result in
value `v'. */
void eval(Expr * e, Value & v);
void eval(Env & env, Expr * e, Value & v);
/* Evaluation the expression, then verify that it has the expected
type. */
bool evalBool(Env & env, Expr * e);
void evalAttrs(Env & env, Expr * e, Value & v);
/* If `v' is a thunk, enter it and overwrite `v' with the result
of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */
void forceValue(Value & v);
/* Force a value, then recursively force list elements and
attributes. */
void strictForceValue(Value & v);
/* Force `v', and then verify that it has the expected type. */
int forceInt(Value & v);
bool forceBool(Value & v);
void forceAttrs(Value & v);
void forceList(Value & v);
void forceFunction(Value & v); // either lambda or primop
string forceString(Value & v);
string forceString(Value & v, PathSet & context);
string forceStringNoCtx(Value & v);
/* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */
bool isDerivation(Value & v);
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect.q */
string coerceToString(Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true);
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
Path coerceToPath(Value & v, PathSet & context);
private:
/* The base environment, containing the builtin functions and
values. */
Env & baseEnv;
unsigned int baseEnvDispl;
public:
/* The same, but used during parsing to resolve variables. */
StaticEnv staticBaseEnv; // !!! should be private
private:
void createBaseEnv();
void addConstant(const string & name, Value & v);
void addPrimOps();
void addPrimOp(const string & name,
unsigned int arity, PrimOp primOp);
Value * lookupVar(Env * env, const VarRef & var);
friend class ExprVar;
friend class ExprAttrs;
friend class ExprLet;
public:
/* Do a deep equality test between two values. That is, list
elements and attributes are compared recursively. */
bool eqValues(Value & v1, Value & v2);
void callFunction(Value & fun, Value & arg, Value & v);
/* Automatically call a function for which each argument has a
default value or has a binding in the `args' map. */
void autoCallFunction(const Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */
Value * allocValues(unsigned int count);
Env & allocEnv(unsigned int size);
void mkList(Value & v, unsigned int length);
void mkAttrs(Value & v);
void mkThunk_(Value & v, Expr * expr);
void cloneAttrs(Value & src, Value & dst);
/* Print statistics. */
void printStats();
private:
unsigned long nrEnvs;
unsigned long nrValuesInEnvs;
unsigned long nrValues;
unsigned long nrListElems;
unsigned long nrEvaluated;
unsigned int recursionDepth;
unsigned int maxRecursionDepth;
char * deepestStack; /* for measuring stack usage */
friend class RecursionCounter;
};
/* Evaluate an expression to normal form. */
Expr evalExpr(EvalState & state, Expr e);
/* Return a string representing the type of the value `v'. */
string showType(const Value & v);
/* Evaluate an expression read from the given file to normal form. */
Expr evalFile(EvalState & state, const Path & path);
/* Evaluate an expression, and recursively evaluate list elements and
attributes. If `canonicalise' is true, we remove things like
position information and make sure that attribute sets are in
sorded order. */
Expr strictEvalExpr(EvalState & state, Expr e);
/* Specific results. */
string evalString(EvalState & state, Expr e, PathSet & context);
string evalStringNoCtx(EvalState & state, Expr e);
int evalInt(EvalState & state, Expr e);
bool evalBool(EvalState & state, Expr e);
ATermList evalList(EvalState & state, Expr e);
/* Flatten nested lists into a single list (or expand a singleton into
a list). */
ATermList flattenList(EvalState & state, Expr e);
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. */
string coerceToString(EvalState & state, Expr e, PathSet & context,
bool coerceMore = false, bool copyToStore = true);
/* Path coercion. Converts strings, paths and derivations to a path.
The result is guaranteed to be an canonicalised, absolute path.
Nothing is copied to the store. */
Path coerceToPath(EvalState & state, Expr e, PathSet & context);
/* Automatically call a function for which each argument has a default
value or has a binding in the `args' map. Note: result is a call,
not a normal form; it should be evaluated by calling evalExpr(). */
Expr autoCallFunction(Expr e, const ATermMap & args);
/* Print statistics. */
void printEvalStats(EvalState & state);
}

View file

@ -1,186 +0,0 @@
#include "expr-to-xml.hh"
#include "xml-writer.hh"
#include "nixexpr-ast.hh"
#include "aterm.hh"
#include "util.hh"
#include <cstdlib>
namespace nix {
static XMLAttrs singletonAttrs(const string & name, const string & value)
{
XMLAttrs attrs;
attrs[name] = value;
return attrs;
}
/* set<Expr> is safe because all the expressions are also reachable
from the stack, therefore can't be garbage-collected. */
typedef set<Expr> ExprSet;
static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
ExprSet & drvsSeen, bool location);
static void showAttrs(const ATermMap & attrs, XMLWriter & doc,
PathSet & context, ExprSet & drvsSeen, bool location)
{
StringSet names;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
names.insert(aterm2String(i->key));
for (StringSet::iterator i = names.begin(); i != names.end(); ++i) {
ATerm attrRHS = attrs.get(toATerm(*i));
ATerm attr;
Pos pos;
XMLAttrs xmlAttrs;
xmlAttrs["name"] = *i;
if(matchAttrRHS(attrRHS, attr, pos)) {
ATerm path;
int line, column;
if (location && matchPos(pos, path, line, column)) {
xmlAttrs["path"] = aterm2String(path);
xmlAttrs["line"] = (format("%1%") % line).str();
xmlAttrs["column"] = (format("%1%") % column).str();
}
} else
abort(); // Should not happen.
XMLOpenElement _(doc, "attr", xmlAttrs);
printTermAsXML(attr, doc, context, drvsSeen, location);
}
}
static void printPatternAsXML(Pattern pat, XMLWriter & doc)
{
ATerm name;
ATermList formals;
Pattern pat1, pat2;
ATermBool ellipsis;
if (matchVarPat(pat, name))
doc.writeEmptyElement("varpat", singletonAttrs("name", aterm2String(name)));
else if (matchAttrsPat(pat, formals, ellipsis)) {
XMLOpenElement _(doc, "attrspat");
for (ATermIterator i(formals); i; ++i) {
Expr name; ATerm dummy;
if (!matchFormal(*i, name, dummy)) abort();
doc.writeEmptyElement("attr", singletonAttrs("name", aterm2String(name)));
}
if (ellipsis == eTrue) doc.writeEmptyElement("ellipsis");
}
else if (matchAtPat(pat, pat1, pat2)) {
XMLOpenElement _(doc, "at");
printPatternAsXML(pat1, doc);
printPatternAsXML(pat2, doc);
}
}
static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
ExprSet & drvsSeen, bool location)
{
XMLAttrs attrs;
string s;
ATerm s2;
int i;
ATermList as, es;
ATerm pat, body, pos;
checkInterrupt();
if (matchStr(e, s, context)) /* !!! show the context? */
doc.writeEmptyElement("string", singletonAttrs("value", s));
else if (matchPath(e, s2))
doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s2)));
else if (matchNull(e))
doc.writeEmptyElement("null");
else if (matchInt(e, i))
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % i).str()));
else if (e == eTrue)
doc.writeEmptyElement("bool", singletonAttrs("value", "true"));
else if (e == eFalse)
doc.writeEmptyElement("bool", singletonAttrs("value", "false"));
else if (matchAttrs(e, as)) {
ATermMap attrs;
queryAllAttrs(e, attrs, true);
Expr aRHS = attrs.get(toATerm("type"));
Expr a = NULL;
if (aRHS)
matchAttrRHS(aRHS, a, pos);
if (a && matchStr(a, s, context) && s == "derivation") {
XMLAttrs xmlAttrs;
Path outPath, drvPath;
aRHS = attrs.get(toATerm("drvPath"));
matchAttrRHS(aRHS, a, pos);
if (matchStr(a, drvPath, context))
xmlAttrs["drvPath"] = drvPath;
aRHS = attrs.get(toATerm("outPath"));
matchAttrRHS(aRHS, a, pos);
if (matchStr(a, outPath, context))
xmlAttrs["outPath"] = outPath;
XMLOpenElement _(doc, "derivation", xmlAttrs);
if (drvsSeen.find(e) == drvsSeen.end()) {
drvsSeen.insert(e);
showAttrs(attrs, doc, context, drvsSeen, location);
} else
doc.writeEmptyElement("repeated");
}
else {
XMLOpenElement _(doc, "attrs");
showAttrs(attrs, doc, context, drvsSeen, location);
}
}
else if (matchList(e, es)) {
XMLOpenElement _(doc, "list");
for (ATermIterator i(es); i; ++i)
printTermAsXML(*i, doc, context, drvsSeen, location);
}
else if (matchFunction(e, pat, body, pos)) {
ATerm path;
int line, column;
XMLAttrs xmlAttrs;
if (location && matchPos(pos, path, line, column)) {
xmlAttrs["path"] = aterm2String(path);
xmlAttrs["line"] = (format("%1%") % line).str();
xmlAttrs["column"] = (format("%1%") % column).str();
}
XMLOpenElement _(doc, "function", xmlAttrs);
printPatternAsXML(pat, doc);
}
else
doc.writeEmptyElement("unevaluated");
}
void printTermAsXML(Expr e, std::ostream & out, PathSet & context, bool location)
{
XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr");
ExprSet drvsSeen;
printTermAsXML(e, doc, context, drvsSeen, location);
}
}

View file

@ -1,15 +0,0 @@
#ifndef __EXPR_TO_XML_H
#define __EXPR_TO_XML_H
#include <string>
#include <map>
#include "nixexpr.hh"
namespace nix {
void printTermAsXML(Expr e, std::ostream & out, PathSet & context, bool location = false);
}
#endif /* !__EXPR_TO_XML_H */

View file

@ -1,7 +1,5 @@
#include "get-drvs.hh"
#include "nixexpr-ast.hh"
#include "util.hh"
#include "aterm.hh"
namespace nix {
@ -9,17 +7,10 @@ namespace nix {
string DrvInfo::queryDrvPath(EvalState & state) const
{
if (drvPath == "") {
Expr a = attrs->get(toATerm("drvPath"));
/* Backwards compatibility hack with user environments made by
Nix <= 0.10: these contain illegal Path("") expressions. */
ATerm t;
if (a && matchPath(evalExpr(state, a), t))
return aterm2String(t);
if (drvPath == "" && attrs) {
Bindings::iterator i = attrs->find(state.sDrvPath);
PathSet context;
(string &) drvPath = a ? coerceToPath(state, a, context) : "";
(string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
}
return drvPath;
}
@ -27,11 +18,10 @@ string DrvInfo::queryDrvPath(EvalState & state) const
string DrvInfo::queryOutPath(EvalState & state) const
{
if (outPath == "") {
Expr a = attrs->get(toATerm("outPath"));
if (!a) throw TypeError("output path missing");
if (outPath == "" && attrs) {
Bindings::iterator i = attrs->find(state.sOutPath);
PathSet context;
(string &) outPath = coerceToPath(state, a, context);
(string &) outPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
}
return outPath;
}
@ -39,35 +29,30 @@ string DrvInfo::queryOutPath(EvalState & state) const
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
{
MetaInfo meta;
if (metaInfoRead) return meta;
Expr a = attrs->get(toATerm("meta"));
if (!a) return meta; /* fine, empty meta information */
(bool &) metaInfoRead = true;
Bindings::iterator a = attrs->find(state.sMeta);
if (a == attrs->end()) return meta; /* fine, empty meta information */
ATermMap attrs2;
queryAllAttrs(evalExpr(state, a), attrs2);
state.forceAttrs(a->second.value);
for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
Expr e = evalExpr(state, i->value);
string s;
PathSet context;
foreach (Bindings::iterator, i, *a->second.value.attrs) {
MetaValue value;
int n;
ATermList es;
if (matchStr(e, s, context)) {
state.forceValue(i->second.value);
if (i->second.value.type == tString) {
value.type = MetaValue::tpString;
value.stringValue = s;
meta[aterm2String(i->key)] = value;
} else if (matchInt(e, n)) {
value.stringValue = i->second.value.string.s;
} else if (i->second.value.type == tInt) {
value.type = MetaValue::tpInt;
value.intValue = n;
meta[aterm2String(i->key)] = value;
} else if (matchList(e, es)) {
value.intValue = i->second.value.integer;
} else if (i->second.value.type == tList) {
value.type = MetaValue::tpStrings;
for (ATermIterator j(es); j; ++j)
value.stringValues.push_back(evalStringNoCtx(state, *j));
meta[aterm2String(i->key)] = value;
}
for (unsigned int j = 0; j < i->second.value.list.length; ++j)
value.stringValues.push_back(state.forceStringNoCtx(*i->second.value.list.elems[j]));
} else continue;
((MetaInfo &) meta)[i->first] = value;
}
return meta;
@ -83,73 +68,46 @@ MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
void DrvInfo::setMetaInfo(const MetaInfo & meta)
{
ATermMap metaAttrs;
foreach (MetaInfo::const_iterator, i, meta) {
Expr e;
switch (i->second.type) {
case MetaValue::tpInt: e = makeInt(i->second.intValue); break;
case MetaValue::tpString: e = makeStr(i->second.stringValue); break;
case MetaValue::tpStrings: {
ATermList es = ATempty;
foreach (Strings::const_iterator, j, i->second.stringValues)
es = ATinsert(es, makeStr(*j));
e = makeList(ATreverse(es));
break;
}
default: abort();
}
metaAttrs.set(toATerm(i->first), makeAttrRHS(e, makeNoPos()));
}
attrs->set(toATerm("meta"), makeAttrs(metaAttrs));
metaInfoRead = true;
this->meta = meta;
}
/* Cache for already evaluated derivations. Usually putting ATerms in
a STL container is unsafe (they're not scanning for GC roots), but
here it doesn't matter; everything in this set is reachable from
the stack as well. */
typedef set<Expr> Exprs;
/* Cache for already considered attrsets. */
typedef set<Bindings *> Done;
/* Evaluate expression `e'. If it evaluates to an attribute set of
type `derivation', then put information about it in `drvs' (unless
it's already in `doneExprs'). The result boolean indicates whether
it makes sense for the caller to recursively search for derivations
in `e'. */
static bool getDerivation(EvalState & state, Expr e,
const string & attrPath, DrvInfos & drvs, Exprs & doneExprs)
/* Evaluate value `v'. If it evaluates to an attribute set of type
`derivation', then put information about it in `drvs' (unless it's
already in `doneExprs'). The result boolean indicates whether it
makes sense for the caller to recursively search for derivations in
`v'. */
static bool getDerivation(EvalState & state, Value & v,
const string & attrPath, DrvInfos & drvs, Done & done)
{
try {
ATermList es;
e = evalExpr(state, e);
if (!matchAttrs(e, es)) return true;
boost::shared_ptr<ATermMap> attrs(new ATermMap());
queryAllAttrs(e, *attrs, false);
Expr a = attrs->get(toATerm("type"));
if (!a || evalStringNoCtx(state, a) != "derivation") return true;
state.forceValue(v);
if (!state.isDerivation(v)) return true;
/* Remove spurious duplicates (e.g., an attribute set like
`rec { x = derivation {...}; y = x;}'. */
if (doneExprs.find(e) != doneExprs.end()) return false;
doneExprs.insert(e);
if (done.find(v.attrs) != done.end()) return false;
done.insert(v.attrs);
DrvInfo drv;
a = attrs->get(toATerm("name"));
Bindings::iterator i = v.attrs->find(state.sName);
/* !!! We really would like to have a decent back trace here. */
if (!a) throw TypeError("derivation name missing");
drv.name = evalStringNoCtx(state, a);
if (i == v.attrs->end()) throw TypeError("derivation name missing");
drv.name = state.forceStringNoCtx(i->second.value);
a = attrs->get(toATerm("system"));
if (!a)
i = v.attrs->find(state.sSystem);
if (i == v.attrs->end())
drv.system = "unknown";
else
drv.system = evalStringNoCtx(state, a);
drv.system = state.forceStringNoCtx(i->second.value);
drv.attrs = attrs;
drv.attrs = v.attrs;
drv.attrPath = attrPath;
@ -162,11 +120,11 @@ static bool getDerivation(EvalState & state, Expr e,
}
bool getDerivation(EvalState & state, Expr e, DrvInfo & drv)
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv)
{
Exprs doneExprs;
Done done;
DrvInfos drvs;
getDerivation(state, e, "", drvs, doneExprs);
getDerivation(state, v, "", drvs, done);
if (drvs.size() != 1) return false;
drv = drvs.front();
return true;
@ -179,83 +137,73 @@ static string addToPath(const string & s1, const string & s2)
}
static void getDerivations(EvalState & state, Expr e,
const string & pathPrefix, const ATermMap & autoArgs,
DrvInfos & drvs, Exprs & doneExprs)
static void getDerivations(EvalState & state, Value & vIn,
const string & pathPrefix, const Bindings & autoArgs,
DrvInfos & drvs, Done & done)
{
e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
Value v;
state.autoCallFunction(autoArgs, vIn, v);
/* Process the expression. */
ATermList es;
DrvInfo drv;
if (!getDerivation(state, e, pathPrefix, drvs, doneExprs))
return;
if (!getDerivation(state, v, pathPrefix, drvs, done)) ;
if (matchAttrs(e, es)) {
ATermMap drvMap(ATgetLength(es));
queryAllAttrs(e, drvMap);
else if (v.type == tAttrs) {
/* !!! undocumented hackery to support combining channels in
nix-env.cc. */
bool combineChannels = drvMap.get(toATerm("_combineChannels"));
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
/* Consider the attributes in sorted order to get more
deterministic behaviour in nix-env operations (e.g. when
there are names clashes between derivations, the derivation
bound to the attribute with the "lower" name should take
precedence). */
typedef std::map<string, Expr> AttrsSorted;
AttrsSorted attrsSorted;
foreach (ATermMap::const_iterator, i, drvMap)
attrsSorted[aterm2String(i->key)] = i->value;
typedef std::map<string, Symbol> SortedSymbols;
SortedSymbols attrs;
foreach (Bindings::iterator, i, *v.attrs)
attrs.insert(std::pair<string, Symbol>(i->first, i->first));
foreach (AttrsSorted::iterator, i, attrsSorted) {
foreach (SortedSymbols::iterator, i, attrs) {
startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first);
string pathPrefix2 = addToPath(pathPrefix, i->first);
Value & v2((*v.attrs)[i->second].value);
if (combineChannels)
getDerivations(state, i->second, pathPrefix2, autoArgs, drvs, doneExprs);
else if (getDerivation(state, i->second, pathPrefix2, drvs, doneExprs)) {
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
else if (getDerivation(state, v2, pathPrefix2, drvs, done)) {
/* If the value of this attribute is itself an
attribute set, should we recurse into it? => Only
if it has a `recurseForDerivations = true'
attribute. */
ATermList es;
Expr e = evalExpr(state, i->second), e2;
if (matchAttrs(e, es)) {
ATermMap attrs(ATgetLength(es));
queryAllAttrs(e, attrs, false);
if (((e2 = attrs.get(toATerm("recurseForDerivations")))
&& evalBool(state, e2)))
getDerivations(state, e, pathPrefix2, autoArgs, drvs, doneExprs);
if (v2.type == tAttrs) {
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
if (j != v2.attrs->end() && state.forceBool(j->second.value))
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
}
}
}
return;
}
if (matchList(e, es)) {
int n = 0;
for (ATermIterator i(es); i; ++i, ++n) {
else if (v.type == tList) {
for (unsigned int n = 0; n < v.list.length; ++n) {
startNest(nest, lvlDebug,
format("evaluating list element"));
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
if (getDerivation(state, *i, pathPrefix2, drvs, doneExprs))
getDerivations(state, *i, pathPrefix2, autoArgs, drvs, doneExprs);
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done))
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done);
}
return;
}
throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
else throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
}
void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
const ATermMap & autoArgs, DrvInfos & drvs)
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
const Bindings & autoArgs, DrvInfos & drvs)
{
Exprs doneExprs;
getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs);
Done done;
getDerivations(state, v, pathPrefix, autoArgs, drvs, done);
}

View file

@ -29,16 +29,19 @@ struct DrvInfo
private:
string drvPath;
string outPath;
bool metaInfoRead;
MetaInfo meta;
public:
string name;
string attrPath; /* path towards the derivation */
string system;
/* !!! these should really be hidden, and setMetaInfo() should
make a copy since the ATermMap can be shared between multiple
DrvInfos. */
boost::shared_ptr<ATermMap> attrs;
/* !!! make this private */
Bindings * attrs;
DrvInfo() : metaInfoRead(false), attrs(0) { };
string queryDrvPath(EvalState & state) const;
string queryOutPath(EvalState & state) const;
@ -62,13 +65,12 @@ public:
typedef list<DrvInfo> DrvInfos;
/* Evaluate expression `e'. If it evaluates to a derivation, store
information about the derivation in `drv' and return true.
Otherwise, return false. */
bool getDerivation(EvalState & state, Expr e, DrvInfo & drv);
/* If value `v' denotes a derivation, store information about the
derivation in `drv' and return true. Otherwise, return false. */
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv);
void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
const ATermMap & autoArgs, DrvInfos & drvs);
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
const Bindings & autoArgs, DrvInfos & drvs);
}

View file

@ -8,9 +8,7 @@
%{
#include "aterm.hh"
#include "nixexpr.hh"
#include "nixexpr-ast.hh"
#define BISON_HEADER_HACK
#include "parser-tab.hh"
@ -21,13 +19,16 @@ namespace nix {
static void initLoc(YYLTYPE * loc)
{
loc->first_line = 1;
loc->first_column = 1;
loc->first_line = loc->last_line = 1;
loc->first_column = loc->last_column = 1;
}
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
{
loc->first_line = loc->last_line;
loc->first_column = loc->last_column;
while (len--) {
switch (*s++) {
case '\r':
@ -35,17 +36,17 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
s++;
/* fall through */
case '\n':
++loc->first_line;
loc->first_column = 1;
++loc->last_line;
loc->last_column = 1;
break;
default:
++loc->first_column;
++loc->last_column;
}
}
}
static Expr unescapeStr(const char * s)
static Expr * unescapeStr(const char * s)
{
string t;
char c;
@ -65,7 +66,7 @@ static Expr unescapeStr(const char * s)
}
else t += c;
}
return makeStr(toATerm(t), ATempty);
return new ExprString(t);
}
@ -105,19 +106,20 @@ inherit { return INHERIT; }
\/\/ { return UPDATE; }
\+\+ { return CONCAT; }
{ID} { yylval->t = toATerm(yytext); return ID; /* !!! alloc */ }
{ID} { yylval->id = strdup(yytext); return ID; }
{INT} { int n = atoi(yytext); /* !!! overflow */
yylval->t = ATmake("<int>", n);
yylval->n = n;
return INT;
}
\" { BEGIN(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
/* !!! Not quite right: we want a follow restriction on "$", it
shouldn't be followed by a "{". Right now "$\"" will be consumed
as part of a string, rather than a "$" followed by the string
terminator. Disallow "$\"" for now. */
yylval->t = unescapeStr(yytext); /* !!! alloc */
/* !!! Not quite right: we want a follow restriction on
"$", it shouldn't be followed by a "{". Right now
"$\"" will be consumed as part of a string, rather
than a "$" followed by the string terminator.
Disallow "$\"" for now. */
yylval->e = unescapeStr(yytext);
return STR;
}
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
@ -126,31 +128,31 @@ inherit { return INHERIT; }
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->t = makeIndStr(toATerm(yytext));
yylval->e = new ExprIndStr(yytext);
return IND_STR;
}
<IND_STRING>\'\'\$ {
yylval->t = makeIndStr(toATerm("$"));
yylval->e = new ExprIndStr("$");
return IND_STR;
}
<IND_STRING>\'\'\' {
yylval->t = makeIndStr(toATerm("''"));
yylval->e = new ExprIndStr("''");
return IND_STR;
}
<IND_STRING>\'\'\\. {
yylval->t = unescapeStr(yytext + 2);
yylval->e = unescapeStr(yytext + 2);
return IND_STR;
}
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
<IND_STRING>\' {
yylval->t = makeIndStr(toATerm("'"));
yylval->e = new ExprIndStr("'");
return IND_STR;
}
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
{PATH} { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ }
{URI} { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ }
{PATH} { yylval->path = strdup(yytext); return PATH; }
{URI} { yylval->uri = strdup(yytext); return URI; }
[ \t\r\n]+ /* eat up whitespace */
\#[^\r\n]* /* single-line comments */

View file

@ -1,97 +0,0 @@
init initNixExprHelpers
Pos | string int int | Pos |
NoPos | | Pos |
Function | Pattern Expr Pos | Expr |
Assert | Expr Expr Pos | Expr |
With | Expr Expr Pos | Expr |
If | Expr Expr Expr | Expr |
OpNot | Expr | Expr |
OpEq | Expr Expr | Expr |
OpNEq | Expr Expr | Expr |
OpAnd | Expr Expr | Expr |
OpOr | Expr Expr | Expr |
OpImpl | Expr Expr | Expr |
OpUpdate | Expr Expr | Expr |
SubPath | Expr Expr | Expr |
OpHasAttr | Expr string | Expr |
OpPlus | Expr Expr | Expr |
OpConcat | Expr Expr | Expr |
ConcatStrings | ATermList | Expr |
Call | Expr Expr | Expr |
Select | Expr string | Expr |
Var | string | Expr |
Int | int | Expr |
# Strings in the evaluator carry a so-called `context' (the ATermList)
# which is a list of strings representing store paths. This is to
# allow users to write things like
#
# "--with-freetype2-library=" + freetype + "/lib"
#
# where `freetype' is a derivation (or a source to be copied to the
# store). If we just concatenated the strings without keeping track
# of the referenced store paths, then if the string is used as a
# derivation attribute, the derivation will not have the correct
# dependencies in its inputDrvs and inputSrcs.
#
# The semantics of the context is as follows: when a string with
# context C is used as a derivation attribute, then the derivations in
# C will be added to the inputDrvs of the derivation, and the other
# store paths in C will be added to the inputSrcs of the derivations.
#
# For canonicity, the store paths should be in sorted order.
Str | string ATermList | Expr |
Str | string | Expr | ObsoleteStr
# Internal to the parser, doesn't occur in ASTs.
IndStr | string | Expr |
# A path is a reference to a file system object that is to be copied
# to the Nix store when used as a derivation attribute. When it is
# concatenated to a string (i.e., `str + path'), it is also copied and
# the resulting store path is concatenated to the string (with the
# store path in the context). If a string or path is concatenated to
# a path (i.e., `path + str' or `path + path'), the result is a new
# path (if the right-hand side is a string, the context must be
# empty).
Path | string | Expr |
List | ATermList | Expr |
BlackHole | | Expr |
Undefined | | Expr |
Removed | | Expr |
PrimOp | int ATermBlob ATermList | Expr |
Attrs | ATermList | Expr |
Closed | Expr | Expr |
Rec | ATermList ATermList | Expr |
Bool | ATermBool | Expr |
Null | | Expr |
Bind | string Expr Pos | ATerm |
BindAttrPath | ATermList Expr Pos | ATerm | # desugared during parsing
Bind | string Expr | ATerm | ObsoleteBind
Inherit | Expr ATermList Pos | ATerm |
Scope | | Expr |
VarPat | string | Pattern |
AttrsPat | ATermList ATermBool | Pattern | # bool = `...'
AtPat | Pattern Pattern | Pattern |
Formal | string DefaultValue | ATerm |
DefaultValue | Expr | DefaultValue |
NoDefaultValue | | DefaultValue |
True | | ATermBool |
False | | ATermBool |
PrimOpDef | int ATermBlob | ATerm |
AttrRHS | Expr Pos | ATerm |
eTrue = makeBool(makeTrue())
eFalse = makeBool(makeFalse())
sOverrides = toATerm("__overrides")

View file

@ -1,407 +1,325 @@
#include "nixexpr.hh"
#include "derivations.hh"
#include "util.hh"
#include "aterm.hh"
#include "nixexpr-ast.hh"
#include "nixexpr-ast.cc"
#include <cstdlib>
namespace nix {
/* Displaying abstract syntax trees. */
std::ostream & operator << (std::ostream & str, Expr & e)
{
e.show(str);
return str;
}
void Expr::show(std::ostream & str)
{
abort();
}
void ExprInt::show(std::ostream & str)
{
str << n;
}
void ExprString::show(std::ostream & str)
{
str << "\"" << s << "\""; // !!! escaping
}
void ExprPath::show(std::ostream & str)
{
str << s;
}
void ExprVar::show(std::ostream & str)
{
str << info.name;
}
void ExprSelect::show(std::ostream & str)
{
str << "(" << *e << ")." << name;
}
void ExprOpHasAttr::show(std::ostream & str)
{
str << "(" << *e << ") ? " << name;
}
void ExprAttrs::show(std::ostream & str)
{
if (recursive) str << "rec ";
str << "{ ";
foreach (list<Inherited>::iterator, i, inherited)
str << "inherit " << i->first.name << "; ";
foreach (Attrs::iterator, i, attrs)
str << i->first << " = " << *i->second.first << "; ";
str << "}";
}
void ExprList::show(std::ostream & str)
{
str << "[ ";
foreach (vector<Expr *>::iterator, i, elems)
str << "(" << **i << ") ";
str << "]";
}
void ExprLambda::show(std::ostream & str)
{
str << "(";
if (matchAttrs) {
str << "{ ";
bool first = true;
foreach (Formals::Formals_::iterator, i, formals->formals) {
if (first) first = false; else str << ", ";
str << i->name;
if (i->def) str << " ? " << *i->def;
}
str << " }";
if (!arg.empty()) str << " @ ";
}
if (!arg.empty()) str << arg;
str << ": " << *body << ")";
}
void ExprLet::show(std::ostream & str)
{
str << "let ";
foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited)
str << "inherit " << i->first.name << "; ";
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
str << i->first << " = " << *i->second.first << "; ";
str << "in " << *body;
}
void ExprWith::show(std::ostream & str)
{
str << "with " << *attrs << "; " << *body;
}
void ExprIf::show(std::ostream & str)
{
str << "if " << *cond << " then " << *then << " else " << *else_;
}
void ExprAssert::show(std::ostream & str)
{
str << "assert " << *cond << "; " << *body;
}
void ExprOpNot::show(std::ostream & str)
{
str << "! " << *e;
}
void ExprConcatStrings::show(std::ostream & str)
{
bool first = true;
foreach (vector<Expr *>::iterator, i, *es) {
if (first) first = false; else str << " + ";
str << **i;
}
}
std::ostream & operator << (std::ostream & str, const Pos & pos)
{
if (!pos.line)
str << "undefined position";
else
str << (format("`%1%:%2%:%3%'") % pos.file % pos.line % pos.column).str();
return str;
}
Pos noPos;
/* Computing levels/displacements for variables. */
void Expr::bindVars(const StaticEnv & env)
{
abort();
}
void ExprInt::bindVars(const StaticEnv & env)
{
}
void ExprString::bindVars(const StaticEnv & env)
{
}
void ExprPath::bindVars(const StaticEnv & env)
{
}
void VarRef::bind(const StaticEnv & env)
{
/* Check whether the variable appears in the environment. If so,
set its level and displacement. */
const StaticEnv * curEnv;
unsigned int level;
int withLevel = -1;
for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++) {
if (curEnv->isWith) {
if (withLevel == -1) withLevel = level;
} else {
StaticEnv::Vars::const_iterator i = curEnv->vars.find(name);
if (i != curEnv->vars.end()) {
fromWith = false;
this->level = level;
displ = i->second;
return;
}
}
}
/* Otherwise, the variable must be obtained from the nearest
enclosing `with'. If there is no `with', then we can issue an
"undefined variable" error now. */
if (withLevel == -1) throw EvalError(format("undefined variable `%1%'") % name);
fromWith = true;
this->level = withLevel;
}
void ExprVar::bindVars(const StaticEnv & env)
{
info.bind(env);
}
void ExprSelect::bindVars(const StaticEnv & env)
{
e->bindVars(env);
}
void ExprOpHasAttr::bindVars(const StaticEnv & env)
{
e->bindVars(env);
}
void ExprAttrs::bindVars(const StaticEnv & env)
{
if (recursive) {
StaticEnv newEnv(false, &env);
unsigned int displ = 0;
string showPos(ATerm pos)
{
ATerm path;
int line, column;
if (matchNoPos(pos)) return "undefined position";
if (!matchPos(pos, path, line, column))
throw badTerm("position expected", pos);
return (format("`%1%:%2%:%3%'") % aterm2String(path) % line % column).str();
foreach (ExprAttrs::Attrs::iterator, i, attrs)
newEnv.vars[i->first] = displ++;
foreach (list<Inherited>::iterator, i, inherited) {
newEnv.vars[i->first.name] = displ++;
i->first.bind(env);
}
foreach (ExprAttrs::Attrs::iterator, i, attrs)
i->second.first->bindVars(newEnv);
}
else {
foreach (ExprAttrs::Attrs::iterator, i, attrs)
i->second.first->bindVars(env);
foreach (list<Inherited>::iterator, i, inherited)
i->first.bind(env);
}
}
void ExprList::bindVars(const StaticEnv & env)
{
foreach (vector<Expr *>::iterator, i, elems)
(*i)->bindVars(env);
}
void ExprLambda::bindVars(const StaticEnv & env)
{
StaticEnv newEnv(false, &env);
ATerm bottomupRewrite(TermFun & f, ATerm e)
{
checkInterrupt();
if (ATgetType(e) == AT_APPL) {
AFun fun = ATgetAFun(e);
int arity = ATgetArity(fun);
ATerm args[arity];
for (int i = 0; i < arity; ++i)
args[i] = bottomupRewrite(f, ATgetArgument(e, i));
e = (ATerm) ATmakeApplArray(fun, args);
}
else if (ATgetType(e) == AT_LIST) {
ATermList in = (ATermList) e;
ATermList out = ATempty;
for (ATermIterator i(in); i; ++i)
out = ATinsert(out, bottomupRewrite(f, *i));
e = (ATerm) ATreverse(out);
}
return f(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;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
Expr e;
ATerm pos;
if (!matchAttrRHS(i->value, e, pos))
abort(); /* can't happen */
bnds = ATinsert(bnds, makeBind(i->key, e, pos));
}
return makeAttrs(bnds);
}
static void varsBoundByPattern(ATermMap & map, Pattern pat)
{
ATerm name;
ATermList formals;
Pattern pat1, pat2;
ATermBool ellipsis;
/* Use makeRemoved() so that it can be used directly in
substitute(). */
if (matchVarPat(pat, name))
map.set(name, makeRemoved());
else if (matchAttrsPat(pat, formals, ellipsis)) {
for (ATermIterator i(formals); i; ++i) {
ATerm d1;
if (!matchFormal(*i, name, d1)) abort();
map.set(name, makeRemoved());
}
}
else if (matchAtPat(pat, pat1, pat2)) {
varsBoundByPattern(map, pat1);
varsBoundByPattern(map, pat2);
}
else abort();
}
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
since all the ATerms in the set are already reachable from
somewhere else. */
static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
{
if (done.find(e) != done.end()) return;
done.insert(e);
unsigned int displ = 0;
ATerm name, pos, value;
ATerm with, body;
ATermList rbnds, nrbnds;
Pattern pat;
if (!arg.empty()) newEnv.vars[arg] = displ++;
/* Closed terms don't have free variables, so we don't have to
check by definition. */
if (matchClosed(e, value)) return;
if (matchAttrs) {
foreach (Formals::Formals_::iterator, i, formals->formals)
newEnv.vars[i->name] = displ++;
foreach (Formals::Formals_::iterator, i, formals->formals)
if (i->def) i->def->bindVars(newEnv);
}
body->bindVars(newEnv);
}
void ExprLet::bindVars(const StaticEnv & env)
{
StaticEnv newEnv(false, &env);
else if (matchVar(e, name)) {
if (!defs.get(name))
throw EvalError(format("undefined variable `%1%'")
% aterm2String(name));
unsigned int displ = 0;
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
newEnv.vars[i->first] = displ++;
foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited) {
newEnv.vars[i->first.name] = displ++;
i->first.bind(env);
}
else if (matchFunction(e, pat, body, pos)) {
ATermMap defs2(defs);
varsBoundByPattern(defs2, pat);
set<Expr> done2;
checkVarDefs2(done2, defs2, pat);
checkVarDefs2(done2, defs2, body);
}
else if (matchRec(e, rbnds, nrbnds)) {
checkVarDefs2(done, defs, (ATerm) nrbnds);
ATermMap defs2(defs);
for (ATermIterator i(rbnds); i; ++i) {
if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
defs2.set(name, (ATerm) ATempty);
}
for (ATermIterator i(nrbnds); i; ++i) {
if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
defs2.set(name, (ATerm) ATempty);
}
set<Expr> done2;
checkVarDefs2(done2, defs2, (ATerm) rbnds);
}
else if (matchWith(e, with, body, pos)) {
/* We can't check the body without evaluating the definitions
(which is an arbitrary expression), so we don't do that
here but only when actually evaluating the `with'. */
checkVarDefs2(done, defs, with);
}
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
i->second.first->bindVars(newEnv);
else if (ATgetType(e) == AT_APPL) {
int arity = ATgetArity(ATgetAFun(e));
for (int i = 0; i < arity; ++i)
checkVarDefs2(done, defs, ATgetArgument(e, i));
}
else if (ATgetType(e) == AT_LIST)
for (ATermIterator i((ATermList) e); i; ++i)
checkVarDefs2(done, defs, *i);
body->bindVars(newEnv);
}
void checkVarDefs(const ATermMap & defs, Expr e)
void ExprWith::bindVars(const StaticEnv & env)
{
set<Expr> done;
checkVarDefs2(done, defs, 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);
/* Does this `with' have an enclosing `with'? If so, record its
level so that `lookupVar' can look up variables in the previous
`with' if this one doesn't contain the desired attribute. */
const StaticEnv * curEnv;
unsigned int level;
prevWith = 0;
for (curEnv = &env, level = 1; curEnv; curEnv = curEnv->up, level++)
if (curEnv->isWith) {
prevWith = level;
break;
}
return e;
}
};
attrs->bindVars(env);
StaticEnv newEnv(true, &env);
body->bindVars(newEnv);
}
Expr canonicaliseExpr(Expr e)
void ExprIf::bindVars(const StaticEnv & env)
{
Canonicalise canonicalise;
return bottomupRewrite(canonicalise, e);
cond->bindVars(env);
then->bindVars(env);
else_->bindVars(env);
}
Expr makeBool(bool b)
void ExprAssert::bindVars(const StaticEnv & env)
{
return b ? eTrue : eFalse;
cond->bindVars(env);
body->bindVars(env);
}
bool matchStr(Expr e, string & s, PathSet & context)
void ExprOpNot::bindVars(const StaticEnv & env)
{
ATermList l;
ATerm s_;
if (!matchStr(e, s_, l)) return false;
s = aterm2String(s_);
for (ATermIterator i(l); i; ++i)
context.insert(aterm2String(*i));
return true;
e->bindVars(env);
}
Expr makeStr(const string & s, const PathSet & context)
void ExprConcatStrings::bindVars(const StaticEnv & env)
{
return makeStr(toATerm(s), toATermList(context));
foreach (vector<Expr *>::iterator, i, *es)
(*i)->bindVars(env);
}
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

@ -3,8 +3,7 @@
#include <map>
#include "aterm-map.hh"
#include "types.hh"
#include "symbol-table.hh"
namespace nix {
@ -18,105 +17,254 @@ MakeError(Abort, EvalError)
MakeError(TypeError, EvalError)
/* Nix expressions are represented as ATerms. The maximal sharing
property of the ATerm library allows us to implement caching of
normals forms efficiently. */
typedef ATerm Expr;
typedef ATerm DefaultValue;
typedef ATerm Pos;
typedef ATerm Pattern;
typedef ATerm ATermBool;
/* Position objects. */
/* A STL vector of ATerms. Should be used with great care since it's
stored on the heap, and the elements are therefore not roots to the
ATerm garbage collector. */
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
struct Pos
{
ATermMap * map;
const Substitution * prev;
string file;
unsigned int line, column;
Pos() : line(0), column(0) { };
Pos(const string & file, unsigned int line, unsigned int column)
: file(file), line(line), column(column) { };
};
Substitution(const Substitution * prev, ATermMap * map)
{
this->prev = prev;
this->map = map;
}
extern Pos noPos;
std::ostream & operator << (std::ostream & str, const Pos & pos);
struct Env;
struct Value;
struct EvalState;
struct StaticEnv;
/* Abstract syntax of Nix expressions. */
struct Expr
{
virtual void show(std::ostream & str);
virtual void bindVars(const StaticEnv & env);
virtual void eval(EvalState & state, Env & env, Value & v);
};
std::ostream & operator << (std::ostream & str, Expr & e);
#define COMMON_METHODS \
void show(std::ostream & str); \
void eval(EvalState & state, Env & env, Value & v); \
void bindVars(const StaticEnv & env);
struct ExprInt : Expr
{
int n;
ExprInt(int n) : n(n) { };
COMMON_METHODS
};
struct ExprString : Expr
{
string s;
ExprString(const string & s) : s(s) { };
COMMON_METHODS
};
/* Temporary class used during parsing of indented strings. */
struct ExprIndStr : Expr
{
string s;
ExprIndStr(const string & s) : s(s) { };
};
struct ExprPath : Expr
{
string s;
ExprPath(const string & s) : s(s) { };
COMMON_METHODS
};
struct VarRef
{
Symbol name;
/* Whether the variable comes from an environment (e.g. a rec, let
or function argument) or from a "with". */
bool fromWith;
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;
}
/* In the former case, the value is obtained by going `level'
levels up from the current environment and getting the
`displ'th value in that environment. In the latter case, the
value is obtained by getting the attribute named `name' from
the attribute set stored in the environment that is `level'
levels up from the current one.*/
unsigned int level;
unsigned int displ;
VarRef(const Symbol & name) : name(name) { };
void bind(const StaticEnv & env);
};
/* 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. */
struct TermFun
struct ExprVar : Expr
{
virtual ~TermFun() { }
virtual ATerm operator () (ATerm e) = 0;
VarRef info;
ExprVar(const Symbol & name) : info(name) { };
COMMON_METHODS
};
struct ExprSelect : Expr
{
Expr * e;
Symbol name;
ExprSelect(Expr * e, const Symbol & name) : e(e), name(name) { };
COMMON_METHODS
};
struct ExprOpHasAttr : Expr
{
Expr * e;
Symbol name;
ExprOpHasAttr(Expr * e, const Symbol & name) : e(e), name(name) { };
COMMON_METHODS
};
struct ExprAttrs : Expr
{
bool recursive;
typedef std::pair<Expr *, Pos> Attr;
typedef std::pair<VarRef, Pos> Inherited;
typedef std::map<Symbol, Attr> Attrs;
Attrs attrs;
list<Inherited> inherited;
std::map<Symbol, Pos> attrNames; // used during parsing
ExprAttrs() : recursive(false) { };
COMMON_METHODS
};
struct ExprList : Expr
{
std::vector<Expr *> elems;
ExprList() { };
COMMON_METHODS
};
struct Formal
{
Symbol name;
Expr * def;
Formal(const Symbol & name, Expr * def) : name(name), def(def) { };
};
struct Formals
{
typedef std::list<Formal> Formals_;
Formals_ formals;
std::set<Symbol> argNames; // used during parsing
bool ellipsis;
};
struct ExprLambda : Expr
{
Pos pos;
Symbol arg;
bool matchAttrs;
Formals * formals;
Expr * body;
ExprLambda(const Pos & pos, const Symbol & arg, bool matchAttrs, Formals * formals, Expr * body)
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
{
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
% arg % pos);
};
COMMON_METHODS
};
struct ExprLet : Expr
{
ExprAttrs * attrs;
Expr * body;
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
COMMON_METHODS
};
struct ExprWith : Expr
{
Pos pos;
Expr * attrs, * body;
unsigned int prevWith;
ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
COMMON_METHODS
};
struct ExprIf : Expr
{
Expr * cond, * then, * else_;
ExprIf(Expr * cond, Expr * then, Expr * else_) : cond(cond), then(then), else_(else_) { };
COMMON_METHODS
};
struct ExprAssert : Expr
{
Pos pos;
Expr * cond, * body;
ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
COMMON_METHODS
};
struct ExprOpNot : Expr
{
Expr * e;
ExprOpNot(Expr * e) : e(e) { };
COMMON_METHODS
};
#define MakeBinOp(name, s) \
struct Expr##name : Expr \
{ \
Expr * e1, * e2; \
Expr##name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
void show(std::ostream & str) \
{ \
str << *e1 << " " s " " << *e2; \
} \
void bindVars(const StaticEnv & env) \
{ \
e1->bindVars(env); e2->bindVars(env); \
} \
void eval(EvalState & state, Env & env, Value & v); \
};
MakeBinOp(App, "")
MakeBinOp(OpEq, "==")
MakeBinOp(OpNEq, "!=")
MakeBinOp(OpAnd, "&&")
MakeBinOp(OpOr, "||")
MakeBinOp(OpImpl, "->")
MakeBinOp(OpUpdate, "//")
MakeBinOp(OpConcatLists, "++")
struct ExprConcatStrings : Expr
{
vector<Expr *> * es;
ExprConcatStrings(vector<Expr *> * es) : es(es) { };
COMMON_METHODS
};
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);
/* Static environments are used to map variable names onto (level,
displacement) pairs used to obtain the value of the variable at
runtime. */
struct StaticEnv
{
bool isWith;
const StaticEnv * up;
typedef std::map<Symbol, unsigned int> Vars;
Vars vars;
StaticEnv(bool isWith, const StaticEnv * up) : isWith(isWith), up(up) { };
};
/* 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);
Expr makeStr(const string & s, const PathSet & context = PathSet());
/* Showing types, values. */
string showType(Expr e);
string showValue(Expr e);
}

View file

@ -8,12 +8,11 @@ namespace nix {
/* Parse a Nix expression from the specified file. If `path' refers
to a directory, the "/default.nix" is appended. */
Expr parseExprFromFile(EvalState & state, Path path);
to a directory, then "/default.nix" is appended. */
Expr * parseExprFromFile(EvalState & state, Path path);
/* Parse a Nix expression from the specified string. */
Expr parseExprFromString(EvalState & state, const string & s,
const Path & basePath);
Expr * parseExprFromString(EvalState & state, const string & s, const Path & basePath);
}

View file

@ -20,16 +20,14 @@
#include <stdlib.h>
#include <string.h>
#include "aterm.hh"
#include "util.hh"
#include "nixexpr.hh"
#include "parser-tab.hh"
#include "lexer-tab.hh"
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
#include "nixexpr.hh"
#include "nixexpr-ast.hh"
using namespace nix;
@ -39,145 +37,85 @@ namespace nix {
struct ParseData
{
Expr result;
SymbolTable & symbols;
Expr * result;
Path basePath;
Path path;
string error;
Symbol sLetBody;
ParseData(SymbolTable & symbols)
: symbols(symbols)
, sLetBody(symbols.create("<let-body>"))
{ };
};
static string showAttrPath(ATermList attrPath)
static string showAttrPath(const vector<Symbol> & attrPath)
{
string s;
for (ATermIterator i(attrPath); i; ++i) {
foreach (vector<Symbol>::const_iterator, i, attrPath) {
if (!s.empty()) s += '.';
s += aterm2String(*i);
s += *i;
}
return s;
}
struct Tree
static void dupAttr(const vector<Symbol> & attrPath, const Pos & pos, const Pos & prevPos)
{
Expr leaf; ATerm pos; bool recursive;
typedef std::map<ATerm, Tree> Children;
Children children;
Tree() { leaf = 0; recursive = true; }
};
static ATermList buildAttrs(const Tree & t, ATermList & nonrec)
{
ATermList res = ATempty;
for (Tree::Children::const_reverse_iterator i = t.children.rbegin();
i != t.children.rend(); ++i)
if (!i->second.recursive)
nonrec = ATinsert(nonrec, makeBind(i->first, i->second.leaf, i->second.pos));
else
res = ATinsert(res, i->second.leaf
? makeBind(i->first, i->second.leaf, i->second.pos)
: makeBind(i->first, makeAttrs(buildAttrs(i->second, nonrec)), makeNoPos()));
return res;
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % pos % prevPos);
}
static Expr fixAttrs(bool recursive, ATermList as)
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{
Tree attrs;
vector<Symbol> attrPath; attrPath.push_back(attr);
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % pos % prevPos);
}
/* This ATermMap is needed to ensure that the `leaf' fields in the
Tree nodes are not garbage collected. */
ATermMap gcRoots;
for (ATermIterator i(as); i; ++i) {
ATermList names, attrPath; Expr src, e; ATerm name, pos;
if (matchInherit(*i, src, names, pos)) {
bool fromScope = matchScope(src);
for (ATermIterator j(names); j; ++j) {
if (attrs.children.find(*j) != attrs.children.end())
throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
% showAttrPath(ATmakeList1(*j)) % showPos(pos));
Tree & t(attrs.children[*j]);
Expr leaf = fromScope ? makeVar(*j) : makeSelect(src, *j);
gcRoots.set(leaf, leaf);
t.leaf = leaf;
t.pos = pos;
if (recursive && fromScope) t.recursive = false;
static void addAttr(ExprAttrs * attrs, const vector<Symbol> & attrPath,
Expr * e, const Pos & pos)
{
unsigned int n = 0;
foreach (vector<Symbol>::const_iterator, i, attrPath) {
n++;
ExprAttrs::Attrs::iterator j = attrs->attrs.find(*i);
if (j != attrs->attrs.end()) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.first);
if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.second);
attrs = attrs2;
} else {
if (attrs->attrNames.find(*i) != attrs->attrNames.end())
dupAttr(attrPath, pos, attrs->attrNames[*i]);
attrs->attrNames[*i] = pos;
if (n == attrPath.size())
attrs->attrs[*i] = ExprAttrs::Attr(e, pos);
else {
ExprAttrs * nested = new ExprAttrs;
attrs->attrs[*i] = ExprAttrs::Attr(nested, pos);
attrs = nested;
}
}
else if (matchBindAttrPath(*i, attrPath, e, pos)) {
Tree * t(&attrs);
for (ATermIterator j(attrPath); j; ) {
name = *j; ++j;
if (t->leaf) throw ParseError(format("attribute set containing `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % showPos(pos) % showPos (t->pos));
t = &(t->children[name]);
}
if (t->leaf)
throw ParseError(format("duplicate definition of attribute `%1%' at %2% and %3%")
% showAttrPath(attrPath) % showPos(pos) % showPos (t->pos));
if (!t->children.empty())
throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
% showAttrPath(attrPath) % showPos(pos));
t->leaf = e; t->pos = pos;
}
else abort(); /* can't happen */
}
ATermList nonrec = ATempty;
ATermList rec = buildAttrs(attrs, nonrec);
return recursive ? makeRec(rec, nonrec) : makeAttrs(rec);
}
static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
{
ATerm name;
ATermList formals;
Pattern pat1, pat2;
ATermBool ellipsis;
if (matchVarPat(pat, name)) {
if (map.get(name))
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
% aterm2String(name) % showPos(pos));
map.set(name, name);
}
else if (matchAttrsPat(pat, formals, ellipsis)) {
for (ATermIterator i(formals); i; ++i) {
ATerm d1;
if (!matchFormal(*i, name, d1)) abort();
if (map.get(name))
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
% aterm2String(name) % showPos(pos));
map.set(name, name);
}
}
else if (matchAtPat(pat, pat1, pat2)) {
checkPatternVars(pos, map, pat1);
checkPatternVars(pos, map, pat2);
}
else abort();
if (formals->argNames.find(formal.name) != formals->argNames.end())
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
% formal.name % pos);
formals->formals.push_front(formal);
formals->argNames.insert(formal.name);
}
static void checkPatternVars(ATerm pos, Pattern pat)
static Expr * stripIndentation(vector<Expr *> & es)
{
ATermMap map;
checkPatternVars(pos, map, pat);
}
static Expr stripIndentation(ATermList es)
{
if (es == ATempty) return makeStr("");
if (es.empty()) return new ExprString("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
@ -185,9 +123,9 @@ static Expr stripIndentation(ATermList es)
bool atStartOfLine = true; /* = seen only whitespace in the current line */
unsigned int minIndent = 1000000;
unsigned int curIndent = 0;
ATerm e;
for (ATermIterator i(es); i; ++i) {
if (!matchIndStr(*i, e)) {
foreach (vector<Expr *>::iterator, i, es) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
if (!e) {
/* Anti-quotations end the current start-of-line whitespace. */
if (atStartOfLine) {
atStartOfLine = false;
@ -195,12 +133,11 @@ static Expr stripIndentation(ATermList es)
}
continue;
}
string s = aterm2String(e);
for (unsigned int j = 0; j < s.size(); ++j) {
for (unsigned int j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
if (s[j] == ' ')
if (e->s[j] == ' ')
curIndent++;
else if (s[j] == '\n') {
else if (e->s[j] == '\n') {
/* Empty line, doesn't influence minimum
indentation. */
curIndent = 0;
@ -208,7 +145,7 @@ static Expr stripIndentation(ATermList es)
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
} else if (s[j] == '\n') {
} else if (e->s[j] == '\n') {
atStartOfLine = true;
curIndent = 0;
}
@ -216,37 +153,37 @@ static Expr stripIndentation(ATermList es)
}
/* Strip spaces from each line. */
ATermList es2 = ATempty;
vector<Expr *> * es2 = new vector<Expr *>;
atStartOfLine = true;
unsigned int curDropped = 0;
unsigned int n = ATgetLength(es);
for (ATermIterator i(es); i; ++i, --n) {
if (!matchIndStr(*i, e)) {
unsigned int n = es.size();
for (vector<Expr *>::iterator i = es.begin(); i != es.end(); ++i, --n) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
if (!e) {
atStartOfLine = false;
curDropped = 0;
es2 = ATinsert(es2, *i);
es2->push_back(*i);
continue;
}
string s = aterm2String(e);
string s2;
for (unsigned int j = 0; j < s.size(); ++j) {
for (unsigned int j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
if (s[j] == ' ') {
if (e->s[j] == ' ') {
if (curDropped++ >= minIndent)
s2 += s[j];
s2 += e->s[j];
}
else if (s[j] == '\n') {
else if (e->s[j] == '\n') {
curDropped = 0;
s2 += s[j];
s2 += e->s[j];
} else {
atStartOfLine = false;
curDropped = 0;
s2 += s[j];
s2 += e->s[j];
}
} else {
s2 += s[j];
if (s[j] == '\n') atStartOfLine = true;
s2 += e->s[j];
if (e->s[j] == '\n') atStartOfLine = true;
}
}
@ -257,11 +194,11 @@ static Expr stripIndentation(ATermList es)
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
s2 = string(s2, 0, p + 1);
}
es2 = ATinsert(es2, makeStr(s2));
es2->push_back(new ExprString(s2));
}
return makeConcatStrings(ATreverse(es2));
return new ExprConcatStrings(es2);
}
@ -269,13 +206,12 @@ void backToString(yyscan_t scanner);
void backToIndString(yyscan_t scanner);
static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
static Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
{
return makePos(toATerm(data->path),
loc->first_line, loc->first_column);
return Pos(data->path, loc.first_line, loc.first_column);
}
#define CUR_POS makeCurPos(yylocp, data)
#define CUR_POS makeCurPos(*yylocp, data)
}
@ -283,50 +219,43 @@ static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
{
data->error = (format("%1%, at `%2%':%3%:%4%")
% error % data->path % loc->first_line % loc->first_column).str();
data->error = (format("%1%, at %2%")
% error % makeCurPos(*loc, data)).str();
}
/* Make sure that the parse stack is scanned by the ATerm garbage
collector. */
static void * mallocAndProtect(size_t size)
{
void * p = malloc(size);
if (p) ATprotectMemory(p, size);
return p;
}
static void freeAndUnprotect(void * p)
{
ATunprotectMemory(p);
free(p);
}
#define YYMALLOC mallocAndProtect
#define YYFREE freeAndUnprotect
#endif
%}
%union {
ATerm t;
ATermList ts;
struct {
ATermList formals;
bool ellipsis;
} formals;
nix::Expr * e;
nix::ExprList * list;
nix::ExprAttrs * attrs;
nix::Formals * formals;
nix::Formal * formal;
int n;
char * id; // !!! -> Symbol
char * path;
char * uri;
std::vector<nix::Symbol> * ids;
std::vector<nix::Expr *> * string_parts;
}
%type <t> start expr expr_function expr_if expr_op
%type <t> expr_app expr_select expr_simple bind inheritsrc formal
%type <t> pattern pattern2
%type <ts> binds ids attrpath expr_list string_parts ind_string_parts
%type <e> start expr expr_function expr_if expr_op
%type <e> expr_app expr_select expr_simple
%type <list> expr_list
%type <attrs> binds
%type <formals> formals
%token <t> ID INT STR IND_STR PATH URI
%type <formal> formal
%type <ids> ids attrpath
%type <string_parts> string_parts ind_string_parts
%token <id> ID ATTRPATH
%token <e> STR IND_STR
%token <n> INT
%token <path> PATH
%token <uri> URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
%token DOLLAR_CURLY /* == ${ */
%token IND_STRING_OPEN IND_STRING_CLOSE
@ -350,163 +279,172 @@ start: expr { data->result = $1; };
expr: expr_function;
expr_function
: pattern ':' expr_function
{ checkPatternVars(CUR_POS, $1); $$ = makeFunction($1, $3, CUR_POS); }
: ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), false, 0, $3); }
| '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), true, $2, $5); }
| '{' formals '}' '@' ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), true, $2, $7); }
| ID '@' '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), true, $4, $7); }
| ASSERT expr ';' expr_function
{ $$ = makeAssert($2, $4, CUR_POS); }
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
| WITH expr ';' expr_function
{ $$ = makeWith($2, $4, CUR_POS); }
{ $$ = new ExprWith(CUR_POS, $2, $4); }
| LET binds IN expr_function
{ $$ = makeSelect(fixAttrs(true, ATinsert($2, makeBindAttrPath(ATmakeList1(toATerm("<let-body>")), $4, CUR_POS))), toATerm("<let-body>")); }
{ $$ = new ExprLet($2, $4); }
| expr_if
;
expr_if
: IF expr THEN expr ELSE expr
{ $$ = makeIf($2, $4, $6); }
: IF expr THEN expr ELSE expr { $$ = new ExprIf($2, $4, $6); }
| expr_op
;
expr_op
: '!' expr_op %prec NEG { $$ = makeOpNot($2); }
| expr_op EQ expr_op { $$ = makeOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = makeOpNEq($1, $3); }
| expr_op AND expr_op { $$ = makeOpAnd($1, $3); }
| expr_op OR expr_op { $$ = makeOpOr($1, $3); }
| expr_op IMPL expr_op { $$ = makeOpImpl($1, $3); }
| expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); }
| expr_op '~' expr_op { $$ = makeSubPath($1, $3); }
| expr_op '?' ID { $$ = makeOpHasAttr($1, $3); }
| expr_op '+' expr_op { $$ = makeOpPlus($1, $3); }
| expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); }
: '!' expr_op %prec NEG { $$ = new ExprOpNot($2); }
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
| expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); }
| expr_op '?' ID { $$ = new ExprOpHasAttr($1, data->symbols.create($3)); }
| expr_op '+' expr_op
{ vector<Expr *> * l = new vector<Expr *>;
l->push_back($1);
l->push_back($3);
$$ = new ExprConcatStrings(l);
}
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
| expr_app
;
expr_app
: expr_app expr_select
{ $$ = makeCall($1, $2); }
{ $$ = new ExprApp($1, $2); }
| expr_select { $$ = $1; }
;
expr_select
: expr_select '.' ID
{ $$ = makeSelect($1, $3); }
{ $$ = new ExprSelect($1, data->symbols.create($3)); }
| expr_simple { $$ = $1; }
;
expr_simple
: ID { $$ = makeVar($1); }
| INT { $$ = makeInt(ATgetInt((ATermInt) $1)); }
: ID { $$ = new ExprVar(data->symbols.create($1)); }
| INT { $$ = new ExprInt($1); }
| '"' string_parts '"' {
/* For efficiency, and to simplify parse trees a bit. */
if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty);
else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2);
else $$ = makeConcatStrings(ATreverse($2));
if ($2->empty()) $$ = new ExprString("");
else if ($2->size() == 1) $$ = $2->front();
else $$ = new ExprConcatStrings($2);
}
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(ATreverse($2));
$$ = stripIndentation(*$2);
}
| PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); }
| URI { $$ = makeStr($1, ATempty); }
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
| URI { $$ = new ExprString($1); }
| '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared
into `(rec {..., body = ...}).body'. */
| LET '{' binds '}'
{ $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); }
{ $3->recursive = true; $$ = new ExprSelect($3, data->symbols.create("body")); }
| REC '{' binds '}'
{ $$ = fixAttrs(true, $3); }
{ $3->recursive = true; $$ = $3; }
| '{' binds '}'
{ $$ = fixAttrs(false, $2); }
| '[' expr_list ']' { $$ = makeList(ATreverse($2)); }
{ $$ = $2; }
| '[' expr_list ']' { $$ = $2; }
;
string_parts
: string_parts STR { $$ = ATinsert($1, $2); }
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); }
| { $$ = ATempty; }
: string_parts STR { $$ = $1; $1->push_back($2); }
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
| { $$ = new vector<Expr *>; }
;
ind_string_parts
: ind_string_parts IND_STR { $$ = ATinsert($1, $2); }
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = ATinsert($1, $3); }
| { $$ = ATempty; }
;
pattern
: pattern2 '@' pattern { $$ = makeAtPat($1, $3); }
| pattern2
;
pattern2
: ID { $$ = makeVarPat($1); }
| '{' formals '}' { $$ = makeAttrsPat($2.formals, $2.ellipsis ? eTrue : eFalse); }
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = $1; $1->push_back($3); }
| { $$ = new vector<Expr *>; }
;
binds
: binds bind { $$ = ATinsert($1, $2); }
| { $$ = ATempty; }
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
| binds INHERIT ids ';'
{ $$ = $1;
foreach (vector<Symbol>::iterator, i, *$3) {
if ($$->attrNames.find(*i) != $$->attrNames.end())
dupAttr(*i, makeCurPos(@3, data), $$->attrNames[*i]);
Pos pos = makeCurPos(@3, data);
$$->inherited.push_back(ExprAttrs::Inherited(*i, pos));
$$->attrNames[*i] = pos;
}
}
| binds INHERIT '(' expr ')' ids ';'
{ $$ = $1;
/* !!! Should ensure sharing of the expression in $4. */
foreach (vector<Symbol>::iterator, i, *$6) {
if ($$->attrNames.find(*i) != $$->attrNames.end())
dupAttr(*i, makeCurPos(@6, data), $$->attrNames[*i]);
$$->attrs[*i] = ExprAttrs::Attr(new ExprSelect($4, *i), makeCurPos(@6, data));
$$->attrNames[*i] = makeCurPos(@6, data);
}}
| { $$ = new ExprAttrs; }
;
bind
: attrpath '=' expr ';'
{ $$ = makeBindAttrPath(ATreverse($1), $3, CUR_POS); }
| INHERIT inheritsrc ids ';'
{ $$ = makeInherit($2, $3, CUR_POS); }
ids
: ids ID { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ }
| { $$ = new vector<Symbol>; }
;
inheritsrc
: '(' expr ')' { $$ = $2; }
| { $$ = makeScope(); }
;
ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; };
attrpath
: attrpath '.' ID { $$ = ATinsert($1, $3); }
| ID { $$ = ATmakeList1($1); }
: attrpath '.' ID { $$ = $1; $1->push_back(data->symbols.create($3)); }
| ID { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); }
;
expr_list
: expr_list expr_select { $$ = ATinsert($1, $2); }
| { $$ = ATempty; }
: expr_list expr_select { $$ = $1; $1->elems.push_back($2); /* !!! dangerous */ }
| { $$ = new ExprList; }
;
formals
: formal ',' formals /* !!! right recursive */
{ $$.formals = ATinsert($3.formals, $1); $$.ellipsis = $3.ellipsis; }
: formal ',' formals
{ $$ = $3; addFormal(CUR_POS, $$, *$1); }
| formal
{ $$.formals = ATinsert(ATempty, $1); $$.ellipsis = false; }
{ $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
|
{ $$.formals = ATempty; $$.ellipsis = false; }
{ $$ = new Formals; $$->ellipsis = false; }
| ELLIPSIS
{ $$.formals = ATempty; $$.ellipsis = true; }
{ $$ = new Formals; $$->ellipsis = true; }
;
formal
: ID { $$ = makeFormal($1, makeNoDefaultValue()); }
| ID '?' expr { $$ = makeFormal($1, makeDefaultValue($3)); }
: ID { $$ = new Formal(data->symbols.create($1), 0); }
| ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); }
;
%%
#include "eval.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <eval.hh>
namespace nix {
static Expr parse(EvalState & state,
const char * text, const Path & path,
const Path & basePath)
static Expr * parse(EvalState & state, const char * text,
const Path & path, const Path & basePath)
{
yyscan_t scanner;
ParseData data;
ParseData data(state.symbols);
data.basePath = basePath;
data.path = path;
@ -518,7 +456,7 @@ static Expr parse(EvalState & state,
if (res) throw ParseError(data.error);
try {
checkVarDefs(state.primOps, data.result);
data.result->bindVars(state.staticBaseEnv);
} catch (Error & e) {
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
}
@ -527,16 +465,10 @@ static Expr parse(EvalState & state,
}
Expr parseExprFromFile(EvalState & state, Path path)
Expr * parseExprFromFile(EvalState & state, Path path)
{
assert(path[0] == '/');
#if 0
/* Perhaps this is already an imploded parse tree? */
Expr e = ATreadFromNamedFile(path.c_str());
if (e) return e;
#endif
/* If `path' is a symlink, follow it. This is so that relative
path references work. */
struct stat st;
@ -558,7 +490,7 @@ Expr parseExprFromFile(EvalState & state, Path path)
}
Expr parseExprFromString(EvalState & state,
Expr * parseExprFromString(EvalState & state,
const string & s, const Path & basePath)
{
return parse(state, s.c_str(), "(string)", basePath);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,81 @@
#ifndef __SYMBOL_TABLE_H
#define __SYMBOL_TABLE_H
#include <map>
#include <tr1/unordered_set>
#include "types.hh"
namespace nix {
/* Symbol table used by the parser and evaluator to represent and look
up identifiers and attribute sets efficiently.
SymbolTable::create() converts a string into a symbol. Symbols
have the property that they can be compared efficiently (using a
pointer equality test), because the symbol table stores only one
copy of each string. */
class Symbol
{
private:
const string * s; // pointer into SymbolTable
Symbol(const string * s) : s(s) { };
friend class SymbolTable;
public:
bool operator == (const Symbol & s2) const
{
return s == s2.s;
}
bool operator != (const Symbol & s2) const
{
return s != s2.s;
}
bool operator < (const Symbol & s2) const
{
return s < s2.s;
}
operator const string & () const
{
return *s;
}
bool empty() const
{
return s->empty();
}
friend std::ostream & operator << (std::ostream & str, const Symbol & sym);
};
inline std::ostream & operator << (std::ostream & str, const Symbol & sym)
{
str << *sym.s;
return str;
}
class SymbolTable
{
private:
typedef std::tr1::unordered_set<string> Symbols;
Symbols symbols;
public:
Symbol create(const string & s)
{
std::pair<Symbols::iterator, bool> res = symbols.insert(s);
return Symbol(&*res.first);
}
unsigned int size() const
{
return symbols.size();
}
};
}
#endif /* !__SYMBOL_TABLE_H */

161
src/libexpr/value-to-xml.cc Normal file
View file

@ -0,0 +1,161 @@
#include "value-to-xml.hh"
#include "xml-writer.hh"
#include "util.hh"
#include <cstdlib>
namespace nix {
static XMLAttrs singletonAttrs(const string & name, const string & value)
{
XMLAttrs attrs;
attrs[name] = value;
return attrs;
}
static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen);
static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos)
{
xmlAttrs["path"] = pos.file;
xmlAttrs["line"] = (format("%1%") % pos.line).str();
xmlAttrs["column"] = (format("%1%") % pos.column).str();
}
static void showAttrs(EvalState & state, bool strict, bool location,
Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
{
StringSet names;
foreach (Bindings::iterator, i, attrs)
names.insert(i->first);
foreach (StringSet::iterator, i, names) {
Attr & a(attrs[state.symbols.create(*i)]);
XMLAttrs xmlAttrs;
xmlAttrs["name"] = *i;
if (location && a.pos != &noPos) posToXML(xmlAttrs, *a.pos);
XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location,
a.value, doc, context, drvsSeen);
}
}
static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
{
checkInterrupt();
if (strict) state.forceValue(v);
switch (v.type) {
case tInt:
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str()));
break;
case tBool:
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
break;
case tString:
/* !!! show the context? */
doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
break;
case tPath:
doc.writeEmptyElement("path", singletonAttrs("value", v.path));
break;
case tNull:
doc.writeEmptyElement("null");
break;
case tAttrs:
if (state.isDerivation(v)) {
XMLAttrs xmlAttrs;
Bindings::iterator a = v.attrs->find(state.symbols.create("derivation"));
Path drvPath;
a = v.attrs->find(state.sDrvPath);
if (a != v.attrs->end()) {
if (strict) state.forceValue(a->second.value);
if (a->second.value.type == tString)
xmlAttrs["drvPath"] = drvPath = a->second.value.string.s;
}
a = v.attrs->find(state.sOutPath);
if (a != v.attrs->end()) {
if (strict) state.forceValue(a->second.value);
if (a->second.value.type == tString)
xmlAttrs["outPath"] = a->second.value.string.s;
}
XMLOpenElement _(doc, "derivation", xmlAttrs);
if (drvPath != "" && drvsSeen.find(drvPath) == drvsSeen.end()) {
drvsSeen.insert(drvPath);
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
} else
doc.writeEmptyElement("repeated");
}
else {
XMLOpenElement _(doc, "attrs");
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
}
break;
case tList: {
XMLOpenElement _(doc, "list");
for (unsigned int n = 0; n < v.list.length; ++n)
printValueAsXML(state, strict, location, *v.list.elems[n], doc, context, drvsSeen);
break;
}
case tLambda: {
XMLAttrs xmlAttrs;
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
XMLOpenElement _(doc, "function", xmlAttrs);
if (v.lambda.fun->matchAttrs) {
XMLAttrs attrs;
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs);
foreach (Formals::Formals_::iterator, i, v.lambda.fun->formals->formals)
doc.writeEmptyElement("attr", singletonAttrs("name", i->name));
} else
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
break;
}
default:
doc.writeEmptyElement("unevaluated");
}
}
void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context)
{
XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr");
PathSet drvsSeen;
printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
}
}

View file

@ -0,0 +1,17 @@
#ifndef __VALUE_TO_XML_H
#define __VALUE_TO_XML_H
#include <string>
#include <map>
#include "nixexpr.hh"
#include "eval.hh"
namespace nix {
void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context);
}
#endif /* !__VALUE_TO_XML_H */