diff options
-rw-r--r-- | doc/manual/release-notes.xml | 4 | ||||
-rw-r--r-- | misc/emacs/nix-mode.el | 2 | ||||
-rw-r--r-- | src/libexpr/eval.cc | 19 | ||||
-rw-r--r-- | src/libexpr/lexer.l | 1 | ||||
-rw-r--r-- | src/libexpr/nixexpr.cc | 2 | ||||
-rw-r--r-- | src/libexpr/nixexpr.hh | 6 | ||||
-rw-r--r-- | src/libexpr/parser.y | 36 | ||||
-rw-r--r-- | tests/lang/eval-okay-attrs5.exp | 1 | ||||
-rw-r--r-- | tests/lang/eval-okay-attrs5.nix | 21 |
9 files changed, 70 insertions, 22 deletions
diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml index 0c29cae90f..cf025aaf51 100644 --- a/doc/manual/release-notes.xml +++ b/doc/manual/release-notes.xml @@ -32,6 +32,10 @@ <literal>--max-silent-time</literal> is ineffective.</para> </listitem> + <listitem> + <para>TODO: “or” keyword.</para> + </listitem> + </itemizedlist> </section> diff --git a/misc/emacs/nix-mode.el b/misc/emacs/nix-mode.el index 22e735b893..306d478bd8 100644 --- a/misc/emacs/nix-mode.el +++ b/misc/emacs/nix-mode.el @@ -67,7 +67,7 @@ The hook `nix-mode-hook' is run when Nix mode is started. (defvar nix-keywords '("\\<if\\>" "\\<then\\>" "\\<else\\>" "\\<assert\\>" "\\<with\\>" - "\\<let\\>" "\\<in\\>" "\\<rec\\>" "\\<inherit\\>" + "\\<let\\>" "\\<in\\>" "\\<rec\\>" "\\<inherit\\>" "\\<or\\>" ("\\<true\\>" . font-lock-builtin-face) ("\\<false\\>" . font-lock-builtin-face) ("\\<null\\>" . font-lock-builtin-face) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 4d3a369aa1..acf5d7a8ae 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -632,7 +632,6 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v) unsigned long nrLookups = 0; -unsigned long nrLookupSize = 0; 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) { nrLookups++; - state.forceAttrs(*vAttrs); - nrLookupSize += vAttrs->attrs->size(); Bindings::iterator j; - if ((j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end()) - throwEvalError("attribute `%1%' missing", showAttrPath(attrPath)); + if (def) { + 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; pos = j->pos; } @@ -1270,7 +1278,6 @@ void EvalState::printStats() printMsg(v, format(" number of thunks: %1%") % nrThunks); printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided); printMsg(v, format(" number of attr lookups: %1%") % nrLookups); - printMsg(v, format(" attr lookup size: %1%") % nrLookupSize); } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 5b27e2582d..330c2bd54d 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -96,6 +96,7 @@ let { return LET; } in { return IN; } rec { return REC; } inherit { return INHERIT; } +or { return OR_KW; } \.\.\. { return ELLIPSIS; } \=\= { return EQ; } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 2632d9f3ff..c52a413916 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -44,6 +44,7 @@ void ExprVar::show(std::ostream & str) void ExprSelect::show(std::ostream & str) { str << "(" << *e << ")." << showAttrPath(attrPath); + if (def) str << " or " << *def; } void ExprOpHasAttr::show(std::ostream & str) @@ -211,6 +212,7 @@ void ExprVar::bindVars(const StaticEnv & env) void ExprSelect::bindVars(const StaticEnv & env) { e->bindVars(env); + if (def) def->bindVars(env); } void ExprOpHasAttr::bindVars(const StaticEnv & env) diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 725e30c6fa..166289b298 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -121,10 +121,10 @@ struct ExprVar : Expr struct ExprSelect : Expr { - Expr * e; + Expr * e, * def; AttrPath attrPath; - ExprSelect(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { }; - ExprSelect(Expr * e, const Symbol & name) : e(e) { attrPath.push_back(name); }; + ExprSelect(Expr * e, const AttrPath & attrPath, Expr * def) : e(e), def(def), attrPath(attrPath) { }; + ExprSelect(Expr * e, const Symbol & name) : e(e), def(0) { attrPath.push_back(name); }; COMMON_METHODS }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 49bd7bfa5f..ec194a516a 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -237,7 +237,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err char * id; // !!! -> Symbol char * path; char * uri; - std::vector<nix::Symbol> * ids; + std::vector<nix::Symbol> * attrNames; 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 <formals> formals %type <formal> formal -%type <ids> ids attrpath +%type <attrNames> attrs attrpath %type <string_parts> string_parts ind_string_parts +%type <id> attr %token <id> ID ATTRPATH %token <e> STR IND_STR %token <n> INT %token <path> PATH %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 IND_STRING_OPEN IND_STRING_CLOSE %token ELLIPSIS @@ -326,7 +327,13 @@ expr_app expr_select : 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; } ; @@ -370,7 +377,7 @@ ind_string_parts binds : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); } - | binds INHERIT ids ';' + | binds INHERIT attrs ';' { $$ = $1; foreach (AttrPath::iterator, i, *$3) { if ($$->attrs.find(*i) != $$->attrs.end()) @@ -379,26 +386,31 @@ binds $$->attrs[*i] = ExprAttrs::AttrDef(*i, pos); } } - | binds INHERIT '(' expr ')' ids ';' + | binds INHERIT '(' expr ')' attrs ';' { $$ = $1; /* !!! Should ensure sharing of the expression in $4. */ foreach (vector<Symbol>::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)); - }} - + } + } | { $$ = new ExprAttrs; } ; -ids - : ids ID { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ } +attrs + : attrs attr { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ } | { $$ = new vector<Symbol>; } ; attrpath - : attrpath '.' ID { $$ = $1; $1->push_back(data->symbols.create($3)); } - | ID { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); } + : attrpath '.' attr { $$ = $1; $1->push_back(data->symbols.create($3)); } + | attr { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); } + ; + +attr + : ID { $$ = $1; } + | OR_KW { $$ = "or"; } ; expr_list diff --git a/tests/lang/eval-okay-attrs5.exp b/tests/lang/eval-okay-attrs5.exp new file mode 100644 index 0000000000..ce0430d780 --- /dev/null +++ b/tests/lang/eval-okay-attrs5.exp @@ -0,0 +1 @@ +[ 123 "foo" 456 456 "foo" "xyzzy" "xyzzy" true ] diff --git a/tests/lang/eval-okay-attrs5.nix b/tests/lang/eval-okay-attrs5.nix new file mode 100644 index 0000000000..e77ee7a1c2 --- /dev/null +++ b/tests/lang/eval-okay-attrs5.nix @@ -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]) + ] |