From 56370378023fc84eb0153b991f4138f6acd011e3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Jul 2011 10:58:17 +0000 Subject: [PATCH] =?UTF-8?q?*=20In=20the=20=E2=80=98=3F=E2=80=99=20operator?= =?UTF-8?q?,=20allow=20attribute=20paths.=20=20For=20instance,=20you=20can?= =?UTF-8?q?=20=20=20write=20=E2=80=98attrs=20=3F=20a.b=E2=80=99=20to=20tes?= =?UTF-8?q?t=20whether=20=E2=80=98attrs=E2=80=99=20has=20an=20attribute=20?= =?UTF-8?q?=E2=80=98a=E2=80=99=20=20=20containing=20an=20attribute=20?= =?UTF-8?q?=E2=80=98b=E2=80=99.=20=20This=20is=20more=20convenient=20than?= =?UTF-8?q?=20=E2=80=98attrs=20=3F=20=20=20a=20&&=20attrs.a=20=3F=20b?= =?UTF-8?q?=E2=80=99.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/libexpr/eval.cc | 22 +++++++++++++++++++--- src/libexpr/nixexpr.cc | 13 ++++++++++++- src/libexpr/nixexpr.hh | 10 ++++++++-- src/libexpr/parser.y | 26 ++++++++------------------ 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 949f45e787..c307f2a420 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 147f50853e..5957618702 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 8f976f1de8..205f579daf 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 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 c6d29b6ca8..073fac1bee 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 & attrPath) -{ - string s; - foreach (vector::const_iterator, i, attrPath) { - if (!s.empty()) s += '.'; - s += *i; - } - return s; -} - - -static void dupAttr(const vector & 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 & attrPath, const Pos & pos, const Pos static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos) { - vector 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 & attrPath, +static void addAttr(ExprAttrs * attrs, AttrPath & attrPath, Expr * e, const Pos & pos) { unsigned int n = 0; - foreach (vector::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 * l = new vector; 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::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::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));