-
-
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
Conversation
Yes, there are quite a few tests in this project, each file (or module) has the tests embedded with the code. So for String, you will see the tests at the bottom of the file. Im sure there are enough tests here for you to familiar yourself with. |
Im not sure if using regex directly will work, as the rules may differ from rust to JS. Maybe you could use that? Tests could be added first to see which solution is more compatible Secondly incase you're unaware your rustfmt is failing: You can run |
Nice, @jasonwilliams! I'll add tests (sorry, hadn't located them just out of Rust-noobness) and rustfmt sometime today. About RegExp, I started with that, but Rust complained about it not being exported or something. But my final version will most likely use that. Thanks for the feedback! |
This is taking some shape now. I have included a |
src/lib/builtins/string.rs
Outdated
@@ -766,6 +798,7 @@ pub fn create_constructor(global: &Value) -> Value { | |||
proto.set_field_slice("substr", to_value(substr as NativeFunctionData)); | |||
proto.set_field_slice("valueOf", to_value(value_of as NativeFunctionData)); | |||
proto.set_field_slice("matchAll", to_value(match_all as NativeFunctionData)); | |||
proto.set_field_slice("replace", to_value(replace as NativeFunctionData)); |
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.
You need to set the length
property of the function as well.
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.
I don't know if I understood that. Do you mean str.replace.length
should return 2
? If so, would you say that str.charAt.length
should return 1
then?
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.
Yes, to both your questions. A function
's length represents the number of "formal" parameters, e.g. those with no default value.
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.
Do the other functions in String
already support that in boa or are you suggesting that they should? I'm locally getting str.charAt.length == undefined
, but I may have an outdated branch...
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.
Unfortunately, some functions in boa (not just in String
) don't have their length set because it is easily missable.
I have suggested an approach in #193 and would very much like we have something like that, so we don't miss these things.
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.
Nice. I'm working on other stuff here, but I'll make sure to check where that PR of yours goes.
a72479b
to
a540bd7
Compare
src/lib/builtins/regexp.rs
Outdated
/// Regex matcher. | ||
matcher: Regex, | ||
pub matcher: Regex, |
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.
Need some help here. I'm feeling a bit uneasy about making this public. What is the idiomatic way to access regex funcionality from outside the regexp module?
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.
Do you intend to modify the value in any way?
The most "popular" and obvious way would be to have an immutable getter (and a mutable one only if really needed).
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.
I don't intend to modify the value. I was considering getter vs. something like adding a replace_all
delegating method. The second option could be used to deal with differences between JS and Rust regex engines.
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.
What exactly is your idea for replace_all
?
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.
Something along the lines of fbf8817. But I'm not really sure this is the intended use for the lib structs.
3a674c7
to
8215057
Compare
src/lib/builtins/string.rs
Outdated
// We then do the same thing for the other args | ||
// TODO: what if there are missing arguments? | ||
let pattern = get_argument(args, 0); | ||
if undefined() == pattern { |
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.
If I am not mistaken, there was an is_undefined
method.
So you can try pattern.is_undefined()
instead.
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.
Good catch!
2e3b0a5
to
1899da7
Compare
I think this is ready for another round of review. My main concern here is about @IovoslavIovchev's suggestion on |
The problem with |
src/lib/builtins/string.rs
Outdated
// Make a Rust Regex from the first argument | ||
let pattern = get_argument(args, 0); | ||
if pattern.is_undefined() { | ||
return Ok(to_value(this_str)); |
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.
return Ok(to_value(this_str)); | |
return Ok(this.clone()); |
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.
This is just a small optimisation to avoid calling ctx.value_to_rust_string
if the regex is undefined
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); |
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.
src/lib/builtins/string.rs
Outdated
.call(&replacement, &undefined(), args) | ||
.unwrap_or_else(|_| undefined()); | ||
ctx.value_to_rust_string(&rs_value) | ||
}) as Box<dyn FnMut((&Captures)) -> (String)> |
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.
Remove the parentheses, so it becomes
}) as Box<dyn FnMut((&Captures)) -> (String)> | |
}) as Box<dyn FnMut(&Captures) -> String> |
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 comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
I know something similar exists in regexp.rs but nothing else.
"#; | ||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
assert_eq!(forward(&mut engine, "result1"), "The quick brown fox jumps over the lazy monkey. If the monkey reacted, was it really lazy?"); | |
assert_eq!(forward(&mut engine, "result1"), "The quick brown fox jumps over the lazy monkey. If the dog reacted, was it really lazy?"); |
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?"); | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
assert_eq!(forward(&mut engine, "regexResult"), "The quick brown fox jumps over the lazy monkey. If the monkey.reacted, was it really lazy?"); | |
assert_eq!(forward(&mut engine, "regexResult"), "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?"); | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
"The quick brown fox jumps over the lazy DOG. If the DOG reacted, was it really lazy?" | |
"The quick brown fox jumps over the lazy DOG. If the dog reacted, was it really lazy?" |
); | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
"The quick brown fox jumps over the lazy DOG. If the DOG reacted, was it really lazy?" | |
"The quick brown fox jumps over the lazy DOG. If the dog reacted, was it really lazy?" |
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 comment
The reason will be displayed to describe this comment to others. Learn more.
assert_eq!(forward(&mut engine, "exceptional2"), "The quick brown fox jumps over the lazy undefined. If the undefined reacted, was it really lazy?"); | |
assert_eq!(forward(&mut engine, "exceptional2"), "The quick brown fox jumps over the lazy undefined. If the dog reacted, was it really lazy?"); |
src/lib/builtins/string.rs
Outdated
"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?"); | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this line, it's a duplicate
The current implementation replaces all occurrences of the first argument when it's a |
1899da7
to
2212b8b
Compare
@thiagoarrais do you need any help with this? |
I think I've addressed most of your comments, @IovoslavIovchev . The one thing I couldn't tackle yet is the spec breakage regarding replacing only the first occurrence. I plan on getting back to this some time, but I'm a little short on time. If you could help on that, it would be greatly appreciated. Maybe you can give some general direction on a good approach? I'd also include a commit of yours here if you're willing to branch from this PR. |
@thiagoarrais no worries. I will take a look. |
This has now been added |
Work in progress pull request for fixing #116
The implementation isn't complete yet, but I intend to incorporate changes based on feedback. For starters, just let me know if this is the structure you expect. I'll try to evolve the
replace
method to be spec-compliant.Related question: does this project use tests? Where should I add them if so?
TODO List:
Include function length (related: Create a macro for properly setting builtin member functions #193)