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:
commit
aa45027818
109 changed files with 3350 additions and 4010 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
1628
src/libexpr/eval.cc
1628
src/libexpr/eval.cc
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
@ -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>";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
81
src/libexpr/symbol-table.hh
Normal file
81
src/libexpr/symbol-table.hh
Normal 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
161
src/libexpr/value-to-xml.cc
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
17
src/libexpr/value-to-xml.hh
Normal file
17
src/libexpr/value-to-xml.hh
Normal 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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue