forked from lix-project/lix
* 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.
This commit is contained in:
parent
34f4b91820
commit
5637037802
|
@ -656,9 +656,25 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value vAttrs;
|
Value vTmp;
|
||||||
state.evalAttrs(env, e, vAttrs);
|
Value * vAttrs = &vTmp;
|
||||||
mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end());
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ void ExprSelect::show(std::ostream & str)
|
||||||
|
|
||||||
void ExprOpHasAttr::show(std::ostream & str)
|
void ExprOpHasAttr::show(std::ostream & str)
|
||||||
{
|
{
|
||||||
str << "(" << *e << ") ? " << name;
|
str << "(" << *e << ") ? " << showAttrPath(attrPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAttrs::show(std::ostream & str)
|
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;
|
Pos noPos;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,12 @@ struct EvalState;
|
||||||
struct StaticEnv;
|
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. */
|
/* Abstract syntax of Nix expressions. */
|
||||||
|
|
||||||
struct Expr
|
struct Expr
|
||||||
|
@ -124,8 +130,8 @@ struct ExprSelect : Expr
|
||||||
struct ExprOpHasAttr : Expr
|
struct ExprOpHasAttr : Expr
|
||||||
{
|
{
|
||||||
Expr * e;
|
Expr * e;
|
||||||
Symbol name;
|
AttrPath attrPath;
|
||||||
ExprOpHasAttr(Expr * e, const Symbol & name) : e(e), name(name) { };
|
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -61,18 +61,7 @@ using namespace nix;
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
static string showAttrPath(const vector<Symbol> & attrPath)
|
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
||||||
% showAttrPath(attrPath) % pos % prevPos);
|
% 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)
|
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%")
|
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
||||||
% showAttrPath(attrPath) % pos % prevPos);
|
% 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)
|
Expr * e, const Pos & pos)
|
||||||
{
|
{
|
||||||
unsigned int n = 0;
|
unsigned int n = 0;
|
||||||
foreach (vector<Symbol>::const_iterator, i, attrPath) {
|
foreach (AttrPath::const_iterator, i, attrPath) {
|
||||||
n++;
|
n++;
|
||||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*i);
|
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*i);
|
||||||
if (j != attrs->attrs.end()) {
|
if (j != attrs->attrs.end()) {
|
||||||
|
@ -238,6 +227,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%union {
|
%union {
|
||||||
|
// !!! We're probably leaking stuff here.
|
||||||
nix::Expr * e;
|
nix::Expr * e;
|
||||||
nix::ExprList * list;
|
nix::ExprList * list;
|
||||||
nix::ExprAttrs * attrs;
|
nix::ExprAttrs * attrs;
|
||||||
|
@ -317,7 +307,7 @@ expr_op
|
||||||
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
|
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
|
||||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
|
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
|
||||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate($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
|
| expr_op '+' expr_op
|
||||||
{ vector<Expr *> * l = new vector<Expr *>;
|
{ vector<Expr *> * l = new vector<Expr *>;
|
||||||
l->push_back($1);
|
l->push_back($1);
|
||||||
|
@ -382,7 +372,7 @@ binds
|
||||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
|
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
|
||||||
| binds INHERIT ids ';'
|
| binds INHERIT ids ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
foreach (vector<Symbol>::iterator, i, *$3) {
|
foreach (AttrPath::iterator, i, *$3) {
|
||||||
if ($$->attrs.find(*i) != $$->attrs.end())
|
if ($$->attrs.find(*i) != $$->attrs.end())
|
||||||
dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos);
|
dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos);
|
||||||
Pos pos = makeCurPos(@3, data);
|
Pos pos = makeCurPos(@3, data);
|
||||||
|
@ -392,7 +382,7 @@ binds
|
||||||
| binds INHERIT '(' expr ')' ids ';'
|
| binds INHERIT '(' expr ')' ids ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
/* !!! Should ensure sharing of the expression in $4. */
|
/* !!! 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())
|
if ($$->attrs.find(*i) != $$->attrs.end())
|
||||||
dupAttr(*i, makeCurPos(@6, data), $$->attrs[*i].pos);
|
dupAttr(*i, makeCurPos(@6, data), $$->attrs[*i].pos);
|
||||||
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprSelect($4, *i), makeCurPos(@6, data));
|
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprSelect($4, *i), makeCurPos(@6, data));
|
||||||
|
|
Loading…
Reference in a new issue