diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml index e32bc2da..53c8d4bf 100644 --- a/doc/manual/release-notes.xml +++ b/doc/manual/release-notes.xml @@ -55,7 +55,10 @@ TODO: new built-ins builtins.attrNames, - builtins.filterSource. + builtins.filterSource, + builtins.sub, + builtins.stringLength, + builtins.substring. diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b80e2069..01806a6c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -723,6 +723,14 @@ static Expr primAdd(EvalState & state, const ATermVector & args) } +static Expr primSub(EvalState & state, const ATermVector & args) +{ + int i1 = evalInt(state, args[0]); + int i2 = evalInt(state, args[1]); + return makeInt(i1 - i2); +} + + static Expr primLessThan(EvalState & state, const ATermVector & args) { int i1 = evalInt(state, args[0]); @@ -779,6 +787,36 @@ static Expr primFilterSource(EvalState & state, const ATermVector & args) } +/************************************************************* + * String manipulation + *************************************************************/ + + +/* `substr start len str' returns the substring of `str' starting at + character position `min(start, stringLength str)' inclusive and + ending at `min(start + len, stringLength str)'. `start' must be + non-negative. */ +static Expr prim_substring(EvalState & state, const ATermVector & args) +{ + int start = evalInt(state, args[0]); + int len = evalInt(state, args[1]); + PathSet context; + string s = coerceToString(state, args[2], context); + + if (start < 0) throw EvalError("negative start position in `substring'"); + + return makeStr(string(s, start, len), context); +} + + +static Expr prim_stringLength(EvalState & state, const ATermVector & args) +{ + PathSet context; + string s = coerceToString(state, args[0], context); + return makeInt(s.size()); +} + + void EvalState::addPrimOps() { addPrimOp("builtins", 0, primBuiltins); @@ -813,10 +851,14 @@ void EvalState::addPrimOps() addPrimOp("removeAttrs", 2, primRemoveAttrs); addPrimOp("relativise", 2, primRelativise); addPrimOp("__add", 2, primAdd); + addPrimOp("__sub", 2, primSub); addPrimOp("__lessThan", 2, primLessThan); addPrimOp("__toFile", 2, primToFile); addPrimOp("__filterSource", 2, primFilterSource); + + addPrimOp("__substring", 3, prim_substring); + addPrimOp("__stringLength", 1, prim_stringLength); } - + } diff --git a/tests/filter-source.nix.in b/tests/filter-source.nix.in index 8493835c..6fd49479 100644 --- a/tests/filter-source.nix.in +++ b/tests/filter-source.nix.in @@ -4,6 +4,9 @@ derivation { builder = "@shell@"; args = ["-e" "-x" (builtins.toFile "builder" "PATH=@testPath@; ln -s $input $out")]; input = - let filter = path: type: type != "symlink" && baseNameOf (toString path) != "foo"; + let filter = path: type: + type != "symlink" + && baseNameOf path != "foo" + && !((import ./lang/lib.nix).hasSuffix ".bak" (baseNameOf path)); in builtins.filterSource filter ./test-tmp/filterin; } diff --git a/tests/filter-source.sh b/tests/filter-source.sh index 96ccaedc..73f35302 100644 --- a/tests/filter-source.sh +++ b/tests/filter-source.sh @@ -5,6 +5,9 @@ mkdir $TEST_ROOT/filterin mkdir $TEST_ROOT/filterin/foo touch $TEST_ROOT/filterin/foo/bar touch $TEST_ROOT/filterin/xyzzy +touch $TEST_ROOT/filterin/b +touch $TEST_ROOT/filterin/bak +touch $TEST_ROOT/filterin/bla.c.bak ln -s xyzzy $TEST_ROOT/filterin/link $NIX_BIN_DIR/nix-build ./filter-source.nix -o $TEST_ROOT/filterout @@ -12,4 +15,6 @@ $NIX_BIN_DIR/nix-build ./filter-source.nix -o $TEST_ROOT/filterout set -x test ! -e $TEST_ROOT/filterout/foo/bar test -e $TEST_ROOT/filterout/xyzzy +test -e $TEST_ROOT/filterout/bak +test ! -e $TEST_ROOT/filterout/bla.c.bak test ! -L $TEST_ROOT/filterout/link diff --git a/tests/lang/eval-fail-substring.nix b/tests/lang/eval-fail-substring.nix new file mode 100644 index 00000000..f37c2bc0 --- /dev/null +++ b/tests/lang/eval-fail-substring.nix @@ -0,0 +1 @@ +builtins.substring (builtins.sub 0 1) 1 "x" diff --git a/tests/lang/eval-okay-substring.exp b/tests/lang/eval-okay-substring.exp new file mode 100644 index 00000000..d6098476 --- /dev/null +++ b/tests/lang/eval-okay-substring.exp @@ -0,0 +1 @@ +Str("ooxfoobarybarzobaabb",[]) diff --git a/tests/lang/eval-okay-substring.nix b/tests/lang/eval-okay-substring.nix new file mode 100644 index 00000000..184d7258 --- /dev/null +++ b/tests/lang/eval-okay-substring.nix @@ -0,0 +1,19 @@ +with builtins; + +let + + s = "foobar"; + +in + +substring 1 2 s ++ "x" ++ substring 0 (stringLength s) s ++ "y" ++ substring 3 100 s ++ "z" ++ substring 2 (sub (stringLength s) 3) s ++ "a" ++ substring 3 0 s ++ "b" ++ substring 3 1 s diff --git a/tests/lang/lib.nix b/tests/lang/lib.nix index f888453f..ec705299 100644 --- a/tests/lang/lib.nix +++ b/tests/lang/lib.nix @@ -1,18 +1,26 @@ +with builtins; + rec { fold = op: nul: list: if list == [] then nul - else op (builtins.head list) (fold op nul (builtins.tail list)); + else op (head list) (fold op nul (tail list)); concat = fold (x: y: x + y) ""; flatten = x: - if builtins.isList x + if isList x then fold (x: y: (flatten x) ++ y) [] x else [x]; - sum = fold (x: y: builtins.add x y) 0; + sum = fold (x: y: add x y) 0; + + hasSuffix = ext: fileName: + let lenFileName = stringLength fileName; + lenExt = stringLength ext; + in !(lessThan lenFileName lenExt) && + substring (sub lenFileName lenExt) lenFileName fileName == ext; }