* Added syntactic sugar to the construction of attribute sets to

`inherit' variables from the surrounding lexical scope.

  E.g.,

    {stdenv, libfoo}: derivation {
      builder = ./bla;
      inherit stdenv libfoo;
      xyzzy = 1;
    }

  is equivalent to

    {stdenv, libfoo}: derivation {
      builder = ./bla;
      stdenv = stdenv;
      libfoo = libfoo;
      xyzzy = 1;
    }

  Note that for mutually recursive attribute set definitions (`rec
  {...}'), this also works, that is, `rec {inherit x;}' is equivalent
  to `let {fresh = x; body = rec {x = fresh;};}', *not*
  `rec {x = x}'.
This commit is contained in:
Eelco Dolstra 2004-02-02 21:39:33 +00:00
parent d9f30fe7c7
commit 1c9c0a5a46
6 changed files with 78 additions and 41 deletions

View file

@ -55,15 +55,15 @@ static Expr substArgs(Expr body, ATermList formals, Expr arg)
/* Transform a mutually recursive set into a non-recursive set. Each /* Transform a mutually recursive set into a non-recursive set. Each
attribute is transformed into an expression that has all references attribute is transformed into an expression that has all references
to attributes substituted with selection expressions on the to attributes substituted with selection expressions on the
original set. E.g., e = `rec {x = f x y, y = x}' becomes `{x = f original set. E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f
(e.x) (e.y), y = e.x}'. */ (e.x) (e.y); y = e.x;}'. */
ATerm expandRec(ATerm e, ATermList bnds) ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds)
{ {
ATMatcher m; ATMatcher m;
/* Create the substitution list. */ /* Create the substitution list. */
ATermMap subs; ATermMap subs;
for (ATermIterator i(bnds); i; ++i) { for (ATermIterator i(rbnds); i; ++i) {
string s; string s;
Expr e2; Expr e2;
if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) if (!(atMatch(m, *i) >> "Bind" >> s >> e2))
@ -73,7 +73,7 @@ ATerm expandRec(ATerm e, ATermList bnds)
/* Create the non-recursive set. */ /* Create the non-recursive set. */
ATermMap as; ATermMap as;
for (ATermIterator i(bnds); i; ++i) { for (ATermIterator i(rbnds); i; ++i) {
string s; string s;
Expr e2; Expr e2;
if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) if (!(atMatch(m, *i) >> "Bind" >> s >> e2))
@ -81,6 +81,15 @@ ATerm expandRec(ATerm e, ATermList bnds)
as.set(s, substitute(subs, e2)); as.set(s, substitute(subs, e2));
} }
/* Copy the non-recursive bindings. !!! inefficient */
for (ATermIterator i(nrbnds); i; ++i) {
string s;
Expr e2;
if (!(atMatch(m, *i) >> "Bind" >> s >> e2))
abort(); /* can't happen */
as.set(s, e2);
}
return makeAttrs(as); return makeAttrs(as);
} }
@ -175,14 +184,9 @@ Expr evalExpr2(EvalState & state, Expr e)
} }
/* Mutually recursive sets. */ /* Mutually recursive sets. */
ATermList bnds; ATermList rbnds, nrbnds;
if (atMatch(m, e) >> "Rec" >> bnds) if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds)
return expandRec(e, bnds); return expandRec(e, rbnds, nrbnds);
/* Let expressions `let {..., body = ...}' are just desugared
into `(rec {..., body = ...}).body'. */
if (atMatch(m, e) >> "LetRec" >> bnds)
return evalExpr(state, ATmake("Select(Rec(<term>), \"body\")", bnds));
/* Conditionals. */ /* Conditionals. */
if (atMatch(m, e) >> "If" >> e1 >> e2 >> e3) { if (atMatch(m, e) >> "If" >> e1 >> e2 >> e3) {

View file

@ -50,6 +50,7 @@ else { return ELSE; }
assert { return ASSERT; } assert { return ASSERT; }
let { return LET; } let { return LET; }
rec { return REC; } rec { return REC; }
inherit { return INHERIT; }
\=\= { return EQ; } \=\= { return EQ; }
\!\= { return NEQ; } \!\= { return NEQ; }

View file

@ -181,16 +181,18 @@ Expr substitute(const ATermMap & subs, Expr e)
} }
/* Idem for a mutually recursive attribute set. */ /* Idem for a mutually recursive attribute set. */
ATermList bindings; ATermList rbnds, nrbnds;
if (atMatch(m, e) >> "Rec" >> bindings) { if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds) {
ATermMap subs2(subs); ATermMap subs2(subs);
for (ATermIterator i(bindings); i; ++i) { for (ATermIterator i(rbnds); i; ++i) {
Expr e; Expr e;
if (!(atMatch(m, *i) >> "Bind" >> s >> e)) if (!(atMatch(m, *i) >> "Bind" >> s >> e))
abort(); /* can't happen */ abort(); /* can't happen */
subs2.remove(s); subs2.remove(s);
} }
return ATmake("Rec(<term>)", substitute(subs2, (ATerm) bindings)); return ATmake("Rec(<term>, <term>)",
substitute(subs2, (ATerm) rbnds),
substitute(subs, (ATerm) nrbnds));
} }
if (ATgetType(e) == AT_APPL) { if (ATgetType(e) == AT_APPL) {

View file

@ -17,32 +17,52 @@ struct ParseData
string error; string error;
}; };
extern "C" { extern "C" {
#include "parser-tab.h" #include "parser-tab.h"
#include "lexer-tab.h" #include "lexer-tab.h"
/* Callbacks for getting from C to C++. Due to a (small) bug in the /* Callbacks for getting from C to C++. Due to a (small) bug in the
GLR code of Bison we cannot currently compile the parser as C++ GLR code of Bison we cannot currently compile the parser as C++
code. */ code. */
void setParseResult(ParseData * data, ATerm t) void setParseResult(ParseData * data, ATerm t)
{ {
data->result = t; data->result = t;
}
ATerm absParsedPath(ParseData * data, ATerm t)
{
return string2ATerm(absPath(aterm2String(t), data->basePath).c_str());
}
void parseError(ParseData * data, char * error, int line, int column)
{
data->error = (format("%1%, at line %2%, column %3%, of %4%")
% error % line % column % data->location).str();
}
ATerm fixAttrs(int recursive, ATermList as)
{
ATMatcher m;
ATermList bs = ATempty, cs = ATempty;
ATermList * is = recursive ? &cs : &bs;
for (ATermIterator i(as); i; ++i) {
ATermList names;
if (atMatch(m, *i) >> "Inherit" >> names)
for (ATermIterator j(names); j; ++j)
*is = ATinsert(*is,
ATmake("Bind(<term>, Var(<term>))", *j, *j));
else bs = ATinsert(bs, *i);
} }
if (recursive)
return ATmake("Rec(<term>, <term>)", bs, cs);
else
return ATmake("Attrs(<term>)", bs);
}
ATerm absParsedPath(ParseData * data, ATerm t) int yyparse(yyscan_t scanner, ParseData * data);
{
return string2ATerm(absPath(aterm2String(t), data->basePath).c_str());
}
void parseError(ParseData * data, char * error, int line, int column)
{
data->error = (format("%1%, at line %2%, column %3%, of %4%")
% error % line % column % data->location).str();
}
int yyparse(yyscan_t scanner, ParseData * data);
} }

View file

@ -18,6 +18,7 @@
void setParseResult(void * data, ATerm t); void setParseResult(void * data, ATerm t);
void parseError(void * data, char * error, int line, int column); void parseError(void * data, char * error, int line, int column);
ATerm absParsedPath(void * data, ATerm t); ATerm absParsedPath(void * data, ATerm t);
ATerm fixAttrs(int recursive, ATermList as);
void yyerror(YYLTYPE * loc, yyscan_t scanner, void * data, char * s) void yyerror(YYLTYPE * loc, yyscan_t scanner, void * data, char * s)
{ {
@ -33,9 +34,9 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, void * data, char * s)
%type <t> start expr expr_function expr_assert expr_op %type <t> start expr expr_function expr_assert expr_op
%type <t> expr_app expr_select expr_simple bind formal %type <t> expr_app expr_select expr_simple bind formal
%type <ts> binds expr_list formals %type <ts> binds ids expr_list formals
%token <t> ID INT STR PATH URI %token <t> ID INT STR PATH URI
%token IF THEN ELSE ASSERT LET REC EQ NEQ AND OR IMPL %token IF THEN ELSE ASSERT LET REC INHERIT EQ NEQ AND OR IMPL
%nonassoc IMPL %nonassoc IMPL
%left OR %left OR
@ -90,9 +91,14 @@ expr_simple
| PATH { $$ = ATmake("Path(<term>)", absParsedPath(data, $1)); } | PATH { $$ = ATmake("Path(<term>)", absParsedPath(data, $1)); }
| URI { $$ = ATmake("Uri(<term>)", $1); } | URI { $$ = ATmake("Uri(<term>)", $1); }
| '(' expr ')' { $$ = $2; } | '(' expr ')' { $$ = $2; }
| LET '{' binds '}' { $$ = ATmake("LetRec(<term>)", $3); } /* Let expressions `let {..., body = ...}' are just desugared
| REC '{' binds '}' { $$ = ATmake("Rec(<term>)", $3); } into `(rec {..., body = ...}).body'. */
| '{' binds '}' { $$ = ATmake("Attrs(<term>)", $2); } | LET '{' binds '}'
{ $$ = ATmake("Select(<term>, \"body\")", fixAttrs(1, $3)); }
| REC '{' binds '}'
{ $$ = fixAttrs(1, $3); }
| '{' binds '}'
{ $$ = fixAttrs(0, $2); }
| '[' expr_list ']' { $$ = ATmake("List(<term>)", $2); } | '[' expr_list ']' { $$ = ATmake("List(<term>)", $2); }
| IF expr THEN expr ELSE expr | IF expr THEN expr ELSE expr
{ $$ = ATmake("If(<term>, <term>, <term>)", $2, $4, $6); } { $$ = ATmake("If(<term>, <term>, <term>)", $2, $4, $6); }
@ -106,8 +112,12 @@ binds
bind bind
: ID '=' expr ';' : ID '=' expr ';'
{ $$ = ATmake("Bind(<term>, <term>)", $1, $3); } { $$ = ATmake("Bind(<term>, <term>)", $1, $3); }
| INHERIT ids ';'
{ $$ = ATmake("Inherit(<term>)", $2); }
; ;
ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; };
expr_list expr_list
: expr_select expr_list { $$ = ATinsert($2, $1); } : expr_select expr_list { $$ = ATinsert($2, $1); }
/* yes, this is right-recursive, but it doesn't matter since /* yes, this is right-recursive, but it doesn't matter since

View file

@ -96,7 +96,7 @@ static string processBinding(EvalState & state, Expr e, StoreExpr & ne)
return st.str(); return st.str();
} }
if (atMatch(m, e) >> "Attrs" >> es) { if (atMatch(m, e) >> "Attrs") {
Expr a = queryAttr(e, "type"); Expr a = queryAttr(e, "type");
if (a && evalString(state, a) == "derivation") { if (a && evalString(state, a) == "derivation") {
a = queryAttr(e, "drvPath"); a = queryAttr(e, "drvPath");