Skip to content

Commit

Permalink
Add an 'include' keyword
Browse files Browse the repository at this point in the history
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 ...'.)
  • Loading branch information
edolstra committed Nov 20, 2018
1 parent 4f7cd58 commit 5e625b6
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 4 deletions.
17 changes: 17 additions & 0 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1422,6 +1422,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;
Expand Down
1 change: 1 addition & 0 deletions src/libexpr/lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ in { return IN; }
rec { return REC; }
inherit { return INHERIT; }
or { return OR_KW; }
include { return INCLUDE; }
\.\.\. { return ELLIPSIS; }

\=\= { return EQ; }
Expand Down
23 changes: 23 additions & 0 deletions src/libexpr/nixexpr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ void ExprPos::show(std::ostream & str) const
str << "__curPos";
}

void ExprInclude::show(std::ostream & str) const
{
str << "(include " << *path << ")";
}


std::ostream & operator << (std::ostream & str, const Pos & pos)
{
Expand Down Expand Up @@ -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. */

Expand Down
15 changes: 12 additions & 3 deletions src/libexpr/nixexpr.hh
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ struct Expr
std::ostream & operator << (std::ostream & str, const Expr & e);

#define COMMON_METHODS \
void show(std::ostream & str) const; \
void eval(EvalState & state, Env & env, Value & v); \
void bindVars(const StaticEnv & env);
void show(std::ostream & str) const override; \
void eval(EvalState & state, Env & env, Value & v) override; \
void bindVars(const StaticEnv & env) override;

struct ExprInt : Expr
{
Expand Down Expand Up @@ -325,6 +325,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
Expand Down
3 changes: 2 additions & 1 deletion src/libexpr/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,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
Expand Down Expand Up @@ -346,6 +346,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
Expand Down
1 change: 1 addition & 0 deletions tests/lang/eval-okay-include.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
16 changes: 16 additions & 0 deletions tests/lang/eval-okay-include.nix
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions tests/lang/include-1.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let x = 1; in x
1 change: 1 addition & 0 deletions tests/lang/include-2.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1 + x
1 change: 1 addition & 0 deletions tests/lang/include-3.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
if x > 0 then x * (let x_ = x; in let x = x_ - 1; in include ./include-3.nix) else 1

0 comments on commit 5e625b6

Please sign in to comment.