From 991a130b1e71b3b849b3640264f8834d6e13da22 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 25 Jul 2005 15:05:34 +0000 Subject: [PATCH] * Added a list concatenation operator: [1 2 3] ++ [4 5 6] => [1 2 3 4 5 6] --- src/libexpr/eval.cc | 16 ++++++++++++++++ src/libexpr/eval.hh | 1 + src/libexpr/lexer.l | 1 + src/libexpr/nixexpr-ast.def | 1 + src/libexpr/parser.y | 2 ++ src/libexpr/primops.cc | 18 ++++++------------ tests/lang/eval-okay-concat.exp | 1 + tests/lang/eval-okay-concat.nix | 1 + 8 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 tests/lang/eval-okay-concat.exp create mode 100644 tests/lang/eval-okay-concat.nix diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d4593b5c..9a6d414d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -140,6 +140,15 @@ bool evalBool(EvalState & state, Expr e) } +ATermList evalList(EvalState & state, Expr e) +{ + e = evalExpr(state, e); + ATermList list; + if (!matchList(e, list)) throw Error("list expected"); + return list; +} + + Expr evalExpr2(EvalState & state, Expr e) { Expr e1, e2, e3, e4; @@ -336,6 +345,13 @@ Expr evalExpr2(EvalState & state, Expr e) else throw Error("wrong argument types in `+' operator"); } + /* List concatenation. */ + if (matchOpConcat(e, e1, e2)) { + ATermList l1 = evalList(state, e1); + ATermList l2 = evalList(state, e2); + return makeList(ATconcat(l1, l2)); + } + /* Barf. */ throw badTerm("invalid expression", e); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 8ea0aec0..5562066f 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -46,6 +46,7 @@ Expr evalFile(EvalState & state, const Path & path); /* Specific results. */ string evalString(EvalState & state, Expr e); Path evalPath(EvalState & state, Expr e); +ATermList evalList(EvalState & state, Expr e); /* Print statistics. */ void printEvalStats(EvalState & state); diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 38d403c9..d5a14f51 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -59,6 +59,7 @@ inherit { return INHERIT; } \|\| { return OR; } \-\> { return IMPL; } \/\/ { return UPDATE; } +\+\+ { return CONCAT; } {ID} { yylval->t = ATmake("", yytext); return ID; /* !!! alloc */ } {INT} { int n = atoi(yytext); /* !!! overflow */ diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def index 19601847..b384ff7c 100644 --- a/src/libexpr/nixexpr-ast.def +++ b/src/libexpr/nixexpr-ast.def @@ -18,6 +18,7 @@ OpUpdate | Expr Expr | Expr | SubPath | Expr Expr | Expr | OpHasAttr | Expr string | Expr | OpPlus | Expr Expr | Expr | +OpConcat | Expr Expr | Expr | Call | Expr Expr | Expr | Select | Expr string | Expr | Var | string | Expr | diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 1f204a22..23f1965e 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -64,6 +64,7 @@ static Pos makeCurPos(YYLTYPE * loc, void * data) %right UPDATE %left NEG %left '+' +%left CONCAT %nonassoc '?' %nonassoc '~' @@ -102,6 +103,7 @@ expr_op | expr_op '~' expr_op { $$ = makeSubPath($1, $3); } | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); } | expr_op '+' expr_op { $$ = makeOpPlus($1, $3); } + | expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); } | expr_app ; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b0986028..5736a7f9 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -414,17 +414,13 @@ static Expr primIsNull(EvalState & state, const ATermVector & args) static Expr primMap(EvalState & state, const ATermVector & args) { Expr fun = evalExpr(state, args[0]); - Expr list = evalExpr(state, args[1]); + ATermList list = evalList(state, args[1]); - ATermList list2; - if (!matchList(list, list2)) - throw Error("`map' expects a list as its second argument"); + ATermList res = ATempty; + for (ATermIterator i(list); i; ++i) + res = ATinsert(res, makeCall(fun, *i)); - ATermList list3 = ATempty; - for (ATermIterator i(list2); i; ++i) - list3 = ATinsert(list3, makeCall(fun, *i)); - - return makeList(ATreverse(list3)); + return makeList(ATreverse(res)); } @@ -449,9 +445,7 @@ static Expr primRemoveAttrs(EvalState & state, const ATermVector & args) ATermMap attrs; queryAllAttrs(evalExpr(state, args[0]), attrs, true); - ATermList list; - if (!matchList(evalExpr(state, args[1]), list)) - throw Error("`removeAttrs' expects a list as its second argument"); + ATermList list = evalList(state, args[1]); for (ATermIterator i(list); i; ++i) /* It's not an error for *i not to exist. */ diff --git a/tests/lang/eval-okay-concat.exp b/tests/lang/eval-okay-concat.exp new file mode 100644 index 00000000..359ccef8 --- /dev/null +++ b/tests/lang/eval-okay-concat.exp @@ -0,0 +1 @@ +List([Int(1),Int(2),Int(3),Int(4),Int(5),Int(6),Int(7),Int(8),Int(9)]) diff --git a/tests/lang/eval-okay-concat.nix b/tests/lang/eval-okay-concat.nix new file mode 100644 index 00000000..d158a9bf --- /dev/null +++ b/tests/lang/eval-okay-concat.nix @@ -0,0 +1 @@ +[1 2 3] ++ [4 5 6] ++ [7 8 9]