Skip to content

Commit 1bb9ba1

Browse files
authored
Fix mlr -s shebang doc and reject arrays/maps in contains/index (#1658) (#2058)
The shebang example `#!/usr/bin/env mlr -s` does not work on Linux because env(1) does not accept arguments to the interpreter; use `#!/usr/bin/env -S mlr -s` instead. `contains` and `index` previously stringified their inputs silently, so `contains([1,2], $foo)` would match against the literal string `[1, 2]` and produce surprising results. Both now return a type-error when either argument is an array or map; help text points users at `any(arr, func(e){return e == x})` for membership tests.
1 parent e0cf596 commit 1bb9ba1

11 files changed

Lines changed: 32 additions & 8 deletions

File tree

docs/src/example-mlr-s-script

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env mlr -s
1+
#!/usr/bin/env -S mlr -s
22
--c2p
33
filter '$quantity != 20' # Here is a comment
44
then count-distinct -f shape

docs/src/online-help.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Flags:
6161
mlr help format-conversion-keystroke-saver-flags
6262
mlr help json-only-flags
6363
mlr help legacy-flags
64+
mlr help markdown-only-flags
6465
mlr help miscellaneous-flags
6566
mlr help output-colorization-flags
6667
mlr help pprint-only-flags

docs/src/reference-dsl-builtin-functions.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,12 +1217,13 @@ collapse_whitespace (class=string #args=1) Strip repeated whitespace from strin
12171217

12181218
### contains
12191219
<pre class="pre-non-highlight-non-pair">
1220-
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.
1220+
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})`.
12211221
Examples:
12221222
contains("abcde", "e") gives true
12231223
contains("abcde", "x") gives false
12241224
contains(12345, 34) gives true
12251225
contains("forêt", "ê") gives true
1226+
contains([1,2,3], 2) gives (error)
12261227
</pre>
12271228

12281229

@@ -1258,12 +1259,13 @@ gsub("prefix4529:suffix8567", "(....ix)([0-9]+)", "[\1 : \2]") gives "[prefix :
12581259

12591260
### index
12601261
<pre class="pre-non-highlight-non-pair">
1261-
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.
1262+
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.
12621263
Examples:
12631264
index("abcde", "e") gives 5
12641265
index("abcde", "x") gives -1
12651266
index(12345, 34) gives 3
12661267
index("forêt", "t") gives 5
1268+
index([1,2,3], 2) gives (error)
12671269
</pre>
12681270

12691271

docs/src/scripting.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ circle 3 0.3
3535
Typing this out can get a bit old, if the only thing that changes for you is the filename. Some options include:
3636

3737
* On Linux/Mac/etc you can make a script with `#!/bin/sh` which invokes Miller as part of the shell-script body.
38-
* On Linux/Mac/etc you can make a script with `#!/usr/bin/env mlr -s` which invokes Miller.
38+
* On Linux/Mac/etc you can make a script with `#!/usr/bin/env -S mlr -s` which invokes Miller.
3939
* 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`.
4040

4141
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
135135
<b>cat ./example-mlr-s-script</b>
136136
</pre>
137137
<pre class="pre-non-highlight-in-pair">
138-
#!/usr/bin/env mlr -s
138+
#!/usr/bin/env -S mlr -s
139139
--c2p
140140
filter '$quantity != 20' # Here is a comment
141141
then count-distinct -f shape

docs/src/scripting.md.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ GENMD-EOF
1313
Typing this out can get a bit old, if the only thing that changes for you is the filename. Some options include:
1414

1515
* On Linux/Mac/etc you can make a script with `#!/bin/sh` which invokes Miller as part of the shell-script body.
16-
* On Linux/Mac/etc you can make a script with `#!/usr/bin/env mlr -s` which invokes Miller.
16+
* On Linux/Mac/etc you can make a script with `#!/usr/bin/env -S mlr -s` which invokes Miller.
1717
* 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`.
1818

1919
Let's look at examples of each.

pkg/bifs/strings.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ func BIF_index(input1, input2 *mlrval.Mlrval) *mlrval.Mlrval {
134134
if input1.IsError() {
135135
return mlrval.FromTypeErrorUnary("index", input1)
136136
}
137+
if input1.IsArrayOrMap() || input2.IsArrayOrMap() {
138+
return mlrval.FromTypeErrorBinary("index", input1, input2)
139+
}
137140
sinput1 := input1.String()
138141
sinput2 := input2.String()
139142

@@ -156,6 +159,9 @@ func BIF_contains(input1, input2 *mlrval.Mlrval) *mlrval.Mlrval {
156159
if input1.IsError() {
157160
return input1
158161
}
162+
if input1.IsArrayOrMap() || input2.IsArrayOrMap() {
163+
return mlrval.FromTypeErrorBinary("contains", input1, input2)
164+
}
159165

160166
return mlrval.FromBool(strings.Contains(input1.String(), input2.String()))
161167
}

pkg/dsl/cst/builtin_function_manager.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,25 +581,27 @@ Arrays are new in Miller 6; the substr function is older.`,
581581
{
582582
name: "index",
583583
class: FUNC_CLASS_STRING,
584-
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.`,
584+
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.`,
585585
binaryFunc: bifs.BIF_index,
586586
examples: []string{
587587
`index("abcde", "e") gives 5`,
588588
`index("abcde", "x") gives -1`,
589589
`index(12345, 34) gives 3`,
590590
`index("forêt", "t") gives 5`,
591+
`index([1,2,3], 2) gives (error)`,
591592
},
592593
},
593594
{
594595
name: "contains",
595596
class: FUNC_CLASS_STRING,
596-
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.`,
597+
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})`" + `.`,
597598
binaryFunc: bifs.BIF_contains,
598599
examples: []string{
599600
`contains("abcde", "e") gives true`,
600601
`contains("abcde", "x") gives false`,
601602
`contains(12345, 34) gives true`,
602603
`contains("forêt", "ê") gives true`,
604+
`contains([1,2,3], 2) gives (error)`,
603605
},
604606
},
605607

test/cases/dsl-contains/0002/cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mlr -n put -q -f ${CASEDIR}/mlr

test/cases/dsl-contains/0002/experr

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(error)
2+
(error)
3+
(error)
4+
(error)
5+
(error)

0 commit comments

Comments
 (0)