diff --git a/inputs/passing/stringMethods/padEnd.ts b/inputs/passing/stringMethods/padEnd.ts new file mode 100644 index 0000000..7b23359 --- /dev/null +++ b/inputs/passing/stringMethods/padEnd.ts @@ -0,0 +1,18 @@ +// test_output! ["foo ","🚀","🚀","🚀","🚀","🚀","🚀🚀","🚀🚀","abc12","abc123","abc1231","abc12312"] + +export default function () { + return [ + "foo".padEnd(5), + "🚀".padEnd(3, "🚀"), + "🚀".padEnd(4, "🚀"), + "🚀".padEnd(5, "🚀"), + "🚀".padEnd(6, "🚀"), + "🚀".padEnd(7, "🚀"), + "🚀".padEnd(8, "🚀"), + "🚀".padEnd(9, "🚀"), + "abc".padEnd(5, "123"), + "abc".padEnd(6, "123"), + "abc".padEnd(7, "123"), + "abc".padEnd(8, "123"), + ]; +} diff --git a/inputs/passing/stringMethods/padStart.ts b/inputs/passing/stringMethods/padStart.ts new file mode 100644 index 0000000..1fc0986 --- /dev/null +++ b/inputs/passing/stringMethods/padStart.ts @@ -0,0 +1,18 @@ +// test_output! [" foo","🚀","🚀","🚀","🚀","🚀","🚀🚀","🚀🚀","12abc","123abc","1231abc","12312abc"] + +export default function () { + return [ + "foo".padStart(5), + "🚀".padStart(3, "🚀"), + "🚀".padStart(4, "🚀"), + "🚀".padStart(5, "🚀"), + "🚀".padStart(6, "🚀"), + "🚀".padStart(7, "🚀"), + "🚀".padStart(8, "🚀"), + "🚀".padStart(9, "🚀"), + "abc".padStart(5, "123"), + "abc".padStart(6, "123"), + "abc".padStart(7, "123"), + "abc".padStart(8, "123"), + ]; +} diff --git a/valuescript_vm/src/string_methods.rs b/valuescript_vm/src/string_methods.rs index 0462b0d..9fb0523 100644 --- a/valuescript_vm/src/string_methods.rs +++ b/valuescript_vm/src/string_methods.rs @@ -53,6 +53,9 @@ pub fn get_string_method(method: &str) -> Val { "localeCompare" => Val::Static(&LOCALE_COMPARE), // (TODO) "match" => Val::Static(&TODO_REGEXES), // (TODO: regex) "matchAll" => Val::Static(&TODO_REGEXES), // (TODO: regex) + "normalize" => Val::Static(&NORMALIZE), // (TODO) + "padEnd" => Val::Static(&PAD_END), + "padStart" => Val::Static(&PAD_START), _ => Val::Undefined, } } @@ -280,6 +283,128 @@ static TODO_REGEXES: NativeFunction = NativeFunction { }, }; +static NORMALIZE: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Val { + match this { + Val::String(_string_data) => { + // Consider https://docs.rs/unicode-normalization/latest/unicode_normalization/ + panic!("TODO: normalize"); + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + +// TODO: JS has some locale-specific behavior, not sure yet how we should deal with that +static PAD_END: NativeFunction = NativeFunction { + fn_: |this: &mut Val, params: Vec| -> Val { + match this { + Val::String(string_data) => { + let target_length = match params.get(0) { + Some(p) => match p.to_index() { + Some(i) => i, + None => return Val::String(string_data.clone()), + }, + _ => return Val::String(string_data.clone()), + }; + + if target_length <= string_data.as_bytes().len() { + return Val::String(string_data.clone()); + } + + let mut string = string_data.to_string(); + + let pad_string = match params.get(1) { + Some(s) => s.val_to_string(), + _ => " ".to_string(), + }; + + let mut length_deficit = target_length - string.as_bytes().len(); + + let whole_copies = length_deficit / pad_string.as_bytes().len(); + + for _ in 0..whole_copies { + string.push_str(&pad_string); + } + + length_deficit -= whole_copies * pad_string.as_bytes().len(); + + if length_deficit > 0 { + for c in pad_string.chars() { + let c_len = c.len_utf8(); + + if c_len > length_deficit { + break; + } + + string.push(c); + length_deficit -= c_len; + } + } + + Val::String(Rc::new(string)) + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + +// TODO: JS has some locale-specific behavior, not sure yet how we should deal with that +static PAD_START: NativeFunction = NativeFunction { + fn_: |this: &mut Val, params: Vec| -> Val { + match this { + Val::String(string_data) => { + let target_length = match params.get(0) { + Some(p) => match p.to_index() { + Some(i) => i, + None => return Val::String(string_data.clone()), + }, + _ => return Val::String(string_data.clone()), + }; + + if target_length <= string_data.as_bytes().len() { + return Val::String(string_data.clone()); + } + + let pad_string = match params.get(1) { + Some(s) => s.val_to_string(), + _ => " ".to_string(), + }; + + let mut length_deficit = target_length - string_data.as_bytes().len(); + + let whole_copies = length_deficit / pad_string.as_bytes().len(); + + let mut prefix = String::new(); + + for _ in 0..whole_copies { + prefix.push_str(&pad_string); + } + + length_deficit -= whole_copies * pad_string.as_bytes().len(); + + if length_deficit > 0 { + for c in pad_string.chars() { + let c_len = c.len_utf8(); + + if c_len > length_deficit { + break; + } + + prefix.push(c); + length_deficit -= c_len; + } + } + + prefix.push_str(string_data); + + Val::String(Rc::new(prefix)) + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + fn index_of(string_bytes: &[u8], search_bytes: &[u8], start_pos: usize) -> Option { let search_length = search_bytes.len();