diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 77cab55d03..820e934a63 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -55,15 +55,15 @@ static Expr substArgs(Expr body, ATermList formals, Expr arg) /* Transform a mutually recursive set into a non-recursive set. Each attribute is transformed into an expression that has all references to attributes substituted with selection expressions on the - original set. E.g., e = `rec {x = f x y, y = x}' becomes `{x = f - (e.x) (e.y), y = e.x}'. */ -ATerm expandRec(ATerm e, ATermList bnds) + original set. E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f + (e.x) (e.y); y = e.x;}'. */ +ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds) { ATMatcher m; /* Create the substitution list. */ ATermMap subs; - for (ATermIterator i(bnds); i; ++i) { + for (ATermIterator i(rbnds); i; ++i) { string s; Expr e2; if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) @@ -73,7 +73,7 @@ ATerm expandRec(ATerm e, ATermList bnds) /* Create the non-recursive set. */ ATermMap as; - for (ATermIterator i(bnds); i; ++i) { + for (ATermIterator i(rbnds); i; ++i) { string s; Expr e2; if (!(atMatch(m, *i) >> "Bind" >> s >> e2)) @@ -81,6 +81,15 @@ ATerm expandRec(ATerm e, ATermList bnds) 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); } @@ -175,14 +184,9 @@ Expr evalExpr2(EvalState & state, Expr e) } /* Mutually recursive sets. */ - ATermList bnds; - if (atMatch(m, e) >> "Rec" >> bnds) - return expandRec(e, bnds); - - /* Let expressions `let {..., body = ...}' are just desugared - into `(rec {..., body = ...}).body'. */ - if (atMatch(m, e) >> "LetRec" >> bnds) - return evalExpr(state, ATmake("Select(Rec(), \"body\")", bnds)); + ATermList rbnds, nrbnds; + if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds) + return expandRec(e, rbnds, nrbnds); /* Conditionals. */ if (atMatch(m, e) >> "If" >> e1 >> e2 >> e3) { diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 3b6e0bb657..853362cd0b 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -50,6 +50,7 @@ else { return ELSE; } assert { return ASSERT; } let { return LET; } rec { return REC; } +inherit { return INHERIT; } \=\= { return EQ; } \!\= { return NEQ; } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 7de3e823c4..b0f506e65d 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -181,16 +181,18 @@ Expr substitute(const ATermMap & subs, Expr e) } /* Idem for a mutually recursive attribute set. */ - ATermList bindings; - if (atMatch(m, e) >> "Rec" >> bindings) { + ATermList rbnds, nrbnds; + if (atMatch(m, e) >> "Rec" >> rbnds >> nrbnds) { ATermMap subs2(subs); - for (ATermIterator i(bindings); i; ++i) { + for (ATermIterator i(rbnds); i; ++i) { Expr e; if (!(atMatch(m, *i) >> "Bind" >> s >> e)) abort(); /* can't happen */ subs2.remove(s); } - return ATmake("Rec()", substitute(subs2, (ATerm) bindings)); + return ATmake("Rec(, )", + substitute(subs2, (ATerm) rbnds), + substitute(subs, (ATerm) nrbnds)); } if (ATgetType(e) == AT_APPL) { diff --git a/src/libexpr/parser.cc b/src/libexpr/parser.cc index 167c34bd83..2574a55bd4 100644 --- a/src/libexpr/parser.cc +++ b/src/libexpr/parser.cc @@ -17,32 +17,52 @@ struct ParseData string error; }; + extern "C" { #include "parser-tab.h" #include "lexer-tab.h" - /* 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++ - code. */ - - void setParseResult(ParseData * data, ATerm t) - { - data->result = t; - } +/* 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++ + code. */ - ATerm absParsedPath(ParseData * data, ATerm t) - { - return string2ATerm(absPath(aterm2String(t), data->basePath).c_str()); - } +void setParseResult(ParseData * data, ATerm 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(); - } +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); +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(, Var())", *j, *j)); + else bs = ATinsert(bs, *i); + } + if (recursive) + return ATmake("Rec(, )", bs, cs); + else + return ATmake("Attrs()", bs); +} + +int yyparse(yyscan_t scanner, ParseData * data); } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index dc03117bb1..d97106fcae 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -18,6 +18,7 @@ void setParseResult(void * data, ATerm t); void parseError(void * data, char * error, int line, int column); ATerm absParsedPath(void * data, ATerm t); +ATerm fixAttrs(int recursive, ATermList as); 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 start expr expr_function expr_assert expr_op %type expr_app expr_select expr_simple bind formal -%type binds expr_list formals +%type binds ids expr_list formals %token 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 %left OR @@ -90,9 +91,14 @@ expr_simple | PATH { $$ = ATmake("Path()", absParsedPath(data, $1)); } | URI { $$ = ATmake("Uri()", $1); } | '(' expr ')' { $$ = $2; } - | LET '{' binds '}' { $$ = ATmake("LetRec()", $3); } - | REC '{' binds '}' { $$ = ATmake("Rec()", $3); } - | '{' binds '}' { $$ = ATmake("Attrs()", $2); } + /* Let expressions `let {..., body = ...}' are just desugared + into `(rec {..., body = ...}).body'. */ + | LET '{' binds '}' + { $$ = ATmake("Select(, \"body\")", fixAttrs(1, $3)); } + | REC '{' binds '}' + { $$ = fixAttrs(1, $3); } + | '{' binds '}' + { $$ = fixAttrs(0, $2); } | '[' expr_list ']' { $$ = ATmake("List()", $2); } | IF expr THEN expr ELSE expr { $$ = ATmake("If(, , )", $2, $4, $6); } @@ -106,8 +112,12 @@ binds bind : ID '=' expr ';' { $$ = ATmake("Bind(, )", $1, $3); } + | INHERIT ids ';' + { $$ = ATmake("Inherit()", $2); } ; +ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; }; + expr_list : expr_select expr_list { $$ = ATinsert($2, $1); } /* yes, this is right-recursive, but it doesn't matter since diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index da6927d0fc..d1c398a348 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -96,7 +96,7 @@ static string processBinding(EvalState & state, Expr e, StoreExpr & ne) return st.str(); } - if (atMatch(m, e) >> "Attrs" >> es) { + if (atMatch(m, e) >> "Attrs") { Expr a = queryAttr(e, "type"); if (a && evalString(state, a) == "derivation") { a = queryAttr(e, "drvPath");