difftreelog
Merge pull request #124 from pawelbeza/missing-std-features
in: master
10 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -375,7 +375,9 @@
"serde",
"serde_json",
"serde_yaml_with_quirks",
+ "sha1",
"sha2",
+ "sha3",
"structdump",
]
@@ -388,6 +390,15 @@
]
[[package]]
+name = "keccak"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940"
+dependencies = [
+ "cpufeatures",
+]
+
+[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -676,6 +687,17 @@
]
[[package]]
+name = "sha1"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
name = "sha2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -687,6 +709,16 @@
]
[[package]]
+name = "sha3"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
+dependencies = [
+ "digest",
+ "keccak",
+]
+
+[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
crates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-stdlib/Cargo.toml
+++ b/crates/jrsonnet-stdlib/Cargo.toml
@@ -32,8 +32,12 @@
# std.md5
md5 = "0.7.0"
+# std.sha1
+sha1 = "0.10.5"
# std.sha256, std.sha512
sha2 = "0.10.6"
+# std.sha3
+sha3 = "0.10.8"
# std.base64
base64 = "0.21.0"
# std.parseJson
crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth1#![allow(non_snake_case)]23use jrsonnet_evaluator::{4 error::{ErrorKind::RuntimeError, Result},5 function::{builtin, FuncVal},6 throw,7 typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},8 val::{equals, ArrValue, IndexableVal, StrValue},9 Either, IStr, Thunk, Val,10};1112pub(crate) fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {13 if let Some(on_empty) = on_empty {14 on_empty.evaluate()15 } else {16 throw!("expected non-empty array")17 }18}1920#[builtin]21pub fn builtin_make_array(sz: BoundedI32<0, { i32::MAX }>, func: FuncVal) -> Result<ArrValue> {22 if *sz == 0 {23 return Ok(ArrValue::empty());24 }25 if let Some(trivial) = func.evaluate_trivial() {26 let mut out = Vec::with_capacity(*sz as usize);27 for _ in 0..*sz {28 out.push(trivial.clone())29 }30 Ok(ArrValue::eager(out))31 } else {32 Ok(ArrValue::range_exclusive(0, *sz).map(func))33 }34}3536#[builtin]37pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Val> {38 Ok(match what {39 Either2::A(s) => Val::Str(StrValue::Flat(s.repeat(count).into())),40 Either2::B(arr) => Val::Arr(41 ArrValue::repeated(arr, count)42 .ok_or_else(|| RuntimeError("repeated length overflow".into()))?,43 ),44 })45}4647#[builtin]48pub fn builtin_slice(49 indexable: IndexableVal,50 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,51 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,52 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,53) -> Result<Val> {54 indexable.slice(index, end, step).map(Val::from)55}5657#[builtin]58pub fn builtin_map(func: FuncVal, arr: IndexableVal) -> ArrValue {59 let arr = arr.to_array();60 arr.map(func)61}6263#[builtin]64pub fn builtin_flatmap(65 func: NativeFn<((Either![String, Val],), Val)>,66 arr: IndexableVal,67) -> Result<IndexableVal> {68 use std::fmt::Write;69 match arr {70 IndexableVal::Str(str) => {71 let mut out = String::new();72 for c in str.chars() {73 match func(Either2::A(c.to_string()))? {74 Val::Str(o) => write!(out, "{o}").unwrap(),75 Val::Null => continue,76 _ => throw!("in std.join all items should be strings"),77 };78 }79 Ok(IndexableVal::Str(out.into()))80 }81 IndexableVal::Arr(a) => {82 let mut out = Vec::new();83 for el in a.iter() {84 let el = el?;85 match func(Either2::B(el))? {86 Val::Arr(o) => {87 for oe in o.iter() {88 out.push(oe?);89 }90 }91 Val::Null => continue,92 _ => throw!("in std.join all items should be arrays"),93 };94 }95 Ok(IndexableVal::Arr(out.into()))96 }97 }98}99100#[builtin]101pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {102 arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),), false)?))103}104105#[builtin]106pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {107 let mut acc = init;108 for i in arr.iter() {109 acc = func.evaluate_simple(&(acc, i?), false)?;110 }111 Ok(acc)112}113114#[builtin]115pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {116 let mut acc = init;117 for i in arr.iter().rev() {118 acc = func.evaluate_simple(&(i?, acc), false)?;119 }120 Ok(acc)121}122123#[builtin]124pub fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {125 if to < from {126 return Ok(ArrValue::empty());127 }128 Ok(ArrValue::range_inclusive(from, to))129}130131#[builtin]132pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {133 use std::fmt::Write;134 Ok(match sep {135 IndexableVal::Arr(joiner_items) => {136 let mut out = Vec::new();137138 let mut first = true;139 for item in arr.iter() {140 let item = item?.clone();141 if let Val::Arr(items) = item {142 if !first {143 out.reserve(joiner_items.len());144 // TODO: extend145 for item in joiner_items.iter() {146 out.push(item?);147 }148 }149 first = false;150 out.reserve(items.len());151 for item in items.iter() {152 out.push(item?);153 }154 } else if matches!(item, Val::Null) {155 continue;156 } else {157 throw!("in std.join all items should be arrays");158 }159 }160161 IndexableVal::Arr(out.into())162 }163 IndexableVal::Str(sep) => {164 let mut out = String::new();165166 let mut first = true;167 for item in arr.iter() {168 let item = item?.clone();169 if let Val::Str(item) = item {170 if !first {171 out += &sep;172 }173 first = false;174 write!(out, "{item}").unwrap()175 } else if matches!(item, Val::Null) {176 continue;177 } else {178 throw!("in std.join all items should be strings");179 }180 }181182 IndexableVal::Str(out.into())183 }184 })185}186187#[builtin]188pub fn builtin_reverse(arr: ArrValue) -> ArrValue {189 arr.reversed()190}191192#[builtin]193pub fn builtin_any(arr: ArrValue) -> Result<bool> {194 for v in arr.iter() {195 let v = bool::from_untyped(v?)?;196 if v {197 return Ok(true);198 }199 }200 Ok(false)201}202203#[builtin]204pub fn builtin_all(arr: ArrValue) -> Result<bool> {205 for v in arr.iter() {206 let v = bool::from_untyped(v?)?;207 if !v {208 return Ok(false);209 }210 }211 Ok(true)212}213214#[builtin]215pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {216 match arr {217 IndexableVal::Str(str) => {218 let x: IStr = IStr::from_untyped(x)?;219 Ok(!x.is_empty() && str.contains(&*x))220 }221 IndexableVal::Arr(a) => {222 for item in a.iter() {223 let item = item?;224 if equals(&item, &x)? {225 return Ok(true);226 }227 }228 Ok(false)229 }230 }231}232233#[builtin]234pub fn builtin_contains(arr: IndexableVal, elem: Val) -> Result<bool> {235 builtin_member(arr, elem)236}237238#[builtin]239pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {240 let mut count = 0;241 for item in arr.iter() {242 if equals(&item?, &x)? {243 count += 1;244 }245 }246 Ok(count)247}248249#[builtin]250pub fn builtin_avg(arr: Vec<f64>, onEmpty: Option<Thunk<Val>>) -> Result<Val> {251 if arr.is_empty() {252 return eval_on_empty(onEmpty);253 }254 Ok(Val::Num(arr.iter().sum::<f64>() / (arr.len() as f64)))255}1#![allow(non_snake_case)]23use jrsonnet_evaluator::{4 error::{ErrorKind::RuntimeError, Result},5 function::{builtin, FuncVal},6 throw,7 typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},8 val::{equals, ArrValue, IndexableVal, StrValue},9 Either, IStr, Thunk, Val,10};1112pub(crate) fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {13 if let Some(on_empty) = on_empty {14 on_empty.evaluate()15 } else {16 throw!("expected non-empty array")17 }18}1920#[builtin]21pub fn builtin_make_array(sz: BoundedI32<0, { i32::MAX }>, func: FuncVal) -> Result<ArrValue> {22 if *sz == 0 {23 return Ok(ArrValue::empty());24 }25 if let Some(trivial) = func.evaluate_trivial() {26 let mut out = Vec::with_capacity(*sz as usize);27 for _ in 0..*sz {28 out.push(trivial.clone())29 }30 Ok(ArrValue::eager(out))31 } else {32 Ok(ArrValue::range_exclusive(0, *sz).map(func))33 }34}3536#[builtin]37pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Val> {38 Ok(match what {39 Either2::A(s) => Val::Str(StrValue::Flat(s.repeat(count).into())),40 Either2::B(arr) => Val::Arr(41 ArrValue::repeated(arr, count)42 .ok_or_else(|| RuntimeError("repeated length overflow".into()))?,43 ),44 })45}4647#[builtin]48pub fn builtin_slice(49 indexable: IndexableVal,50 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,51 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,52 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,53) -> Result<Val> {54 indexable.slice(index, end, step).map(Val::from)55}5657#[builtin]58pub fn builtin_map(func: FuncVal, arr: IndexableVal) -> ArrValue {59 let arr = arr.to_array();60 arr.map(func)61}6263#[builtin]64pub fn builtin_flatmap(65 func: NativeFn<((Either![String, Val],), Val)>,66 arr: IndexableVal,67) -> Result<IndexableVal> {68 use std::fmt::Write;69 match arr {70 IndexableVal::Str(str) => {71 let mut out = String::new();72 for c in str.chars() {73 match func(Either2::A(c.to_string()))? {74 Val::Str(o) => write!(out, "{o}").unwrap(),75 Val::Null => continue,76 _ => throw!("in std.join all items should be strings"),77 };78 }79 Ok(IndexableVal::Str(out.into()))80 }81 IndexableVal::Arr(a) => {82 let mut out = Vec::new();83 for el in a.iter() {84 let el = el?;85 match func(Either2::B(el))? {86 Val::Arr(o) => {87 for oe in o.iter() {88 out.push(oe?);89 }90 }91 Val::Null => continue,92 _ => throw!("in std.join all items should be arrays"),93 };94 }95 Ok(IndexableVal::Arr(out.into()))96 }97 }98}99100#[builtin]101pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {102 arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),), false)?))103}104105#[builtin]106pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {107 let mut acc = init;108 for i in arr.iter() {109 acc = func.evaluate_simple(&(acc, i?), false)?;110 }111 Ok(acc)112}113114#[builtin]115pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {116 let mut acc = init;117 for i in arr.iter().rev() {118 acc = func.evaluate_simple(&(i?, acc), false)?;119 }120 Ok(acc)121}122123#[builtin]124pub fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {125 if to < from {126 return Ok(ArrValue::empty());127 }128 Ok(ArrValue::range_inclusive(from, to))129}130131#[builtin]132pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {133 use std::fmt::Write;134 Ok(match sep {135 IndexableVal::Arr(joiner_items) => {136 let mut out = Vec::new();137138 let mut first = true;139 for item in arr.iter() {140 let item = item?.clone();141 if let Val::Arr(items) = item {142 if !first {143 out.reserve(joiner_items.len());144 // TODO: extend145 for item in joiner_items.iter() {146 out.push(item?);147 }148 }149 first = false;150 out.reserve(items.len());151 for item in items.iter() {152 out.push(item?);153 }154 } else if matches!(item, Val::Null) {155 continue;156 } else {157 throw!("in std.join all items should be arrays");158 }159 }160161 IndexableVal::Arr(out.into())162 }163 IndexableVal::Str(sep) => {164 let mut out = String::new();165166 let mut first = true;167 for item in arr.iter() {168 let item = item?.clone();169 if let Val::Str(item) = item {170 if !first {171 out += &sep;172 }173 first = false;174 write!(out, "{item}").unwrap()175 } else if matches!(item, Val::Null) {176 continue;177 } else {178 throw!("in std.join all items should be strings");179 }180 }181182 IndexableVal::Str(out.into())183 }184 })185}186187#[builtin]188pub fn builtin_reverse(arr: ArrValue) -> ArrValue {189 arr.reversed()190}191192#[builtin]193pub fn builtin_any(arr: ArrValue) -> Result<bool> {194 for v in arr.iter() {195 let v = bool::from_untyped(v?)?;196 if v {197 return Ok(true);198 }199 }200 Ok(false)201}202203#[builtin]204pub fn builtin_all(arr: ArrValue) -> Result<bool> {205 for v in arr.iter() {206 let v = bool::from_untyped(v?)?;207 if !v {208 return Ok(false);209 }210 }211 Ok(true)212}213214#[builtin]215pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {216 match arr {217 IndexableVal::Str(str) => {218 let x: IStr = IStr::from_untyped(x)?;219 Ok(!x.is_empty() && str.contains(&*x))220 }221 IndexableVal::Arr(a) => {222 for item in a.iter() {223 let item = item?;224 if equals(&item, &x)? {225 return Ok(true);226 }227 }228 Ok(false)229 }230 }231}232233#[builtin]234pub fn builtin_contains(arr: IndexableVal, elem: Val) -> Result<bool> {235 builtin_member(arr, elem)236}237238#[builtin]239pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {240 let mut count = 0;241 for item in arr.iter() {242 if equals(&item?, &x)? {243 count += 1;244 }245 }246 Ok(count)247}248249#[builtin]250pub fn builtin_avg(arr: Vec<f64>, onEmpty: Option<Thunk<Val>>) -> Result<Val> {251 if arr.is_empty() {252 return eval_on_empty(onEmpty);253 }254 Ok(Val::Num(arr.iter().sum::<f64>() / (arr.len() as f64)))255}256257#[builtin]258pub fn builtin_remove_at(259 arr: ArrValue,260 index: usize,261) -> Result<ArrValue> {262 let newArrLeft = arr.clone().slice(None, Some(index), None);263 let newArrRight = arr.clone().slice(Some(index + 1), None, None);264 return Ok(ArrValue::extended(265 newArrLeft.unwrap_or(ArrValue::empty()),266 newArrRight.unwrap_or(ArrValue::empty()))267 );268}269270#[builtin]271pub fn builtin_remove(arr: ArrValue, elem: Val) -> Result<ArrValue> {272 for (index, item) in arr.iter().enumerate() {273 if equals(&item?, &elem)? {274 return builtin_remove_at(arr.clone(), index) 275 }276 }277 Ok(arr)278}crates/jrsonnet-stdlib/src/hash.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/hash.rs
+++ b/crates/jrsonnet-stdlib/src/hash.rs
@@ -16,3 +16,15 @@
use sha2::digest::Digest;
format!("{:x}", sha2::Sha512::digest(s.as_bytes()))
}
+
+#[builtin]
+pub fn builtin_sha1(s: IStr) -> String {
+ use sha1::digest::Digest;
+ format!("{:x}", sha1::Sha1::digest(s.as_bytes()))
+}
+
+#[builtin]
+pub fn builtin_sha3(s: IStr) -> String {
+ use sha3::digest::Digest;
+ format!("{:x}", sha3::Sha3_512::digest(s.as_bytes()))
+}
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -83,6 +83,8 @@
("contains", builtin_member::INST),
("count", builtin_count::INST),
("avg", builtin_avg::INST),
+ ("removeAt", builtin_remove_at::INST),
+ ("remove", builtin_remove::INST),
// Math
("abs", builtin_abs::INST),
("sign", builtin_sign::INST),
@@ -104,11 +106,17 @@
("exp", builtin_exp::INST),
("mantissa", builtin_mantissa::INST),
("exponent", builtin_exponent::INST),
+ ("round", builtin_round::INST),
+ ("isEven", builtin_is_even::INST),
+ ("isOdd", builtin_is_odd::INST),
+ ("isInteger", builtin_is_integer::INST),
+ ("isDecimal", builtin_is_decimal::INST),
// Operator
("mod", builtin_mod::INST),
("primitiveEquals", builtin_primitive_equals::INST),
("equals", builtin_equals::INST),
("xor", builtin_xor::INST),
+ ("xnor", builtin_xnor::INST),
("format", builtin_format::INST),
// Sort
("sort", builtin_sort::INST),
@@ -118,8 +126,10 @@
("maxArray", builtin_max_array::INST),
// Hash
("md5", builtin_md5::INST),
+ ("sha1", builtin_sha1::INST),
("sha256", builtin_sha256::INST),
("sha512", builtin_sha512::INST),
+ ("sha3", builtin_sha3::INST),
// Encoding
("encodeUTF8", builtin_encode_utf8::INST),
("decodeUTF8", builtin_decode_utf8::INST),
@@ -129,6 +139,7 @@
// Objects
("objectFieldsEx", builtin_object_fields_ex::INST),
("objectHasEx", builtin_object_has_ex::INST),
+ ("objectRemoveKey", builtin_object_remove_key::INST),
// Manifest
("escapeStringJson", builtin_escape_string_json::INST),
("manifestJsonEx", builtin_manifest_json_ex::INST),
@@ -142,6 +153,8 @@
("substr", builtin_substr::INST),
("char", builtin_char::INST),
("strReplace", builtin_str_replace::INST),
+ ("isEmpty", builtin_is_empty::INST),
+ ("equalsIgnoreCase", builtin_equals_ignore_case::INST),
("splitLimit", builtin_splitlimit::INST),
("asciiUpper", builtin_ascii_upper::INST),
("asciiLower", builtin_ascii_lower::INST),
crates/jrsonnet-stdlib/src/math.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/math.rs
+++ b/crates/jrsonnet-stdlib/src/math.rs
@@ -114,3 +114,28 @@
pub fn builtin_exponent(x: f64) -> i16 {
frexp(x).1
}
+
+#[builtin]
+pub fn builtin_round(x: f64) -> f64 {
+ x.round()
+}
+
+#[builtin]
+pub fn builtin_is_even(x: f64) -> bool {
+ builtin_round(x) % 2.0 == 0.0
+}
+
+#[builtin]
+pub fn builtin_is_odd(x: f64) -> bool {
+ builtin_round(x) % 2.0 == 1.0
+}
+
+#[builtin]
+pub fn builtin_is_integer(x: f64) -> bool {
+ builtin_round(x) == x
+}
+
+#[builtin]
+pub fn builtin_is_decimal(x: f64) -> bool {
+ builtin_round(x) != x
+}
crates/jrsonnet-stdlib/src/objects.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/objects.rs
+++ b/crates/jrsonnet-stdlib/src/objects.rs
@@ -1,9 +1,10 @@
use jrsonnet_evaluator::{
function::builtin,
val::{StrValue, Val},
- IStr, ObjValue,
+ IStr, ObjValue, ObjValueBuilder,
};
+
#[builtin]
pub fn builtin_object_fields_ex(
obj: ObjValue,
@@ -27,3 +28,16 @@
pub fn builtin_object_has_ex(obj: ObjValue, fname: IStr, hidden: bool) -> bool {
obj.has_field_ex(fname, hidden)
}
+
+#[builtin]
+pub fn builtin_object_remove_key(obj: ObjValue, key: IStr) -> ObjValue {
+ let mut new_obj = ObjValueBuilder::with_capacity(obj.len() - 1);
+ for (k, v) in obj.iter() {
+ if k == key {
+ continue
+ }
+ new_obj.member(k).value_unchecked(v.unwrap())
+ }
+
+ new_obj.build()
+}
crates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/operator.rs
+++ b/crates/jrsonnet-stdlib/src/operator.rs
@@ -39,6 +39,11 @@
}
#[builtin]
+pub fn builtin_xnor(x: bool, y: bool) -> bool {
+ x == y
+}
+
+#[builtin]
pub fn builtin_format(str: IStr, vals: Val) -> Result<String> {
std_format(&str, vals)
}
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -274,6 +274,12 @@
objectValuesAll(o)::
[o[k] for k in std.objectFieldsAll(o)],
+ objectKeysValues(o)::
+ [{ key: k, value: o[k] } for k in std.objectFields(o)],
+
+ objectKeysValuesAll(o)::
+ [{ key: k, value: o[k] } for k in std.objectFieldsAll(o)],
+
resolvePath(f, r)::
local arr = std.split(f, '/');
std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),
crates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -28,6 +28,16 @@
}
#[builtin]
+pub fn builtin_is_empty(str: String) -> bool {
+ str.is_empty()
+}
+
+#[builtin]
+pub fn builtin_equals_ignore_case(x: String, y: String) -> bool {
+ x.to_ascii_lowercase() == y.to_ascii_lowercase()
+}
+
+#[builtin]
pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> ArrValue {
use Either2::*;
match maxsplits {