* 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 (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()) |                 if ((j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end()) | ||||||
|                     throwEvalError("attribute `%1%' missing", showAttrPath(attrPath)); |                     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…
	
	Add table
		Add a link
		
	
		Reference in a new issue