* Check for duplicate attributes in fixAttrs, rather than doing a
separate traversal after parsing. Likewise, check for duplicate pattern variables right away.
This commit is contained in:
		
							parent
							
								
									50d11b90ca
								
							
						
					
					
						commit
						e42975490f
					
				
					 1 changed files with 63 additions and 94 deletions
				
			
		|  | @ -44,28 +44,77 @@ struct ParseData | ||||||
|     Path path; |     Path path; | ||||||
|     string error; |     string error; | ||||||
| }; | }; | ||||||
|  |   | ||||||
| 
 | 
 | ||||||
|  | static void duplicateAttr(ATerm name, ATerm pos, ATerm prevPos) | ||||||
|  | { | ||||||
|  |     throw EvalError(format("duplicate attribute `%1%' at %2% (previously defined at %3%)") | ||||||
|  |         % aterm2String(name) % showPos(pos) % showPos (prevPos)); | ||||||
|  | } | ||||||
|  |   | ||||||
| 
 | 
 | ||||||
| static Expr fixAttrs(int recursive, ATermList as) | static Expr fixAttrs(bool recursive, ATermList as) | ||||||
| { | { | ||||||
|     ATermList bs = ATempty, cs = ATempty; |     ATermList bs = ATempty, cs = ATempty; | ||||||
|     ATermList * is = recursive ? &cs : &bs; |     ATermList * is = recursive ? &cs : &bs; | ||||||
|  | 
 | ||||||
|  |     ATermMap used; | ||||||
|  |      | ||||||
|     for (ATermIterator i(as); i; ++i) { |     for (ATermIterator i(as); i; ++i) { | ||||||
|         ATermList names; |         ATermList names; Expr src, e; ATerm name, pos; | ||||||
|         Expr src; |  | ||||||
|         ATerm pos; |  | ||||||
|         if (matchInherit(*i, src, names, pos)) { |         if (matchInherit(*i, src, names, pos)) { | ||||||
|             bool fromScope = matchScope(src); |             bool fromScope = matchScope(src); | ||||||
|             for (ATermIterator j(names); j; ++j) { |             for (ATermIterator j(names); j; ++j) { | ||||||
|                 Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j); |                 Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j); | ||||||
|  |                 if (used.get(*j)) duplicateAttr(*j, pos, used[*j]); | ||||||
|  |                 used.set(*j, pos); | ||||||
|                 *is = ATinsert(*is, makeBind(*j, rhs, pos)); |                 *is = ATinsert(*is, makeBind(*j, rhs, pos)); | ||||||
|             } |             } | ||||||
|         } else bs = ATinsert(bs, *i); |         } else if (matchBind(*i, name, e, pos)) { | ||||||
|  |             if (used.get(name)) duplicateAttr(name, pos, used[name]); | ||||||
|  |             used.set(name, pos); | ||||||
|  |             bs = ATinsert(bs, *i); | ||||||
|  |         } else abort(); /* can't happen */ | ||||||
|     } |     } | ||||||
|     if (recursive) | 
 | ||||||
|         return makeRec(bs, cs); |     return recursive? makeRec(bs, cs) : makeAttrs(bs); | ||||||
|     else | } | ||||||
|         return makeAttrs(bs); | 
 | ||||||
|  | 
 | ||||||
|  | static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat) | ||||||
|  | { | ||||||
|  |     ATerm name; | ||||||
|  |     ATermList formals; | ||||||
|  |     Pattern pat1, pat2; | ||||||
|  |     ATermBool ellipsis; | ||||||
|  |     if (matchVarPat(pat, name)) { | ||||||
|  |         if (map.get(name)) | ||||||
|  |             throw EvalError(format("duplicate formal function argument `%1%' at %2%") | ||||||
|  |                 % aterm2String(name) % showPos(pos)); | ||||||
|  |         map.set(name, name); | ||||||
|  |     } | ||||||
|  |     else if (matchAttrsPat(pat, formals, ellipsis)) {  | ||||||
|  |         for (ATermIterator i(formals); i; ++i) { | ||||||
|  |             ATerm d1; | ||||||
|  |             if (!matchFormal(*i, name, d1)) abort(); | ||||||
|  |             if (map.get(name)) | ||||||
|  |                 throw EvalError(format("duplicate formal function argument `%1%' at %2%") | ||||||
|  |                     % aterm2String(name) % showPos(pos)); | ||||||
|  |             map.set(name, name); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (matchAtPat(pat, pat1, pat2)) { | ||||||
|  |         checkPatternVars(pos, map, pat1); | ||||||
|  |         checkPatternVars(pos, map, pat2); | ||||||
|  |     } | ||||||
|  |     else abort(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void checkPatternVars(ATerm pos, Pattern pat) | ||||||
|  | { | ||||||
|  |     ATermMap map; | ||||||
|  |     checkPatternVars(pos, map, pat); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -245,13 +294,13 @@ expr: expr_function; | ||||||
| 
 | 
 | ||||||
| expr_function | expr_function | ||||||
|   : pattern ':' expr_function |   : pattern ':' expr_function | ||||||
|     { $$ = makeFunction($1, $3, CUR_POS); } |     { checkPatternVars(CUR_POS, $1); $$ = makeFunction($1, $3, CUR_POS); } | ||||||
|   | ASSERT expr ';' expr_function |   | ASSERT expr ';' expr_function | ||||||
|     { $$ = makeAssert($2, $4, CUR_POS); } |     { $$ = makeAssert($2, $4, CUR_POS); } | ||||||
|   | WITH expr ';' expr_function |   | WITH expr ';' expr_function | ||||||
|     { $$ = makeWith($2, $4, CUR_POS); } |     { $$ = makeWith($2, $4, CUR_POS); } | ||||||
|   | LET binds IN expr_function |   | LET binds IN expr_function | ||||||
|     { $$ = makeSelect(fixAttrs(1, ATinsert($2, makeBind(toATerm("<let-body>"), $4, CUR_POS))), toATerm("<let-body>")); } |     { $$ = makeSelect(fixAttrs(true, ATinsert($2, makeBind(toATerm("<let-body>"), $4, CUR_POS))), toATerm("<let-body>")); } | ||||||
|   | expr_if |   | expr_if | ||||||
|   ; |   ; | ||||||
| 
 | 
 | ||||||
|  | @ -306,11 +355,11 @@ expr_simple | ||||||
|   /* Let expressions `let {..., body = ...}' are just desugared |   /* Let expressions `let {..., body = ...}' are just desugared | ||||||
|      into `(rec {..., body = ...}).body'. */ |      into `(rec {..., body = ...}).body'. */ | ||||||
|   | LET '{' binds '}' |   | LET '{' binds '}' | ||||||
|     { $$ = makeSelect(fixAttrs(1, $3), toATerm("body")); } |     { $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); } | ||||||
|   | REC '{' binds '}' |   | REC '{' binds '}' | ||||||
|     { $$ = fixAttrs(1, $3); } |     { $$ = fixAttrs(true, $3); } | ||||||
|   | '{' binds '}' |   | '{' binds '}' | ||||||
|     { $$ = fixAttrs(0, $2); } |     { $$ = fixAttrs(false, $2); } | ||||||
|   | '[' expr_list ']' { $$ = makeList(ATreverse($2)); } |   | '[' expr_list ']' { $$ = makeList(ATreverse($2)); } | ||||||
|   ; |   ; | ||||||
| 
 | 
 | ||||||
|  | @ -390,84 +439,6 @@ formal | ||||||
| namespace nix { | namespace nix { | ||||||
|        |        | ||||||
| 
 | 
 | ||||||
| static void checkAttrs(ATermMap & names, ATermList bnds) |  | ||||||
| { |  | ||||||
|     for (ATermIterator i(bnds); i; ++i) { |  | ||||||
|         ATerm name; |  | ||||||
|         Expr e; |  | ||||||
|         ATerm pos; |  | ||||||
|         if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */ |  | ||||||
|         if (names.get(name)) |  | ||||||
|             throw EvalError(format("duplicate attribute `%1%' at %2%") |  | ||||||
|                 % aterm2String(name) % showPos(pos)); |  | ||||||
|         names.set(name, name); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat) |  | ||||||
| { |  | ||||||
|     ATerm name; |  | ||||||
|     ATermList formals; |  | ||||||
|     Pattern pat1, pat2; |  | ||||||
|     ATermBool ellipsis; |  | ||||||
|     if (matchVarPat(pat, name)) { |  | ||||||
|         if (map.get(name)) |  | ||||||
|             throw EvalError(format("duplicate formal function argument `%1%' at %2%") |  | ||||||
|                 % aterm2String(name) % showPos(pos)); |  | ||||||
|         map.set(name, name); |  | ||||||
|     } |  | ||||||
|     else if (matchAttrsPat(pat, formals, ellipsis)) {  |  | ||||||
|         for (ATermIterator i(formals); i; ++i) { |  | ||||||
|             ATerm d1; |  | ||||||
|             if (!matchFormal(*i, name, d1)) abort(); |  | ||||||
|             if (map.get(name)) |  | ||||||
|                 throw EvalError(format("duplicate formal function argument `%1%' at %2%") |  | ||||||
|                     % aterm2String(name) % showPos(pos)); |  | ||||||
|             map.set(name, name); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     else if (matchAtPat(pat, pat1, pat2)) { |  | ||||||
|         checkPatternVars(pos, map, pat1); |  | ||||||
|         checkPatternVars(pos, map, pat2); |  | ||||||
|     } |  | ||||||
|     else abort(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void checkAttrSets(ATerm e) |  | ||||||
| { |  | ||||||
|     ATerm pat, body, pos; |  | ||||||
|     if (matchFunction(e, pat, body, pos)) { |  | ||||||
|         ATermMap map(16); |  | ||||||
|         checkPatternVars(pos, map, pat); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ATermList bnds; |  | ||||||
|     if (matchAttrs(e, bnds)) { |  | ||||||
|         ATermMap names(ATgetLength(bnds)); |  | ||||||
|         checkAttrs(names, bnds); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     ATermList rbnds, nrbnds; |  | ||||||
|     if (matchRec(e, rbnds, nrbnds)) { |  | ||||||
|         ATermMap names(ATgetLength(rbnds) + ATgetLength(nrbnds)); |  | ||||||
|         checkAttrs(names, rbnds); |  | ||||||
|         checkAttrs(names, nrbnds); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     if (ATgetType(e) == AT_APPL) { |  | ||||||
|         int arity = ATgetArity(ATgetAFun(e)); |  | ||||||
|         for (int i = 0; i < arity; ++i) |  | ||||||
|             checkAttrSets(ATgetArgument(e, i)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     else if (ATgetType(e) == AT_LIST) |  | ||||||
|         for (ATermIterator i((ATermList) e); i; ++i) |  | ||||||
|             checkAttrSets(*i); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static Expr parse(EvalState & state, | static Expr parse(EvalState & state, | ||||||
|     const char * text, const Path & path, |     const char * text, const Path & path, | ||||||
|     const Path & basePath) |     const Path & basePath) | ||||||
|  | @ -490,8 +461,6 @@ static Expr parse(EvalState & state, | ||||||
|         throw EvalError(format("%1%, in `%2%'") % e.msg() % path); |         throw EvalError(format("%1%, in `%2%'") % e.msg() % path); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     checkAttrSets(data.result); |  | ||||||
| 
 |  | ||||||
|     return data.result; |     return data.result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue