diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f94c23ea7..8bc8e072b 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -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 seen; diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index e5e01fb58..da48376a9 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -109,6 +109,7 @@ in { return IN; } rec { return REC; } inherit { return INHERIT; } or { return OR_KW; } +include { return INCLUDE; } \.\.\. { return ELLIPSIS; } \=\= { return EQ; } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 7b0a127cd..5bd00c336 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -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. */ diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 30be79bb5..81d632df9 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -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 diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index ef11dd609..ac0145631 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -270,7 +270,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err %token FLOAT %token PATH HPATH SPATH %token 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 diff --git a/tests/lang/eval-okay-include.exp b/tests/lang/eval-okay-include.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/lang/eval-okay-include.exp @@ -0,0 +1 @@ +true diff --git a/tests/lang/eval-okay-include.nix b/tests/lang/eval-okay-include.nix new file mode 100644 index 000000000..86faaf50b --- /dev/null +++ b/tests/lang/eval-okay-include.nix @@ -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 diff --git a/tests/lang/include-1.nix b/tests/lang/include-1.nix new file mode 100644 index 000000000..6dccede41 --- /dev/null +++ b/tests/lang/include-1.nix @@ -0,0 +1 @@ +let x = 1; in x diff --git a/tests/lang/include-2.nix b/tests/lang/include-2.nix new file mode 100644 index 000000000..b4e1fda66 --- /dev/null +++ b/tests/lang/include-2.nix @@ -0,0 +1 @@ +1 + x diff --git a/tests/lang/include-3.nix b/tests/lang/include-3.nix new file mode 100644 index 000000000..7a12a967b --- /dev/null +++ b/tests/lang/include-3.nix @@ -0,0 +1 @@ +if x > 0 then x * (let x_ = x; in let x = x_ - 1; in include ./include-3.nix) else 1