* 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:
parent
d9f30fe7c7
commit
1c9c0a5a46
6 changed files with 78 additions and 41 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
|
||||||
{
|
|
||||||
data->result = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
ATerm absParsedPath(ParseData * data, ATerm t)
|
void setParseResult(ParseData * data, ATerm t)
|
||||||
{
|
{
|
||||||
return string2ATerm(absPath(aterm2String(t), data->basePath).c_str());
|
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)
|
void parseError(ParseData * data, char * error, int line, int column)
|
||||||
{
|
{
|
||||||
data->error = (format("%1%, at line %2%, column %3%, of %4%")
|
data->error = (format("%1%, at line %2%, column %3%, of %4%")
|
||||||
% error % line % column % data->location).str();
|
% error % line % column % data->location).str();
|
||||||
}
|
}
|
||||||
|
|
||||||
int yyparse(yyscan_t scanner, ParseData * data);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int yyparse(yyscan_t scanner, ParseData * data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in a new issue