-
-
Notifications
You must be signed in to change notification settings - Fork 407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add String.replace #190
Add String.replace #190
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -3,12 +3,13 @@ use crate::{ | |||||
function::NativeFunctionData, | ||||||
object::{Object, ObjectKind, PROTOTYPE}, | ||||||
property::Property, | ||||||
regexp::{make_regexp, match_all as regexp_match_all, r#match as regexp_match}, | ||||||
value::{from_value, to_value, ResultValue, Value, ValueData}, | ||||||
regexp::{make_regexp, match_all as regexp_match_all, r#match as regexp_match, RegExp}, | ||||||
value::{from_value, to_value, undefined, ResultValue, Value, ValueData}, | ||||||
}, | ||||||
exec::Interpreter, | ||||||
}; | ||||||
use gc::Gc; | ||||||
use regex::{Captures, Regex}; | ||||||
use std::{ | ||||||
cmp::{max, min}, | ||||||
f64::NAN, | ||||||
|
@@ -722,6 +723,61 @@ pub fn match_all(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV | |||||
regexp_match_all(&re, ctx.value_to_rust_string(this)) | ||||||
} | ||||||
|
||||||
/// Return a String where the matches of a pattern in the original string is replaced with a given | ||||||
/// replacement. | ||||||
/// <https://tc39.es/ecma262/#sec-string.prototype.replace> | ||||||
pub fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { | ||||||
// First we get it the actual string a private field stored on the object only the engine has access to. | ||||||
// Then we convert it into a Rust String | ||||||
let this_str: &str = &ctx.value_to_rust_string(this); | ||||||
|
||||||
// Simple guard for undefined pattern | ||||||
let pattern = get_argument(args, 0); | ||||||
if pattern.is_undefined() { | ||||||
return Ok(this.clone()); | ||||||
} | ||||||
|
||||||
// Make a Rust Regex from the first argument | ||||||
let pattern_re = if let Some(re_internal_state) = pattern.get_internal_state() { | ||||||
if let Some(pattern_jsre) = re_internal_state.downcast_ref::<RegExp>() { | ||||||
pattern_jsre.get_matcher().clone() | ||||||
} else { | ||||||
let pattern_str = &ctx.value_to_rust_string(&pattern); | ||||||
Regex::new(®ex::escape(pattern_str)).unwrap() | ||||||
} | ||||||
} else { | ||||||
let pattern_str = &ctx.value_to_rust_string(&pattern); | ||||||
Regex::new(®ex::escape(pattern_str)).unwrap() | ||||||
}; | ||||||
|
||||||
// Make a replacement closure from the second argument | ||||||
let replacement = get_argument(args, 1); | ||||||
|
||||||
let replacement_cls = if replacement.is_function() { | ||||||
Box::new(|caps: &Captures| { | ||||||
let m = caps.get(0).unwrap().as_str(); | ||||||
let args = vec![to_value(m)]; | ||||||
let rs_value = ctx | ||||||
.call(&replacement, &undefined(), args) | ||||||
.unwrap_or_else(|_| undefined()); | ||||||
ctx.value_to_rust_string(&rs_value) | ||||||
}) as Box<dyn FnMut(&Captures) -> String> | ||||||
} else { | ||||||
Box::new(|_: &Captures| ctx.value_to_rust_string(&replacement)) | ||||||
}; | ||||||
|
||||||
// Replace the pattern by the replacement closure | ||||||
let result_str = pattern_re.replace_all(this_str, replacement_cls); | ||||||
Ok(to_value(result_str.into_owned())) | ||||||
} | ||||||
|
||||||
fn get_argument(args: &[Value], idx: usize) -> Value { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit more general, so it would be better you move it somewhere, where it can be widely accessed. I am thinking exec.rs. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually wonder if this already exists somewhere. But I can't find it... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know something similar exists in regexp.rs but nothing else. |
||||||
match args.get(idx) { | ||||||
Some(arg) => arg.clone(), | ||||||
None => undefined(), | ||||||
} | ||||||
} | ||||||
|
||||||
/// Create a new `String` object | ||||||
pub fn create_constructor(global: &Value) -> Value { | ||||||
// Create constructor function object | ||||||
|
@@ -760,6 +816,7 @@ pub fn create_constructor(global: &Value) -> Value { | |||||
make_builtin_fn!(substr, named "substr", with length 2, of proto); | ||||||
make_builtin_fn!(value_of, named "valueOf", of proto); | ||||||
make_builtin_fn!(match_all, named "matchAll", with length 1, of proto); | ||||||
make_builtin_fn!(replace, named "replace", with length 2, of proto); | ||||||
|
||||||
let string = to_value(string_constructor); | ||||||
proto.set_field_slice("constructor", string.clone()); | ||||||
|
@@ -1036,4 +1093,40 @@ mod tests { | |||||
); | ||||||
assert_eq!(forward(&mut engine, "result4[0]"), "B"); | ||||||
} | ||||||
|
||||||
#[test] | ||||||
fn replace() { | ||||||
let realm = Realm::create(); | ||||||
let mut engine = Executor::new(realm); | ||||||
let init = r#" | ||||||
var str = 'The quick brown fox jumps over the lazy dog. If the dog reacted, was it really lazy?'; | ||||||
var result1 = str.replace('dog', 'monkey'); | ||||||
var result2 = str.replace('dog.', 'monkey.'); | ||||||
var regexResult = str.replace(/dog./, 'monkey.'); | ||||||
var funResult1 = str.replace(/dog/, function(word) { return word.toUpperCase(); }) | ||||||
var funResult2 = str.replace('dog', function(word) { return word.toUpperCase(); }) | ||||||
var intResult = 'abc4e'.replace(4, 'd') | ||||||
var exceptional1 = str.replace(); | ||||||
var exceptional2 = str.replace('dog'); | ||||||
"#; | ||||||
|
||||||
forward(&mut engine, init); | ||||||
assert_eq!(forward(&mut engine, "result1"), "The quick brown fox jumps over the lazy monkey. If the monkey reacted, was it really lazy?"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
assert_eq!(forward(&mut engine, "result2"), "The quick brown fox jumps over the lazy monkey. If the dog reacted, was it really lazy?"); | ||||||
assert_eq!(forward(&mut engine, "regexResult"), "The quick brown fox jumps over the lazy monkey. If the monkey.reacted, was it really lazy?"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
assert_eq!( | ||||||
forward(&mut engine, "funResult1"), | ||||||
"The quick brown fox jumps over the lazy DOG. If the DOG reacted, was it really lazy?" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
); | ||||||
assert_eq!( | ||||||
forward(&mut engine, "funResult2"), | ||||||
"The quick brown fox jumps over the lazy DOG. If the DOG reacted, was it really lazy?" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
); | ||||||
assert_eq!(forward(&mut engine, "intResult"), "abcde"); | ||||||
assert_eq!( | ||||||
forward(&mut engine, "exceptional1"), | ||||||
"The quick brown fox jumps over the lazy dog. If the dog reacted, was it really lazy?" | ||||||
); | ||||||
assert_eq!(forward(&mut engine, "exceptional2"), "The quick brown fox jumps over the lazy undefined. If the undefined reacted, was it really lazy?"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this after the
if
statement below.