diff --git a/src/libexpr/eval-test.cc b/src/libexpr/eval-test.cc index 32f4940df9..dd8ead04a6 100644 --- a/src/libexpr/eval-test.cc +++ b/src/libexpr/eval-test.cc @@ -45,6 +45,7 @@ void run(Strings args) doTest(state, "({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); //doTest(state, "({x ? y, y ? x}: y) { }"); doTest(state, "let x = 1; in x"); + doTest(state, "let { x = 1; body = x; }"); doTest(state, "with { x = 1; }; x"); doTest(state, "let x = 2; in with { x = 1; }; x"); // => 2 doTest(state, "with { x = 1; }; with { x = 2; }; x"); // => 1 @@ -65,18 +66,19 @@ void run(Strings args) doTest(state, "__head [ 1 2 3 ]"); doTest(state, "__add 1 2"); doTest(state, "null"); - //doTest(state, "\"foo\""); - //doTest(state, "let s = \"bar\"; in \"foo${s}\""); + doTest(state, "\"foo\""); + doTest(state, "let s = \"bar\"; in \"foo${s}\""); doTest(state, "if true then 1 else 2"); doTest(state, "if false then 1 else 2"); doTest(state, "if false || true then 1 else 2"); + doTest(state, "!(true || false)"); doTest(state, "let x = x; in if true || x then 1 else 2"); doTest(state, "http://nixos.org/"); doTest(state, "/etc/passwd"); //doTest(state, "import ./foo.nix"); doTest(state, "map (x: __add 1 x) [ 1 2 3 ]"); doTest(state, "map (builtins.add 1) [ 1 2 3 ]"); - //doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); + doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); doTest(state, "let x = 1; as = rec { inherit x; y = as.x; }; in as.y"); doTest(state, "let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); doTest(state, "let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 8ead986b81..21c22333bf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -160,9 +160,9 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2)) throw TypeError(format(s) % s2); } -LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s2)) +LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos)) { - throw AssertionError(format(s) % s2); + throw AssertionError(format(s) % pos); } LocalNoInline(void addErrorPrefix(Error & e, const char * s)) @@ -341,73 +341,13 @@ void EvalState::eval(Env & env, Expr * e, Value & v) char x; if (&x < deepestStack) deepestStack = &x; - //debug(format("eval: %1%") % e); + //debug(format("eval: %1%") % *e); checkInterrupt(); nrEvaluated++; e->eval(*this, env, v); - -#if 0 - Sym name; - int n; - ATerm s; ATermList context, es; - ATermList rbnds, nrbnds; - Expr e1, e2, e3, fun, arg, attrs; - Pattern pat; Expr body; Pos pos; - - else if (matchConcatStrings(e, es)) { - PathSet context; - std::ostringstream s; - - bool first = true, isPath = false; - Value vStr; - - for (ATermIterator i(es); i; ++i) { - eval(env, *i, vStr); - - /* If the first element is a path, then the result will - also be a path, we don't copy anything (yet - that's - done later, since paths are copied when they are used - in a derivation), and none of the strings are allowed - to have contexts. */ - if (first) { - isPath = vStr.type == tPath; - first = false; - } - - s << coerceToString(vStr, context, false, !isPath); - } - - if (isPath && !context.empty()) - throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str()); - - if (isPath) - mkPath(v, s.str().c_str()); - else - mkString(v, s.str(), context); - } - - /* Assertions. */ - else if (matchAssert(e, e1, e2, pos)) { - if (!evalBool(env, e1)) - throwAssertionError("assertion failed at %1%", showPos(pos)); - eval(env, e2, v); - } - - /* Negation. */ - else if (matchOpNot(e, e1)) - mkBool(v, !evalBool(env, e1)); - - /* Attribute existence test (?). */ - else if (matchOpHasAttr(e, e1, name)) { - Value vAttrs; - eval(env, e1, vAttrs); - forceAttrs(vAttrs); - mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end()); - } -#endif } @@ -516,6 +456,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) } +void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) +{ + Value vAttrs; + state.eval(env, e, vAttrs); + state.forceAttrs(vAttrs); + mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end()); +} + + void ExprLambda::eval(EvalState & state, Env & env, Value & v) { v.type = tLambda; @@ -663,6 +612,20 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v) } +void ExprAssert::eval(EvalState & state, Env & env, Value & v) +{ + if (!state.evalBool(env, cond)) + throwAssertionError("assertion failed at %1%", pos); + state.eval(env, body, v); +} + + +void ExprOpNot::eval(EvalState & state, Env & env, Value & v) +{ + mkBool(v, !state.evalBool(env, e)); +} + + void ExprOpEq::eval(EvalState & state, Env & env, Value & v) { Value v1; state.eval(env, e1, v1); @@ -713,12 +676,6 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) } -void ExprOpConcatStrings::eval(EvalState & state, Env & env, Value & v) -{ - abort(); -} - - void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) { Value v1; state.eval(env, e1, v1); @@ -735,6 +692,39 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) } +void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) +{ + PathSet context; + std::ostringstream s; + + bool first = true, isPath = false; + Value vStr; + + foreach (vector::iterator, i, *es) { + state.eval(env, *i, vStr); + + /* If the first element is a path, then the result will also + be a path, we don't copy anything (yet - that's done later, + since paths are copied when they are used in a derivation), + and none of the strings are allowed to have contexts. */ + if (first) { + isPath = vStr.type == tPath; + first = false; + } + + s << state.coerceToString(vStr, context, false, !isPath); + } + + if (isPath && !context.empty()) + throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str()); + + if (isPath) + mkPath(v, s.str().c_str()); + else + mkString(v, s.str(), context); +} + + void EvalState::forceValue(Value & v) { if (v.type == tThunk) { diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index f750cfd02d..e905700fde 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -45,7 +45,6 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) static Expr * unescapeStr(const char * s) { -#if 0 string t; char c; while ((c = *s++)) { @@ -64,8 +63,7 @@ static Expr * unescapeStr(const char * s) } else t += c; } - return makeStr(toATerm(t), ATempty); -#endif + return new ExprString(t); } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 05dfbd3223..b044aaa943 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -40,6 +40,11 @@ void ExprSelect::show(std::ostream & str) str << "(" << *e << ")." << name; } +void ExprOpHasAttr::show(std::ostream & str) +{ + str << "(" << *e << ") ? " << name; +} + void ExprAttrs::show(std::ostream & str) { if (recursive) str << "rec "; @@ -87,19 +92,37 @@ void ExprIf::show(std::ostream & str) str << "if " << *cond << " then " << *then << " else " << *else_; } - -#if 0 -string showPos(ATerm pos) +void ExprAssert::show(std::ostream & str) { - ATerm path; - int line, column; - if (matchNoPos(pos)) return "undefined position"; - if (!matchPos(pos, path, line, column)) - throw badTerm("position expected", pos); - return (format("`%1%:%2%:%3%'") % aterm2String(path) % line % column).str(); + str << "assert " << *cond << "; " << *body; +} + +void ExprOpNot::show(std::ostream & str) +{ + str << "! " << *e; +} + +void ExprConcatStrings::show(std::ostream & str) +{ + bool first = true; + foreach (vector::iterator, i, *es) { + if (first) first = false; else str << " + "; + str << **i; + } +} + + +std::ostream & operator << (std::ostream & str, const Pos & pos) +{ + if (!pos.line) + str << "undefined position"; + else + str << (format("`%1%:%2%:%3%'") % pos.file % pos.line % pos.column).str(); + return str; } +#if 0 ATerm bottomupRewrite(TermFun & f, ATerm e) { checkInterrupt(); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index ebdfd0a152..d6e088c416 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -24,6 +24,9 @@ struct Pos }; +std::ostream & operator << (std::ostream & str, const Pos & pos); + + /* Abstract syntax of Nix expressions. */ struct Env; @@ -81,6 +84,14 @@ struct ExprSelect : Expr COMMON_METHODS }; +struct ExprOpHasAttr : Expr +{ + Expr * e; + string name; + ExprOpHasAttr(Expr * e, const string & name) : e(e), name(name) { }; + COMMON_METHODS +}; + struct ExprAttrs : Expr { bool recursive; @@ -139,6 +150,21 @@ struct ExprIf : Expr COMMON_METHODS }; +struct ExprAssert : Expr +{ + Pos pos; + Expr * cond, * body; + ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { }; + COMMON_METHODS +}; + +struct ExprOpNot : Expr +{ + Expr * e; + ExprOpNot(Expr * e) : e(e) { }; + COMMON_METHODS +}; + #define MakeBinOp(name, s) \ struct Expr##name : Expr \ { \ @@ -158,15 +184,17 @@ MakeBinOp(OpAnd, "&&") MakeBinOp(OpOr, "||") MakeBinOp(OpImpl, "->") MakeBinOp(OpUpdate, "//") -MakeBinOp(OpConcatStrings, "+") MakeBinOp(OpConcatLists, "++") +struct ExprConcatStrings : Expr +{ + vector * es; + ExprConcatStrings(vector * es) : es(es) { }; + COMMON_METHODS +}; + #if 0 -/* Show a position. */ -string showPos(ATerm pos); - - /* Generic bottomup traversal over ATerms. The traversal first recursively descends into subterms, and then applies the given term function to the resulting term. */ diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 96fbe2cb4b..83f454845d 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -20,7 +20,6 @@ #include #include -#include "aterm.hh" #include "util.hh" #include "nixexpr.hh" @@ -116,13 +115,13 @@ static void fixAttrs(ExprAttrs & attrs) for (ATermIterator j(attrPath); j; ) { name = *j; ++j; if (t->leaf) throw ParseError(format("attribute set containing `%1%' at %2% already defined at %3%") - % showAttrPath(attrPath) % showPos(pos) % showPos (t->pos)); + % showAttrPath(attrPath) % showPos(pos) % showPos(t->pos)); t = &(t->children[name]); } if (t->leaf) throw ParseError(format("duplicate definition of attribute `%1%' at %2% and %3%") - % showAttrPath(attrPath) % showPos(pos) % showPos (t->pos)); + % showAttrPath(attrPath) % showPos(pos) % showPos(t->pos)); if (!t->children.empty()) throw ParseError(format("duplicate definition of attribute `%1%' at %2%") % showAttrPath(attrPath) % showPos(pos)); @@ -289,30 +288,11 @@ static Pos makeCurPos(YYLTYPE * loc, ParseData * data) void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error) { - data->error = (format("%1%, at `%2%':%3%:%4%") - % error % data->path % loc->first_line % loc->first_column).str(); + data->error = (format("%1%, at %2%") + % error % makeCurPos(loc, data)).str(); } -/* Make sure that the parse stack is scanned by the ATerm garbage - collector. */ -static void * mallocAndProtect(size_t size) -{ - void * p = malloc(size); - if (p) ATprotectMemory(p, size); - return p; -} - -static void freeAndUnprotect(void * p) -{ - ATunprotectMemory(p); - free(p); -} - -#define YYMALLOC mallocAndProtect -#define YYFREE freeAndUnprotect - - #endif @@ -329,18 +309,20 @@ static void freeAndUnprotect(void * p) char * path; char * uri; std::list * ids; + std::vector * string_parts; } %type start expr expr_function expr_if expr_op %type expr_app expr_select expr_simple %type expr_list %type binds -%type attrpath string_parts ind_string_parts +%type attrpath ind_string_parts %type formals %type formal %type ids +%type string_parts %token ID ATTRPATH -%token STR IND_STR +%token STR IND_STR %token INT %token PATH %token URI @@ -375,9 +357,8 @@ expr_function { $$ = new ExprLambda(CUR_POS, $5, true, $2, $7); } | ID '@' '{' formals '}' ':' expr_function { $$ = new ExprLambda(CUR_POS, $1, true, $4, $7); } - /* | ASSERT expr ';' expr_function - { $$ = makeAssert($2, $4, CUR_POS); } - */ + | ASSERT expr ';' expr_function + { $$ = new ExprAssert(CUR_POS, $2, $4); } | WITH expr ';' expr_function { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN expr_function @@ -391,18 +372,20 @@ expr_if ; expr_op - : /* '!' expr_op %prec NEG { $$ = makeOpNot($2); } - | */ - expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); } + : '!' expr_op %prec NEG { $$ = new ExprOpNot($2); } + | expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); } | expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); } | expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); } | expr_op OR expr_op { $$ = new ExprOpOr($1, $3); } | expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); } | expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); } - /* - | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); } - */ - | expr_op '+' expr_op { $$ = new ExprOpConcatStrings($1, $3); } + | expr_op '?' ID { $$ = new ExprOpHasAttr($1, $3); } + | expr_op '+' expr_op + { vector * l = new vector; + l->push_back($1); + l->push_back($3); + $$ = new ExprConcatStrings(l); + } | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); } | expr_app ; @@ -421,26 +404,25 @@ expr_select expr_simple : ID { $$ = new ExprVar($1); } - | INT { $$ = new ExprInt($1); } /* + | INT { $$ = new ExprInt($1); } | '"' string_parts '"' { - /* For efficiency, and to simplify parse trees a bit. * / - if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty); - else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2); - else $$ = makeConcatStrings(ATreverse($2)); + /* For efficiency, and to simplify parse trees a bit. */ + if ($2->empty()) $$ = new ExprString(""); + else if ($2->size() == 1) $$ = $2->front(); + else $$ = new ExprConcatStrings($2); } + /* | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { $$ = stripIndentation(ATreverse($2)); } - */ + */ | PATH { $$ = new ExprPath(absPath($1, data->basePath)); } | URI { $$ = new ExprString($1); } | '(' expr ')' { $$ = $2; } -/* /* Let expressions `let {..., body = ...}' are just desugared - into `(rec {..., body = ...}).body'. * / + into `(rec {..., body = ...}).body'. */ | LET '{' binds '}' - { $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); } - */ + { fixAttrs(*$3); $3->recursive = true; $$ = new ExprSelect($3, "body"); } | REC '{' binds '}' { fixAttrs(*$3); $3->recursive = true; $$ = $3; } | '{' binds '}' @@ -449,9 +431,9 @@ expr_simple ; string_parts - : string_parts STR { $$ = ATinsert($1, $2); } - | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); } - | { $$ = ATempty; } + : string_parts STR { $$ = $1; $1->push_back($2); } + | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); } + | { $$ = new vector; } ; ind_string_parts