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) void EvalState::forceValueDeep(Value & v)
{ {
std::set<const Value *> seen; std::set<const Value *> seen;

View File

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

View File

@ -191,6 +191,11 @@ void ExprPos::show(std::ostream & str)
str << "__curPos"; str << "__curPos";
} }
void ExprInclude::show(std::ostream & str)
{
str << "(include " << *path << ")";
}
std::ostream & operator << (std::ostream & str, const Pos & pos) 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. */ /* Storing function names. */

View File

@ -86,9 +86,9 @@ struct Expr
std::ostream & operator << (std::ostream & str, Expr & e); std::ostream & operator << (std::ostream & str, Expr & e);
#define COMMON_METHODS \ #define COMMON_METHODS \
void show(std::ostream & str); \ void show(std::ostream & str) override; \
void eval(EvalState & state, Env & env, Value & v); \ void eval(EvalState & state, Env & env, Value & v) override; \
void bindVars(const StaticEnv & env); void bindVars(const StaticEnv & env) override;
struct ExprInt : Expr struct ExprInt : Expr
{ {
@ -326,6 +326,15 @@ struct ExprPos : Expr
COMMON_METHODS 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, /* Static environments are used to map variable names onto (level,
displacement) pairs used to obtain the value of the variable at 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 <nf> FLOAT
%token <path> PATH HPATH SPATH %token <path> PATH HPATH SPATH
%token <uri> URI %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 DOLLAR_CURLY /* == ${ */
%token IND_STRING_OPEN IND_STRING_CLOSE %token IND_STRING_OPEN IND_STRING_CLOSE
%token ELLIPSIS %token ELLIPSIS
@ -348,6 +348,7 @@ expr_app
: expr_app expr_select : expr_app expr_select
{ $$ = new ExprApp(CUR_POS, $1, $2); } { $$ = new ExprApp(CUR_POS, $1, $2); }
| expr_select { $$ = $1; } | expr_select { $$ = $1; }
| INCLUDE expr_select { $$ = new ExprInclude(CUR_POS, $2); }
; ;
expr_select 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