Skip to content

Commit

Permalink
feat: add 2 more string functions: $split and $replace (#33)
Browse files Browse the repository at this point in the history
* feat: add replace fn

* feat: add split fn
  • Loading branch information
asennikov authored May 28, 2024
1 parent 1023a96 commit a2d1d4a
Show file tree
Hide file tree
Showing 34 changed files with 112 additions and 3 deletions.
12 changes: 12 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pub enum Error {
D1009MultipleKeys(usize, String),
D2014RangeOutOfBounds(usize, isize),
D3001StringNotFinite(usize),
D3010EmptyPattern(usize),
D3011NegativeLimit(usize),
D3020NegativeLimit(usize),
D3030NonNumericCast(usize, String),
D3060SqrtNegative(usize, String),
D3061PowUnrepresentable(usize, String, String),
Expand Down Expand Up @@ -106,6 +109,9 @@ impl Error {
Error::D1009MultipleKeys(..) => "D1009",
Error::D2014RangeOutOfBounds(..) => "D2014",
Error::D3001StringNotFinite(..) => "D3001",
Error::D3010EmptyPattern(..) => "D3010",
Error::D3011NegativeLimit(..) => "D3011",
Error::D3020NegativeLimit(..) => "D3020",
Error::D3030NonNumericCast(..) => "D3030",
Error::D3060SqrtNegative(..) => "D3060",
Error::D3061PowUnrepresentable(..) => "D3061",
Expand Down Expand Up @@ -196,6 +202,12 @@ impl fmt::Display for Error {
write!(f, "{}: The size of the sequence allocated by the range operator (..) must not exceed 1e7. Attempted to allocate {}", p, s),
D3001StringNotFinite(ref p) =>
write!(f, "{}: Attempting to invoke string function on Infinity or NaN", p),
D3010EmptyPattern(ref p) =>
write!(f, "{}: Second argument of replace function cannot be an empty string", p),
D3011NegativeLimit(ref p) =>
write!(f, "{}: Fourth argument of replace function must evaluate to a positive number", p),
D3020NegativeLimit(ref p) =>
write!(f, "{}: Third argument of split function must evaluate to a positive number", p),
D3030NonNumericCast(ref p, ref n) =>
write!(f, "{}: Unable to cast value to a number: {}", p, n),
D3060SqrtNegative(ref p, ref n) =>
Expand Down
95 changes: 95 additions & 0 deletions src/evaluator/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,101 @@ pub fn fn_contains<'a>(
))
}

pub fn fn_replace<'a>(
context: FunctionContext<'a, '_>,
args: &'a Value<'a>,
) -> Result<&'a Value<'a>> {
let str_value = &args[0];
let pattern_value = &args[1];
let replacement_value = &args[2];
let limit_value = &args[3];

if str_value.is_undefined() {
return Ok(Value::undefined());
}

if pattern_value.is_string() && pattern_value.as_str().is_empty() {
return Err(Error::D3010EmptyPattern(context.char_index));
}

assert_arg!(str_value.is_string(), context, 1);
assert_arg!(pattern_value.is_string(), context, 2);
assert_arg!(replacement_value.is_string(), context, 3);

let str_value = str_value.as_str();
let pattern_value = pattern_value.as_str();
let replacement_value = replacement_value.as_str();
let limit_value = if limit_value.is_undefined() {
None
} else {
assert_arg!(limit_value.is_number(), context, 4);
if limit_value.as_isize().is_negative() {
return Err(Error::D3011NegativeLimit(context.char_index));
}
Some(limit_value.as_isize())
};

let replaced_string = if let Some(limit) = limit_value {
str_value.replacen(
&pattern_value.to_string(),
&replacement_value,
limit as usize,
)
} else {
str_value.replace(&pattern_value.to_string(), &replacement_value)
};

Ok(Value::string(context.arena, replaced_string))
}

pub fn fn_split<'a>(
context: FunctionContext<'a, '_>,
args: &'a Value<'a>,
) -> Result<&'a Value<'a>> {
let str_value = &args[0];
let separator_value = &args[1];
let limit_value = &args[2];

if str_value.is_undefined() {
return Ok(Value::undefined());
}

assert_arg!(str_value.is_string(), context, 1);
assert_arg!(separator_value.is_string(), context, 2);

let str_value = str_value.as_str();
let separator_value = separator_value.as_str();
let limit_value = if limit_value.is_undefined() {
None
} else {
assert_arg!(limit_value.is_number(), context, 4);
if limit_value.as_isize().is_negative() {
return Err(Error::D3020NegativeLimit(context.char_index));
}
Some(limit_value.as_isize())
};

let substrings: Vec<&str> = if let Some(limit) = limit_value {
str_value
.split(&separator_value.to_string())
.take(limit as usize)
.collect()
} else {
str_value.split(&separator_value.to_string()).collect()
};

let substrings_count = substrings.len();

let result = Value::array_with_capacity(context.arena, substrings_count, ArrayFlags::empty());
for (index, substring) in substrings.into_iter().enumerate() {
if substring.is_empty() && (index == 0 || index == substrings_count - 1) {
continue;
}
result.push(Value::string(context.arena, substring));
}
Ok(result)
}

pub fn fn_abs<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> {
let arg = &args[0];

Expand Down
8 changes: 5 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ impl<'a> JsonAta<'a> {
bind_native!("abs", 1, fn_abs);
bind_native!("append", 2, fn_append);
bind_native!("assert", 2, fn_assert);
bind_native!("base64decode", 1, fn_base64_decode);
bind_native!("base64encode", 1, fn_base64_encode);
bind_native!("boolean", 1, fn_boolean);
bind_native!("ceil", 1, fn_ceil);
bind_native!("contains", 2, fn_contains);
Expand All @@ -145,16 +147,16 @@ impl<'a> JsonAta<'a> {
bind_native!("not", 1, fn_not);
bind_native!("number", 1, fn_number);
bind_native!("power", 2, fn_power);
bind_native!("replace", 4, fn_replace);
bind_native!("reverse", 1, fn_reverse);
bind_native!("sort", 2, fn_sort);
bind_native!("string", 1, fn_string);
bind_native!("split", 3, fn_split);
bind_native!("sqrt", 1, fn_sqrt);
bind_native!("string", 1, fn_string);
bind_native!("substring", 3, fn_substring);
bind_native!("sum", 1, fn_sum);
bind_native!("trim", 1, fn_trim);
bind_native!("uppercase", 1, fn_uppercase);
bind_native!("base64encode", 1, fn_base64_encode);
bind_native!("base64decode", 1, fn_base64_decode);

let chain_ast = Some(parser::parse(
"function($f, $g) { function($x){ $g($f($x)) } }",
Expand Down

0 comments on commit a2d1d4a

Please sign in to comment.