* Allow a default value in attribute selection by writing
x.y.z or default (as originally proposed in https://mail.cs.uu.nl/pipermail/nix-dev/2009-September/002989.html). For instance, an expression like stdenv.lib.attrByPath ["features" "ckSched"] false args can now be written as args.features.ckSched or false
This commit is contained in:
parent
2b9e29b1c8
commit
0a623a10c7
9 changed files with 70 additions and 22 deletions
|
@ -32,6 +32,10 @@
|
||||||
<literal>--max-silent-time</literal> is ineffective.</para>
|
<literal>--max-silent-time</literal> is ineffective.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>TODO: “or” keyword.</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -67,7 +67,7 @@ The hook `nix-mode-hook' is run when Nix mode is started.
|
||||||
|
|
||||||
(defvar nix-keywords
|
(defvar nix-keywords
|
||||||
'("\\<if\\>" "\\<then\\>" "\\<else\\>" "\\<assert\\>" "\\<with\\>"
|
'("\\<if\\>" "\\<then\\>" "\\<else\\>" "\\<assert\\>" "\\<with\\>"
|
||||||
"\\<let\\>" "\\<in\\>" "\\<rec\\>" "\\<inherit\\>"
|
"\\<let\\>" "\\<in\\>" "\\<rec\\>" "\\<inherit\\>" "\\<or\\>"
|
||||||
("\\<true\\>" . font-lock-builtin-face)
|
("\\<true\\>" . font-lock-builtin-face)
|
||||||
("\\<false\\>" . font-lock-builtin-face)
|
("\\<false\\>" . font-lock-builtin-face)
|
||||||
("\\<null\\>" . font-lock-builtin-face)
|
("\\<null\\>" . font-lock-builtin-face)
|
||||||
|
|
|
@ -632,7 +632,6 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
|
|
||||||
unsigned long nrLookups = 0;
|
unsigned long nrLookups = 0;
|
||||||
unsigned long nrLookupSize = 0;
|
|
||||||
|
|
||||||
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
@ -646,11 +645,20 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
foreach (AttrPath::const_iterator, i, attrPath) {
|
foreach (AttrPath::const_iterator, i, attrPath) {
|
||||||
nrLookups++;
|
nrLookups++;
|
||||||
state.forceAttrs(*vAttrs);
|
|
||||||
nrLookupSize += vAttrs->attrs->size();
|
|
||||||
Bindings::iterator j;
|
Bindings::iterator j;
|
||||||
if ((j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end())
|
if (def) {
|
||||||
throwEvalError("attribute `%1%' missing", showAttrPath(attrPath));
|
state.forceValue(*vAttrs);
|
||||||
|
if (vAttrs->type != tAttrs ||
|
||||||
|
(j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end())
|
||||||
|
{
|
||||||
|
state.eval(env, def, v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.forceAttrs(*vAttrs);
|
||||||
|
if ((j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end())
|
||||||
|
throwEvalError("attribute `%1%' missing", showAttrPath(attrPath));
|
||||||
|
}
|
||||||
vAttrs = j->value;
|
vAttrs = j->value;
|
||||||
pos = j->pos;
|
pos = j->pos;
|
||||||
}
|
}
|
||||||
|
@ -1270,7 +1278,6 @@ void EvalState::printStats()
|
||||||
printMsg(v, format(" number of thunks: %1%") % nrThunks);
|
printMsg(v, format(" number of thunks: %1%") % nrThunks);
|
||||||
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
|
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
|
||||||
printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
|
printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
|
||||||
printMsg(v, format(" attr lookup size: %1%") % nrLookupSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,7 @@ let { return LET; }
|
||||||
in { return IN; }
|
in { return IN; }
|
||||||
rec { return REC; }
|
rec { return REC; }
|
||||||
inherit { return INHERIT; }
|
inherit { return INHERIT; }
|
||||||
|
or { return OR_KW; }
|
||||||
\.\.\. { return ELLIPSIS; }
|
\.\.\. { return ELLIPSIS; }
|
||||||
|
|
||||||
\=\= { return EQ; }
|
\=\= { return EQ; }
|
||||||
|
|
|
@ -44,6 +44,7 @@ void ExprVar::show(std::ostream & str)
|
||||||
void ExprSelect::show(std::ostream & str)
|
void ExprSelect::show(std::ostream & str)
|
||||||
{
|
{
|
||||||
str << "(" << *e << ")." << showAttrPath(attrPath);
|
str << "(" << *e << ")." << showAttrPath(attrPath);
|
||||||
|
if (def) str << " or " << *def;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpHasAttr::show(std::ostream & str)
|
void ExprOpHasAttr::show(std::ostream & str)
|
||||||
|
@ -211,6 +212,7 @@ void ExprVar::bindVars(const StaticEnv & env)
|
||||||
void ExprSelect::bindVars(const StaticEnv & env)
|
void ExprSelect::bindVars(const StaticEnv & env)
|
||||||
{
|
{
|
||||||
e->bindVars(env);
|
e->bindVars(env);
|
||||||
|
if (def) def->bindVars(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpHasAttr::bindVars(const StaticEnv & env)
|
void ExprOpHasAttr::bindVars(const StaticEnv & env)
|
||||||
|
|
|
@ -121,10 +121,10 @@ struct ExprVar : Expr
|
||||||
|
|
||||||
struct ExprSelect : Expr
|
struct ExprSelect : Expr
|
||||||
{
|
{
|
||||||
Expr * e;
|
Expr * e, * def;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprSelect(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
|
ExprSelect(Expr * e, const AttrPath & attrPath, Expr * def) : e(e), def(def), attrPath(attrPath) { };
|
||||||
ExprSelect(Expr * e, const Symbol & name) : e(e) { attrPath.push_back(name); };
|
ExprSelect(Expr * e, const Symbol & name) : e(e), def(0) { attrPath.push_back(name); };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -237,7 +237,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
char * id; // !!! -> Symbol
|
char * id; // !!! -> Symbol
|
||||||
char * path;
|
char * path;
|
||||||
char * uri;
|
char * uri;
|
||||||
std::vector<nix::Symbol> * ids;
|
std::vector<nix::Symbol> * attrNames;
|
||||||
std::vector<nix::Expr *> * string_parts;
|
std::vector<nix::Expr *> * string_parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,14 +247,15 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
%type <attrs> binds
|
%type <attrs> binds
|
||||||
%type <formals> formals
|
%type <formals> formals
|
||||||
%type <formal> formal
|
%type <formal> formal
|
||||||
%type <ids> ids attrpath
|
%type <attrNames> attrs attrpath
|
||||||
%type <string_parts> string_parts ind_string_parts
|
%type <string_parts> string_parts ind_string_parts
|
||||||
|
%type <id> attr
|
||||||
%token <id> ID ATTRPATH
|
%token <id> ID ATTRPATH
|
||||||
%token <e> STR IND_STR
|
%token <e> STR IND_STR
|
||||||
%token <n> INT
|
%token <n> INT
|
||||||
%token <path> PATH
|
%token <path> PATH
|
||||||
%token <uri> URI
|
%token <uri> URI
|
||||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
|
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
|
||||||
%token DOLLAR_CURLY /* == ${ */
|
%token DOLLAR_CURLY /* == ${ */
|
||||||
%token IND_STRING_OPEN IND_STRING_CLOSE
|
%token IND_STRING_OPEN IND_STRING_CLOSE
|
||||||
%token ELLIPSIS
|
%token ELLIPSIS
|
||||||
|
@ -326,7 +327,13 @@ expr_app
|
||||||
|
|
||||||
expr_select
|
expr_select
|
||||||
: expr_simple '.' attrpath
|
: expr_simple '.' attrpath
|
||||||
{ $$ = new ExprSelect($1, *$3); }
|
{ $$ = new ExprSelect($1, *$3, 0); }
|
||||||
|
| expr_simple '.' attrpath OR_KW expr_select
|
||||||
|
{ $$ = new ExprSelect($1, *$3, $5); }
|
||||||
|
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
||||||
|
function named ‘or’, allow stuff like ‘map or [...]’. */
|
||||||
|
expr_simple OR_KW
|
||||||
|
{ $$ = new ExprApp($1, new ExprVar(data->symbols.create("or"))); }
|
||||||
| expr_simple { $$ = $1; }
|
| expr_simple { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -370,7 +377,7 @@ ind_string_parts
|
||||||
|
|
||||||
binds
|
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 attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
foreach (AttrPath::iterator, i, *$3) {
|
foreach (AttrPath::iterator, i, *$3) {
|
||||||
if ($$->attrs.find(*i) != $$->attrs.end())
|
if ($$->attrs.find(*i) != $$->attrs.end())
|
||||||
|
@ -379,26 +386,31 @@ binds
|
||||||
$$->attrs[*i] = ExprAttrs::AttrDef(*i, pos);
|
$$->attrs[*i] = ExprAttrs::AttrDef(*i, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| binds INHERIT '(' expr ')' ids ';'
|
| binds INHERIT '(' expr ')' attrs ';'
|
||||||
{ $$ = $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 (vector<Symbol>::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));
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
| { $$ = new ExprAttrs; }
|
| { $$ = new ExprAttrs; }
|
||||||
;
|
;
|
||||||
|
|
||||||
ids
|
attrs
|
||||||
: ids ID { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ }
|
: attrs attr { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ }
|
||||||
| { $$ = new vector<Symbol>; }
|
| { $$ = new vector<Symbol>; }
|
||||||
;
|
;
|
||||||
|
|
||||||
attrpath
|
attrpath
|
||||||
: attrpath '.' ID { $$ = $1; $1->push_back(data->symbols.create($3)); }
|
: attrpath '.' attr { $$ = $1; $1->push_back(data->symbols.create($3)); }
|
||||||
| ID { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); }
|
| attr { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
attr
|
||||||
|
: ID { $$ = $1; }
|
||||||
|
| OR_KW { $$ = "or"; }
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_list
|
expr_list
|
||||||
|
|
1
tests/lang/eval-okay-attrs5.exp
Normal file
1
tests/lang/eval-okay-attrs5.exp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[ 123 "foo" 456 456 "foo" "xyzzy" "xyzzy" true ]
|
21
tests/lang/eval-okay-attrs5.nix
Normal file
21
tests/lang/eval-okay-attrs5.nix
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
with import ./lib.nix;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
as = { x.y.z = 123; a.b.c = 456; };
|
||||||
|
|
||||||
|
bs = { foo.bar = "foo"; };
|
||||||
|
|
||||||
|
or = x: y: x || y;
|
||||||
|
|
||||||
|
in
|
||||||
|
[ as.x.y.z
|
||||||
|
as.foo or "foo"
|
||||||
|
as.x.y.bla or as.a.b.c
|
||||||
|
as.a.b.c or as.x.y.z
|
||||||
|
as.x.y.bla or bs.foo.bar or "xyzzy"
|
||||||
|
as.x.y.bla or bs.bar.foo or "xyzzy"
|
||||||
|
123.bla or null.foo or "xyzzy"
|
||||||
|
# Backwards compatibility test.
|
||||||
|
(fold or [] [true false false])
|
||||||
|
]
|
Loading…
Reference in a new issue