diff --git a/go/src/miller/dsl/cst/function_manager.go b/go/src/miller/dsl/cst/function_manager.go index 16a28d6cf..d903cd0c1 100644 --- a/go/src/miller/dsl/cst/function_manager.go +++ b/go/src/miller/dsl/cst/function_manager.go @@ -341,6 +341,10 @@ var BUILTIN_FUNCTION_LOOKUP_TABLE = []FunctionInfo{ name: "?:", ternaryFunc: TernaryShortCircuitPlaceholder, }, + { + name: "ssub", + ternaryFunc: types.MlrvalSsub, + }, // Variadic built-in functions { diff --git a/go/src/miller/types/mlrval_accessors.go b/go/src/miller/types/mlrval_accessors.go index 8c9b833de..c1334a4f7 100644 --- a/go/src/miller/types/mlrval_accessors.go +++ b/go/src/miller/types/mlrval_accessors.go @@ -14,6 +14,10 @@ func GetTypeName(mvtype MVType) string { } // ---------------------------------------------------------------- +func (this *Mlrval) IsErrorOrAbsent() bool { + return this.mvtype == MT_ERROR || this.mvtype == MT_ABSENT +} + func (this *Mlrval) IsError() bool { return this.mvtype == MT_ERROR } @@ -34,6 +38,10 @@ func (this *Mlrval) IsString() bool { return this.mvtype == MT_STRING } +func (this *Mlrval) IsStringOrVoid() bool { + return this.mvtype == MT_STRING || this.mvtype == MT_VOID +} + func (this *Mlrval) IsInt() bool { return this.mvtype == MT_INT } diff --git a/go/src/miller/types/mlrval_functions.go b/go/src/miller/types/mlrval_functions.go index 8a991afa4..b9c9eb06d 100644 --- a/go/src/miller/types/mlrval_functions.go +++ b/go/src/miller/types/mlrval_functions.go @@ -2,6 +2,7 @@ package types import ( "math" + "strings" "time" "miller/lib" @@ -1704,3 +1705,28 @@ func NumericAscendingComparator(ma *Mlrval, mb *Mlrval) int { func NumericDescendingComparator(ma *Mlrval, mb *Mlrval) int { return NumericAscendingComparator(mb, ma) } + +// ================================================================ +func MlrvalSsub(ma, mb, mc *Mlrval) Mlrval { + if ma.IsErrorOrAbsent() { + return *ma + } + if mb.IsErrorOrAbsent() { + return *mb + } + if mb.IsErrorOrAbsent() { + return *mc + } + if !ma.IsStringOrVoid() { + return MlrvalFromError() + } + if !mb.IsStringOrVoid() { + return MlrvalFromError() + } + if !mc.IsStringOrVoid() { + return MlrvalFromError() + } + return MlrvalFromString( + strings.Replace(ma.printrep, mb.printrep, mc.printrep, 1), + ) +} diff --git a/go/u/try-cst b/go/u/try-cst index 279aa82b3..67f2b240d 100755 --- a/go/u/try-cst +++ b/go/u/try-cst @@ -230,3 +230,5 @@ echo; run_mlr --from u/s.dkvp put 'if (NR == 1) { $z = 100 } else { $z = 900 }' echo; run_mlr --from u/s.dkvp put 'if (NR == 1) { $z = 100 } elif (NR == 2) { $z = 200 }' echo; run_mlr --from u/s.dkvp put 'if (NR == 1) { $z = 100 } elif (NR == 2) { $z = 200 } else { $z = 900 }' echo; run_mlr --from u/s.dkvp put 'if (NR == 1) { $z = 100 } elif (NR == 2) { $z = 200 } elif (NR == 3) { $z = 300 } else { $z = 900 }' + +echo x=eeee | run_mlr put '$y=ssub($x, "e", "X")' diff --git a/go/u/try-cst.out b/go/u/try-cst.out index 65d5d7510..5630ea15a 100644 --- a/go/u/try-cst.out +++ b/go/u/try-cst.out @@ -2416,3 +2416,7 @@ a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533,z=100 a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,z=200 a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776,z=300 a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463,z=900 + +---------------------------------------------------------------- +mlr put $y=ssub($x, "e", "X") +x=eeee,y=Xeee diff --git a/go/u/try-verbs.out b/go/u/try-verbs.out index 22babe586..a5b4e9e9c 100644 --- a/go/u/try-verbs.out +++ b/go/u/try-verbs.out @@ -352,3 +352,22 @@ mlr --json --from u/needs-sorting.json sort-within-records "b": 2, "c": 3 } + +---------------------------------------------------------------- +mlr --json --from u/needs-regularize.json regularize +{ + "b": 2, + "c": 3, + "a": 1 +} +{ + "b": 2, + "c": 3, + "a": 1 +} +{ + "d": 4, + "a": 1, + "b": 2, + "c": 3 +}