1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-14 14:32:42 +01:00

Add an 'include' keyword

Inclusion is like importing, except that all variables that in scope
at the 'include' site are in scope of the included file.

Thus, if ./foo.nix contains 'x', then

  let x = 1; in include ./foo.nix

evaluates to 1.

(Note that 'include' has to be a keyword, not a builtin function,
because inclusion takes place within a specific scope. I.e. 'include
./foo.nix' would not be equivalent to 'let f = include; in ... f
./foo.nix ...'.)
This commit is contained in:
Eelco Dolstra 2018-03-02 17:12:54 +01:00
parent 939cf4cceb
commit 64ea7cc437
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
10 changed files with 75 additions and 4 deletions

View file

@ -1386,6 +1386,23 @@ void ExprPos::eval(EvalState & state, Env & env, Value & v)
}
void ExprInclude::eval(EvalState & state, Env & env, Value & v)
{
Value vPath;
path->eval(state, env, vPath);
PathSet context;
auto realPath = state.checkSourcePath(
state.toRealPath(state.coerceToPath(pos, vPath, context), context));
printError("including file '%1%'", realPath);
auto e = state.parseExprFromFile(resolveExprPath(realPath), *staticEnv);
e->eval(state, env, v);
}
void EvalState::forceValueDeep(Value & v)
{
std::set<const Value *> seen;

View file

@ -109,6 +109,7 @@ in { return IN; }
rec { return REC; }
inherit { return INHERIT; }
or { return OR_KW; }
include { return INCLUDE; }
\.\.\. { return ELLIPSIS; }
\=\= { return EQ; }

View file

@ -191,6 +191,11 @@ void ExprPos::show(std::ostream & str)
str << "__curPos";
}
void ExprInclude::show(std::ostream & str)
{
str << "(include " << *path << ")";
}
std::ostream & operator << (std::ostream & str, const Pos & pos)
{
@ -402,6 +407,24 @@ void ExprPos::bindVars(const StaticEnv & env)
{
}
void ExprInclude::bindVars(const StaticEnv & env)
{
path->bindVars(env);
/* Copy the static environment so that ExprInclude::eval() can
pass it to parseExprFromFile(). FIXME: if there are many uses
of 'include' in the same scope, we should cache this. */
const StaticEnv * srcEnv = &env;
StaticEnv * * dstEnv = &staticEnv;
while (srcEnv) {
*dstEnv = new StaticEnv(*srcEnv);
srcEnv = srcEnv->up;
dstEnv = (StaticEnv * *) &(*dstEnv)->up;
}
}
/* Storing function names. */

View file

@ -86,9 +86,9 @@ struct Expr
std::ostream & operator << (std::ostream & str, Expr & e);
#define COMMON_METHODS \
void show(std::ostream & str); \
void eval(EvalState & state, Env & env, Value & v); \
void bindVars(const StaticEnv & env);
void show(std::ostream & str) override; \
void eval(EvalState & state, Env & env, Value & v) override; \
void bindVars(const StaticEnv & env) override;
struct ExprInt : Expr
{
@ -326,6 +326,15 @@ struct ExprPos : Expr
COMMON_METHODS
};
struct ExprInclude : Expr
{
Pos pos;
Expr * path;
StaticEnv * staticEnv;
ExprInclude(const Pos & pos, Expr * path) : pos(pos), path(path) { };
COMMON_METHODS
};
/* Static environments are used to map variable names onto (level,
displacement) pairs used to obtain the value of the variable at

View file

@ -270,7 +270,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%token <nf> FLOAT
%token <path> PATH HPATH SPATH
%token <uri> URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW INCLUDE
%token DOLLAR_CURLY /* == ${ */
%token IND_STRING_OPEN IND_STRING_CLOSE
%token ELLIPSIS
@ -348,6 +348,7 @@ expr_app
: expr_app expr_select
{ $$ = new ExprApp(CUR_POS, $1, $2); }
| expr_select { $$ = $1; }
| INCLUDE expr_select { $$ = new ExprInclude(CUR_POS, $2); }
;
expr_select

View file

@ -0,0 +1 @@
true

View file

@ -0,0 +1,16 @@
assert include ./include-1.nix == 1;
assert let x = 100; in include ./include-1.nix == 1;
assert with { x = 123; }; include ./include-1.nix == 1;
assert let x = 2; in include ./include-2.nix == 3;
assert let x = 3; in include ./include-2.nix == 4;
assert let x = 3; in let x = 4; in include ./include-2.nix == 5;
assert let x = 3; in let x = 4; in include ./include-2.nix == 5;
assert let x = 6; in with { x = 7; }; include ./include-2.nix == 7;
assert with { x = 0; }; let x = 6; in with { x = 7; }; include ./include-2.nix == 7;
assert with { x = 0; }; let x = 6; in with { x = 7; }; let x = 7; in include ./include-2.nix == 8;
assert with { x = 8; }; include ./include-2.nix == 9;
assert (let x = 10; in include ./include-3.nix) == 3628800;
true

1
tests/lang/include-1.nix Normal file
View file

@ -0,0 +1 @@
let x = 1; in x

1
tests/lang/include-2.nix Normal file
View file

@ -0,0 +1 @@
1 + x

1
tests/lang/include-3.nix Normal file
View file

@ -0,0 +1 @@
if x > 0 then x * (let x_ = x; in let x = x_ - 1; in include ./include-3.nix) else 1