aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2011-07-06 10:58:17 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2011-07-06 10:58:17 +0000
commit56370378023fc84eb0153b991f4138f6acd011e3 (patch)
tree19034285effef61c373eef092e91f717c4cea020
parent34f4b91820796381d04c6e00ea5e805cf53d25da (diff)
* In the ‘?’ operator, allow attribute paths. For instance, you can
write ‘attrs ? a.b’ to test whether ‘attrs’ has an attribute ‘a’ containing an attribute ‘b’. This is more convenient than ‘attrs ? a && attrs.a ? b’. Slight change in the semantics: it's no longer an error if the left-hand side of ‘?’ is not an attribute set. In that case it just returns false. So, ‘null ? foo’ no longer throws an error.
-rw-r--r--src/libexpr/eval.cc22
-rw-r--r--src/libexpr/nixexpr.cc13
-rw-r--r--src/libexpr/nixexpr.hh10
-rw-r--r--src/libexpr/parser.y26
4 files changed, 47 insertions, 24 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 949f45e78..c307f2a42 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -656,9 +656,25 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
{
- Value vAttrs;
- state.evalAttrs(env, e, vAttrs);
- mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end());
+ Value vTmp;
+ Value * vAttrs = &vTmp;
+
+ state.eval(env, e, vTmp);
+
+ foreach (AttrPath::const_iterator, i, attrPath) {
+ state.forceValue(*vAttrs);
+ Bindings::iterator j;
+ if (vAttrs->type != tAttrs ||
+ (j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end())
+ {
+ mkBool(v, false);
+ return;
+ } else {
+ vAttrs = j->value;
+ }
+ }
+
+ mkBool(v, true);
}
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 147f50853..595761870 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -48,7 +48,7 @@ void ExprSelect::show(std::ostream & str)
void ExprOpHasAttr::show(std::ostream & str)
{
- str << "(" << *e << ") ? " << name;
+ str << "(" << *e << ") ? " << showAttrPath(attrPath);
}
void ExprAttrs::show(std::ostream & str)
@@ -140,6 +140,17 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
}
+string showAttrPath(const AttrPath & attrPath)
+{
+ string s;
+ foreach (AttrPath::const_iterator, i, attrPath) {
+ if (!s.empty()) s += '.';
+ s += *i;
+ }
+ return s;
+}
+
+
Pos noPos;
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 8f976f1de..205f579da 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -40,6 +40,12 @@ struct EvalState;
struct StaticEnv;
+/* An attribute path is a sequence of attribute names. */
+typedef vector<Symbol> AttrPath;
+
+string showAttrPath(const AttrPath & attrPath);
+
+
/* Abstract syntax of Nix expressions. */
struct Expr
@@ -124,8 +130,8 @@ struct ExprSelect : Expr
struct ExprOpHasAttr : Expr
{
Expr * e;
- Symbol name;
- ExprOpHasAttr(Expr * e, const Symbol & name) : e(e), name(name) { };
+ AttrPath attrPath;
+ ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
COMMON_METHODS
};
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index c6d29b6ca..073fac1be 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -61,18 +61,7 @@ using namespace nix;
namespace nix {
-static string showAttrPath(const vector<Symbol> & attrPath)
-{
- string s;
- foreach (vector<Symbol>::const_iterator, i, attrPath) {
- if (!s.empty()) s += '.';
- s += *i;
- }
- return s;
-}
-
-
-static void dupAttr(const vector<Symbol> & attrPath, const Pos & pos, const Pos & prevPos)
+static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
{
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % pos % prevPos);
@@ -81,17 +70,17 @@ static void dupAttr(const vector<Symbol> & attrPath, const Pos & pos, const Pos
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{
- vector<Symbol> attrPath; attrPath.push_back(attr);
+ AttrPath attrPath; attrPath.push_back(attr);
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % pos % prevPos);
}
-static void addAttr(ExprAttrs * attrs, const vector<Symbol> & attrPath,
+static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
Expr * e, const Pos & pos)
{
unsigned int n = 0;
- foreach (vector<Symbol>::const_iterator, i, attrPath) {
+ foreach (AttrPath::const_iterator, i, attrPath) {
n++;
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*i);
if (j != attrs->attrs.end()) {
@@ -238,6 +227,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%}
%union {
+ // !!! We're probably leaking stuff here.
nix::Expr * e;
nix::ExprList * list;
nix::ExprAttrs * attrs;
@@ -317,7 +307,7 @@ expr_op
| 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 '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
| expr_op '+' expr_op
{ vector<Expr *> * l = new vector<Expr *>;
l->push_back($1);
@@ -382,7 +372,7 @@ binds
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
| binds INHERIT ids ';'
{ $$ = $1;
- foreach (vector<Symbol>::iterator, i, *$3) {
+ foreach (AttrPath::iterator, i, *$3) {
if ($$->attrs.find(*i) != $$->attrs.end())
dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos);
Pos pos = makeCurPos(@3, data);
@@ -392,7 +382,7 @@ binds
| binds INHERIT '(' expr ')' ids ';'
{ $$ = $1;
/* !!! Should ensure sharing of the expression in $4. */
- foreach (vector<Symbol>::iterator, i, *$6) {
+ foreach (AttrPath::iterator, i, *$6) {
if ($$->attrs.find(*i) != $$->attrs.end())
dupAttr(*i, makeCurPos(@6, data), $$->attrs[*i].pos);
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprSelect($4, *i), makeCurPos(@6, data));