* 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:
Eelco Dolstra 2011-07-13 12:19:57 +00:00
parent 2b9e29b1c8
commit 0a623a10c7
9 changed files with 70 additions and 22 deletions

View file

@ -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>

View file

@ -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)

View file

@ -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);
} }

View file

@ -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; }

View file

@ -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)

View file

@ -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
}; };

View file

@ -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

View file

@ -0,0 +1 @@
[ 123 "foo" 456 456 "foo" "xyzzy" "xyzzy" true ]

View 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])
]