diff --git a/docs/src/example-mlr-s-script b/docs/src/example-mlr-s-script index 50e3f5db4..69d1e1638 100755 --- a/docs/src/example-mlr-s-script +++ b/docs/src/example-mlr-s-script @@ -1,4 +1,4 @@ -#!/usr/bin/env mlr -s +#!/usr/bin/env -S mlr -s --c2p filter '$quantity != 20' # Here is a comment then count-distinct -f shape diff --git a/docs/src/online-help.md b/docs/src/online-help.md index bb8185e10..f112b1e18 100644 --- a/docs/src/online-help.md +++ b/docs/src/online-help.md @@ -61,6 +61,7 @@ Flags: mlr help format-conversion-keystroke-saver-flags mlr help json-only-flags mlr help legacy-flags + mlr help markdown-only-flags mlr help miscellaneous-flags mlr help output-colorization-flags mlr help pprint-only-flags diff --git a/docs/src/reference-dsl-builtin-functions.md b/docs/src/reference-dsl-builtin-functions.md index 1b3b1af40..fa93a240b 100644 --- a/docs/src/reference-dsl-builtin-functions.md +++ b/docs/src/reference-dsl-builtin-functions.md @@ -1217,12 +1217,13 @@ collapse_whitespace (class=string #args=1) Strip repeated whitespace from strin ### contains
-contains  (class=string #args=2) Returns true if the first argument contains the second as a substring. This is like saying `index(arg1, arg2) >= 0`but with less keystroking.
+contains  (class=string #args=2) Returns true if the first argument contains the second as a substring. This is like saying `index(arg1, arg2) >= 0` but with less keystroking. Stringifies non-string scalar inputs; raises an error if either argument is an array or map. To test for array membership, use `any`, e.g. `any([1,2,3], func(e) {return e == $foo})`.
 Examples:
 contains("abcde", "e") gives true
 contains("abcde", "x") gives false
 contains(12345, 34) gives true
 contains("forêt", "ê") gives true
+contains([1,2,3], 2) gives (error)
 
@@ -1258,12 +1259,13 @@ gsub("prefix4529:suffix8567", "(....ix)([0-9]+)", "[\1 : \2]") gives "[prefix : ### index
-index  (class=string #args=2) Returns the index (1-based) of the second argument within the first. Returns -1 if the second argument isn't a substring of the first. Stringifies non-string inputs. Uses UTF-8 encoding to count characters, not bytes.
+index  (class=string #args=2) Returns the index (1-based) of the second argument within the first. Returns -1 if the second argument isn't a substring of the first. Stringifies non-string scalar inputs; raises an error if either argument is an array or map. Uses UTF-8 encoding to count characters, not bytes.
 Examples:
 index("abcde", "e") gives 5
 index("abcde", "x") gives -1
 index(12345, 34) gives 3
 index("forêt", "t") gives 5
+index([1,2,3], 2) gives (error)
 
diff --git a/docs/src/scripting.md b/docs/src/scripting.md index 4766dcb50..953687c81 100644 --- a/docs/src/scripting.md +++ b/docs/src/scripting.md @@ -35,7 +35,7 @@ circle 3 0.3 Typing this out can get a bit old, if the only thing that changes for you is the filename. Some options include: * On Linux/Mac/etc you can make a script with `#!/bin/sh` which invokes Miller as part of the shell-script body. -* On Linux/Mac/etc you can make a script with `#!/usr/bin/env mlr -s` which invokes Miller. +* On Linux/Mac/etc you can make a script with `#!/usr/bin/env -S mlr -s` which invokes Miller. * On any platform you can put the reusable part of your command line into a text file (say `myflags.txt`), then `mlr -s myflags-txt filename-which-varies.csv`. Let's look at examples of each. @@ -135,7 +135,7 @@ Here instead of putting `#!/bin/bash` on the first line, we can put `mlr` direct cat ./example-mlr-s-script
-#!/usr/bin/env mlr -s
+#!/usr/bin/env -S mlr -s
 --c2p
 filter '$quantity != 20' # Here is a comment
 then count-distinct -f shape
diff --git a/docs/src/scripting.md.in b/docs/src/scripting.md.in
index f29fe8b63..5b9dee073 100644
--- a/docs/src/scripting.md.in
+++ b/docs/src/scripting.md.in
@@ -13,7 +13,7 @@ GENMD-EOF
 Typing this out can get a bit old, if the only thing that changes for you is the filename. Some options include:
 
 * On Linux/Mac/etc you can make a script with `#!/bin/sh` which invokes Miller as part of the shell-script body.
-* On Linux/Mac/etc you can make a script with `#!/usr/bin/env mlr -s` which invokes Miller.
+* On Linux/Mac/etc you can make a script with `#!/usr/bin/env -S mlr -s` which invokes Miller.
 * On any platform you can put the reusable part of your command line into a text file (say `myflags.txt`), then `mlr -s myflags-txt filename-which-varies.csv`.
 
 Let's look at examples of each.
diff --git a/pkg/bifs/strings.go b/pkg/bifs/strings.go
index 1cecb39fe..218a03df0 100644
--- a/pkg/bifs/strings.go
+++ b/pkg/bifs/strings.go
@@ -134,6 +134,9 @@ func BIF_index(input1, input2 *mlrval.Mlrval) *mlrval.Mlrval {
 	if input1.IsError() {
 		return mlrval.FromTypeErrorUnary("index", input1)
 	}
+	if input1.IsArrayOrMap() || input2.IsArrayOrMap() {
+		return mlrval.FromTypeErrorBinary("index", input1, input2)
+	}
 	sinput1 := input1.String()
 	sinput2 := input2.String()
 
@@ -156,6 +159,9 @@ func BIF_contains(input1, input2 *mlrval.Mlrval) *mlrval.Mlrval {
 	if input1.IsError() {
 		return input1
 	}
+	if input1.IsArrayOrMap() || input2.IsArrayOrMap() {
+		return mlrval.FromTypeErrorBinary("contains", input1, input2)
+	}
 
 	return mlrval.FromBool(strings.Contains(input1.String(), input2.String()))
 }
diff --git a/pkg/dsl/cst/builtin_function_manager.go b/pkg/dsl/cst/builtin_function_manager.go
index 073a06165..364f98fbd 100644
--- a/pkg/dsl/cst/builtin_function_manager.go
+++ b/pkg/dsl/cst/builtin_function_manager.go
@@ -581,25 +581,27 @@ Arrays are new in Miller 6; the substr function is older.`,
 		{
 			name:       "index",
 			class:      FUNC_CLASS_STRING,
-			help:       `Returns the index (1-based) of the second argument within the first. Returns -1 if the second argument isn't a substring of the first. Stringifies non-string inputs. Uses UTF-8 encoding to count characters, not bytes.`,
+			help:       `Returns the index (1-based) of the second argument within the first. Returns -1 if the second argument isn't a substring of the first. Stringifies non-string scalar inputs; raises an error if either argument is an array or map. Uses UTF-8 encoding to count characters, not bytes.`,
 			binaryFunc: bifs.BIF_index,
 			examples: []string{
 				`index("abcde", "e") gives 5`,
 				`index("abcde", "x") gives -1`,
 				`index(12345, 34) gives 3`,
 				`index("forêt", "t") gives 5`,
+				`index([1,2,3], 2) gives (error)`,
 			},
 		},
 		{
 			name:       "contains",
 			class:      FUNC_CLASS_STRING,
-			help:       `Returns true if the first argument contains the second as a substring. This is like saying ` + "`index(arg1, arg2) >= 0`" + `but with less keystroking.`,
+			help:       `Returns true if the first argument contains the second as a substring. This is like saying ` + "`index(arg1, arg2) >= 0`" + ` but with less keystroking. Stringifies non-string scalar inputs; raises an error if either argument is an array or map. To test for array membership, use ` + "`any`" + `, e.g. ` + "`any([1,2,3], func(e) {return e == $foo})`" + `.`,
 			binaryFunc: bifs.BIF_contains,
 			examples: []string{
 				`contains("abcde", "e") gives true`,
 				`contains("abcde", "x") gives false`,
 				`contains(12345, 34) gives true`,
 				`contains("forêt", "ê") gives true`,
+				`contains([1,2,3], 2) gives (error)`,
 			},
 		},
 
diff --git a/test/cases/dsl-contains/0002/cmd b/test/cases/dsl-contains/0002/cmd
new file mode 100644
index 000000000..df47a8b50
--- /dev/null
+++ b/test/cases/dsl-contains/0002/cmd
@@ -0,0 +1 @@
+mlr -n put -q -f ${CASEDIR}/mlr
diff --git a/test/cases/dsl-contains/0002/experr b/test/cases/dsl-contains/0002/experr
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/cases/dsl-contains/0002/expout b/test/cases/dsl-contains/0002/expout
new file mode 100644
index 000000000..3cb084ffc
--- /dev/null
+++ b/test/cases/dsl-contains/0002/expout
@@ -0,0 +1,5 @@
+(error)
+(error)
+(error)
+(error)
+(error)
diff --git a/test/cases/dsl-contains/0002/mlr b/test/cases/dsl-contains/0002/mlr
new file mode 100644
index 000000000..209d7c5c7
--- /dev/null
+++ b/test/cases/dsl-contains/0002/mlr
@@ -0,0 +1,7 @@
+end {
+    print contains([1,2,3], 2);
+    print contains("abc", [1,2,3]);
+    print contains({"a":1}, "a");
+    print index([1,2,3], 2);
+    print index("abc", {"k":1});
+}