Compare commits

...

2 Commits

Author SHA1 Message Date
Eelco Dolstra 2d52180455
printError -> printTalkative 2018-03-02 17:31:11 +01:00
Eelco Dolstra 64ea7cc437
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 ...'.)
2018-03-02 17:12:54 +01:00
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));
printTalkative("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