diff --git a/inputs/passing/stringMethods/lastIndexOf.ts b/inputs/passing/stringMethods/lastIndexOf.ts new file mode 100644 index 0000000..2f4088c --- /dev/null +++ b/inputs/passing/stringMethods/lastIndexOf.ts @@ -0,0 +1,17 @@ +// test_output! [[4,2,4,6],[-1,-1]] + +export default function () { + const positive = [ + " xxx".lastIndexOf("x"), + "xxx ".lastIndexOf("x"), + " xxx ".lastIndexOf("x"), + " xyxyxy ".lastIndexOf("xy"), + ]; + + const negative = [ + " xxx ".lastIndexOf("x", 5), + "abc".lastIndexOf("abcd"), + ]; + + return [positive, negative]; +} diff --git a/valuescript_vm/src/string_methods.rs b/valuescript_vm/src/string_methods.rs index 3f2f645..f6a2b3d 100644 --- a/valuescript_vm/src/string_methods.rs +++ b/valuescript_vm/src/string_methods.rs @@ -49,6 +49,7 @@ pub fn get_string_method(method: &str) -> Val { "endsWith" => Val::Static(&ENDS_WITH), "includes" => Val::Static(&INCLUDES), "indexOf" => Val::Static(&INDEX_OF), + "lastIndexOf" => Val::Static(&LAST_INDEX_OF), _ => Val::Undefined, } } @@ -222,6 +223,38 @@ static INDEX_OF: NativeFunction = NativeFunction { }, }; +static LAST_INDEX_OF: NativeFunction = NativeFunction { + fn_: |this: &mut Val, params: Vec| -> Val { + match this { + Val::String(string_data) => { + let string_bytes = string_data.as_bytes(); + + let search_string = match params.get(0) { + Some(s) => s.val_to_string(), + _ => return Val::Number(-1.0), + }; + + let search_bytes = search_string.as_bytes(); + + let at_least_pos = match params.get(1) { + Some(p) => match p.to_index() { + // FIXME: to_index isn't quite right here + Some(i) => i, + None => return Val::Number(-1.0), + }, + _ => 0, + }; + + match last_index_of(string_bytes, search_bytes, at_least_pos) { + Some(i) => Val::Number(i as f64), + None => Val::Number(-1.0), + } + } + _ => std::panic!("Not implemented: exceptions/string indirection"), + } + }, +}; + fn index_of(string_bytes: &[u8], search_bytes: &[u8], start_pos: usize) -> Option { let search_length = search_bytes.len(); @@ -235,19 +268,38 @@ fn index_of(string_bytes: &[u8], search_bytes: &[u8], start_pos: usize) -> Optio return Some(0); } - for i in start_pos..(string_bytes.len() - search_length + 1) { - let mut found = true; - + 'outer: for i in start_pos..=(string_bytes.len() - search_length) { for j in 0..search_length { if string_bytes[i + j] != search_bytes[j] { - found = false; - break; + continue 'outer; } } - if found { - return Some(i); + return Some(i); + } + + None +} + +fn last_index_of(string_bytes: &[u8], search_bytes: &[u8], at_least_pos: usize) -> Option { + let search_length = search_bytes.len(); + + if search_length > string_bytes.len() { + return None; + } + + if search_length == 0 { + return Some(string_bytes.len()); + } + + 'outer: for i in (at_least_pos..=string_bytes.len() - search_length).rev() { + for j in 0..search_length { + if string_bytes[i + j] != search_bytes[j] { + continue 'outer; + } } + + return Some(i); } None