* Verify that all variables in a Nix expression are defined.
This commit is contained in:
		
							parent
							
								
									1c9c0a5a46
								
							
						
					
					
						commit
						c4f7ae4aa5
					
				
					 4 changed files with 92 additions and 21 deletions
				
			
		|  | @ -64,30 +64,30 @@ ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds) | |||
|     /* Create the substitution list. */ | ||||
|     ATermMap subs; | ||||
|     for (ATermIterator i(rbnds); i; ++i) { | ||||
|         string s; | ||||
|         ATerm name; | ||||
|         Expr e2; | ||||
|         if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) | ||||
|         if (!(atMatch(m, *i) >> "Bind" >> name >> e2)) | ||||
|             abort(); /* can't happen */ | ||||
|         subs.set(s, ATmake("Select(<term>, <str>)", e, s.c_str())); | ||||
|         subs.set(name, ATmake("Select(<term>, <term>)", e, name)); | ||||
|     } | ||||
| 
 | ||||
|     /* Create the non-recursive set. */ | ||||
|     ATermMap as; | ||||
|     for (ATermIterator i(rbnds); i; ++i) { | ||||
|         string s; | ||||
|         ATerm name; | ||||
|         Expr e2; | ||||
|         if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) | ||||
|         if (!(atMatch(m, *i) >> "Bind" >> name >> e2)) | ||||
|             abort(); /* can't happen */ | ||||
|         as.set(s, substitute(subs, e2)); | ||||
|         as.set(name, substitute(subs, e2)); | ||||
|     } | ||||
| 
 | ||||
|     /* Copy the non-recursive bindings.  !!! inefficient */ | ||||
|     for (ATermIterator i(nrbnds); i; ++i) { | ||||
|         string s; | ||||
|         ATerm name; | ||||
|         Expr e2; | ||||
|         if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) | ||||
|         if (!(atMatch(m, *i) >> "Bind" >> name >> e2)) | ||||
|             abort(); /* can't happen */ | ||||
|         as.set(s, e2); | ||||
|         as.set(name, e2); | ||||
|     } | ||||
| 
 | ||||
|     return makeAttrs(as); | ||||
|  |  | |||
|  | @ -156,10 +156,10 @@ Expr substitute(const ATermMap & subs, Expr e) | |||
|     checkInterrupt(); | ||||
| 
 | ||||
|     ATMatcher m; | ||||
|     string s; | ||||
|     ATerm name; | ||||
| 
 | ||||
|     if (atMatch(m, e) >> "Var" >> s) { | ||||
|         Expr sub = subs.get(s); | ||||
|     if (atMatch(m, e) >> "Var" >> name) { | ||||
|         Expr sub = subs.get(name); | ||||
|         return sub ? sub : e; | ||||
|     } | ||||
| 
 | ||||
|  | @ -170,11 +170,10 @@ Expr substitute(const ATermMap & subs, Expr e) | |||
|     if (atMatch(m, e) >> "Function" >> formals >> body) { | ||||
|         ATermMap subs2(subs); | ||||
|         for (ATermIterator i(formals); i; ++i) { | ||||
|             Expr def; | ||||
|             if (!(atMatch(m, *i) >> "NoDefFormal" >> s) && | ||||
|                 !(atMatch(m, *i) >> "DefFormal" >> s >> def)) | ||||
|             if (!(atMatch(m, *i) >> "NoDefFormal" >> name) && | ||||
|                 !(atMatch(m, *i) >> "DefFormal" >> name)) | ||||
|                 abort(); | ||||
|             subs2.remove(s); | ||||
|             subs2.remove(name); | ||||
|         } | ||||
|         return ATmake("Function(<term>, <term>)", formals, | ||||
|             substitute(subs2, body)); | ||||
|  | @ -184,12 +183,11 @@ Expr substitute(const ATermMap & subs, Expr e) | |||
|     ATermList rbnds, nrbnds; | ||||
|     if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds) { | ||||
|         ATermMap subs2(subs); | ||||
|         for (ATermIterator i(rbnds); i; ++i) { | ||||
|             Expr e; | ||||
|             if (!(atMatch(m, *i) >> "Bind" >> s >> e)) | ||||
|         for (ATermIterator i(rbnds); i; ++i) | ||||
|             if (atMatch(m, *i) >> "Bind" >> name) | ||||
|                 subs2.remove(name); | ||||
|             else | ||||
|                 abort(); /* can't happen */ | ||||
|             subs2.remove(s); | ||||
|         } | ||||
|         return ATmake("Rec(<term>, <term>)", | ||||
|             substitute(subs2, (ATerm) rbnds), | ||||
|             substitute(subs, (ATerm) nrbnds)); | ||||
|  | @ -217,6 +215,59 @@ Expr substitute(const ATermMap & subs, Expr e) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| void checkVarDefs(const ATermMap & defs, Expr e) | ||||
| { | ||||
|     ATMatcher m; | ||||
|     ATerm name; | ||||
|     ATermList formals; | ||||
|     ATerm body; | ||||
|     ATermList rbnds, nrbnds; | ||||
| 
 | ||||
|     if (atMatch(m, e) >> "Var" >> name) { | ||||
|         if (!defs.get(name)) | ||||
|             throw Error(format("undefined variable `%1%'") | ||||
|                 % aterm2String(name)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     else if (atMatch(m, e) >> "Function" >> formals >> body) { | ||||
|         ATermMap defs2(defs); | ||||
|         for (ATermIterator i(formals); i; ++i) { | ||||
|             Expr deflt; | ||||
|             if (!(atMatch(m, *i) >> "NoDefFormal" >> name)) | ||||
|                 if (atMatch(m, *i) >> "DefFormal" >> name >> deflt) | ||||
|                     checkVarDefs(defs, deflt); | ||||
|                 else | ||||
|                     abort(); | ||||
|             defs2.set(name, (ATerm) ATempty); | ||||
|         } | ||||
|         return checkVarDefs(defs2, body); | ||||
|     } | ||||
|          | ||||
|     else if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds) { | ||||
|         checkVarDefs(defs | ||||
|             , (ATerm) nrbnds); | ||||
|         ATermMap defs2(defs); | ||||
|         for (ATermIterator i(rbnds); i; ++i) { | ||||
|             if (!(atMatch(m, *i) >> "Bind" >> name)) | ||||
|                 abort(); /* can't happen */ | ||||
|             defs2.set(name, (ATerm) ATempty); | ||||
|         } | ||||
|         checkVarDefs(defs2, (ATerm) rbnds); | ||||
|     } | ||||
|      | ||||
|     else if (ATgetType(e) == AT_APPL) { | ||||
|         int arity = ATgetArity(ATgetAFun(e)); | ||||
|         for (int i = 0; i < arity; ++i) | ||||
|             checkVarDefs(defs, ATgetArgument(e, i)); | ||||
|     } | ||||
| 
 | ||||
|     else if (ATgetType(e) == AT_LIST) | ||||
|         for (ATermIterator i((ATermList) e); i; ++i) | ||||
|             checkVarDefs(defs, *i); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Expr makeBool(bool b) | ||||
| { | ||||
|     return b ? ATmake("Bool(True)") : ATmake("Bool(False)"); | ||||
|  |  | |||
|  | @ -68,6 +68,10 @@ Expr makeAttrs(const ATermMap & attrs); | |||
| /* Perform a set of substitutions on an expression. */ | ||||
| Expr substitute(const ATermMap & subs, Expr e); | ||||
| 
 | ||||
| /* Check whether all variables are defined in the given expression.
 | ||||
|    Throw an exception if this isn't the case. */ | ||||
| void checkVarDefs(const ATermMap & def, Expr e); | ||||
| 
 | ||||
| /* Create an expression representing a boolean. */ | ||||
| Expr makeBool(bool b); | ||||
| 
 | ||||
|  |  | |||
|  | @ -81,6 +81,22 @@ static Expr parse(const char * text, const string & location, | |||
|      | ||||
|     if (res) throw Error(data.error); | ||||
| 
 | ||||
|     ATermMap primOps; | ||||
|     primOps.set("import", (ATerm) ATempty); | ||||
|     primOps.set("derivation", (ATerm) ATempty); | ||||
|     primOps.set("true", (ATerm) ATempty); | ||||
|     primOps.set("false", (ATerm) ATempty); | ||||
|     primOps.set("null", (ATerm) ATempty); | ||||
|     primOps.set("isNull", (ATerm) ATempty); | ||||
|     primOps.set("toString", (ATerm) ATempty); | ||||
|     primOps.set("baseNameOf", (ATerm) ATempty); | ||||
| 
 | ||||
|     try { | ||||
|         checkVarDefs(primOps, data.result); | ||||
|     } catch (Error & e) { | ||||
|         throw Error(format("%1%, in %2%") % e.msg() % location); | ||||
|     } | ||||
| 
 | ||||
|     return data.result; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue