From 751bf7a30ddc76547bc58b29ef09fc549b92db24 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Thu, 16 Mar 2023 23:41:45 +1100 Subject: [PATCH] substring, toLowerCase, toString, toUpperCase, trim, trimEnd, trimStart, valueOf --- inputs/passing/stringMethods/startsWith.ts | 14 ++ inputs/passing/stringMethods/substring.ts | 25 ++++ inputs/passing/stringMethods/toLowerCase.ts | 13 ++ inputs/passing/stringMethods/toUpperCase.ts | 13 ++ inputs/passing/stringMethods/trim.ts | 13 ++ inputs/passing/stringMethods/trimEnd.ts | 13 ++ inputs/passing/stringMethods/trimStart.ts | 13 ++ valuescript_vm/src/string_methods.rs | 143 +++++++++++++++++++- 8 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 inputs/passing/stringMethods/startsWith.ts create mode 100644 inputs/passing/stringMethods/substring.ts create mode 100644 inputs/passing/stringMethods/toLowerCase.ts create mode 100644 inputs/passing/stringMethods/toUpperCase.ts create mode 100644 inputs/passing/stringMethods/trim.ts create mode 100644 inputs/passing/stringMethods/trimEnd.ts create mode 100644 inputs/passing/stringMethods/trimStart.ts diff --git a/inputs/passing/stringMethods/startsWith.ts b/inputs/passing/stringMethods/startsWith.ts new file mode 100644 index 0000000..77c2f98 --- /dev/null +++ b/inputs/passing/stringMethods/startsWith.ts @@ -0,0 +1,14 @@ +// test_output! [[true,false,true,false,false,true]] + +export default function () { + const startsWithTests = [ + "abcde".startsWith("abc"), + "abcde".startsWith("bcd"), + "abcde".startsWith(""), + "abcde".startsWith("abcdeabcde"), + "abcde".startsWith("fgh"), + "abcde".startsWith("abc", 0), + ]; + + return [startsWithTests]; +} diff --git a/inputs/passing/stringMethods/substring.ts b/inputs/passing/stringMethods/substring.ts new file mode 100644 index 0000000..e122915 --- /dev/null +++ b/inputs/passing/stringMethods/substring.ts @@ -0,0 +1,25 @@ +// test_output! [["abc","de","abcde","cde","bcd"],["","abc",""],["abc","cde","bcd"]] + +export default function () { + const positive = [ + "abcde".substring(0, 3), + "abcde".substring(3, 5), + "abcde".substring(0), + "abcde".substring(2, 5), + "abcde".substring(1, 4), + ]; + + const edgeCases = [ + "abcde".substring(3, 3), + "abcde".substring(-1, 3), + "abcde".substring(6, 8), + ]; + + const parameterSwapping = [ + "abcde".substring(3, 0), + "abcde".substring(5, 2), + "abcde".substring(4, 1), + ]; + + return [positive, edgeCases, parameterSwapping]; +} diff --git a/inputs/passing/stringMethods/toLowerCase.ts b/inputs/passing/stringMethods/toLowerCase.ts new file mode 100644 index 0000000..2133d42 --- /dev/null +++ b/inputs/passing/stringMethods/toLowerCase.ts @@ -0,0 +1,13 @@ +// test_output! [["abc","xyz","a1b2c3","hello world","áéíóú"]] + +export default function () { + const toLowerCaseTests = [ + "ABC".toLowerCase(), + "XYZ".toLowerCase(), + "A1B2C3".toLowerCase(), + "Hello World".toLowerCase(), + "ÁÉÍÓÚ".toLowerCase(), + ]; + + return [toLowerCaseTests]; +} diff --git a/inputs/passing/stringMethods/toUpperCase.ts b/inputs/passing/stringMethods/toUpperCase.ts new file mode 100644 index 0000000..5810ef8 --- /dev/null +++ b/inputs/passing/stringMethods/toUpperCase.ts @@ -0,0 +1,13 @@ +// test_output! [["ABC","XYZ","A1B2C3","HELLO WORLD","ÁÉÍÓÚ"]] + +export default function () { + const toUpperCaseTests = [ + "abc".toUpperCase(), + "xyz".toUpperCase(), + "a1b2c3".toUpperCase(), + "Hello World".toUpperCase(), + "áéíóú".toUpperCase(), + ]; + + return [toUpperCaseTests]; +} diff --git a/inputs/passing/stringMethods/trim.ts b/inputs/passing/stringMethods/trim.ts new file mode 100644 index 0000000..e9c9720 --- /dev/null +++ b/inputs/passing/stringMethods/trim.ts @@ -0,0 +1,13 @@ +// test_output! [["abc","abc","abc","a b c",""]] + +export default function () { + const trimTests = [ + " abc".trim(), + "abc ".trim(), + " abc ".trim(), + " a b c ".trim(), + " ".trim(), + ]; + + return [trimTests]; +} diff --git a/inputs/passing/stringMethods/trimEnd.ts b/inputs/passing/stringMethods/trimEnd.ts new file mode 100644 index 0000000..a35fa42 --- /dev/null +++ b/inputs/passing/stringMethods/trimEnd.ts @@ -0,0 +1,13 @@ +// test_output! [["abc"," abc"," abc"," a b c",""]] + +export default function () { + const trimEndTests = [ + "abc ".trimEnd(), + " abc".trimEnd(), + " abc ".trimEnd(), + " a b c ".trimEnd(), + " ".trimEnd(), + ]; + + return [trimEndTests]; +} diff --git a/inputs/passing/stringMethods/trimStart.ts b/inputs/passing/stringMethods/trimStart.ts new file mode 100644 index 0000000..14ed558 --- /dev/null +++ b/inputs/passing/stringMethods/trimStart.ts @@ -0,0 +1,13 @@ +// test_output! [["abc","abc ","abc ","a b c ",""]] + +export default function () { + const trimStartTests = [ + " abc".trimStart(), + "abc ".trimStart(), + " abc ".trimStart(), + " a b c ".trimStart(), + " ".trimStart(), + ]; + + return [trimStartTests]; +} diff --git a/valuescript_vm/src/string_methods.rs b/valuescript_vm/src/string_methods.rs index 62b8cad..05cbfac 100644 --- a/valuescript_vm/src/string_methods.rs +++ b/valuescript_vm/src/string_methods.rs @@ -56,10 +56,10 @@ pub fn get_string_method(method: &str) -> Val { "includes" => Val::Static(&INCLUDES), "indexOf" => Val::Static(&INDEX_OF), "lastIndexOf" => Val::Static(&LAST_INDEX_OF), - "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) + "localeCompare" => Val::Static(&TODO_LOCALE), // (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), "repeat" => Val::Static(&REPEAT), @@ -69,6 +69,16 @@ pub fn get_string_method(method: &str) -> Val { "slice" => Val::Static(&SLICE), "split" => Val::Static(&SPLIT), "startsWith" => Val::Static(&STARTS_WITH), + "substring" => Val::Static(&SUBSTRING), + "toLocaleLowerCase" => Val::Static(&TODO_LOCALE), + "toLocaleUpperCase" => Val::Static(&TODO_LOCALE), + "toLowerCase" => Val::Static(&TO_LOWER_CASE), + "toString" => Val::Static(&TO_STRING), + "toUpperCase" => Val::Static(&TO_UPPER_CASE), + "trim" => Val::Static(&TRIM), + "trimEnd" => Val::Static(&TRIM_END), + "trimStart" => Val::Static(&TRIM_START), + "valueOf" => Val::Static(&VALUE_OF), _ => Val::Undefined, } } @@ -274,11 +284,11 @@ static LAST_INDEX_OF: NativeFunction = NativeFunction { }, }; -static LOCALE_COMPARE: NativeFunction = NativeFunction { +static TODO_LOCALE: NativeFunction = NativeFunction { fn_: |this: &mut Val, _params: Vec| -> Val { match this { Val::String(_string_data) => { - panic!("TODO: localeCompare"); + panic!("TODO: locale"); } _ => panic!("TODO: exceptions/string indirection"), } @@ -584,6 +594,127 @@ static STARTS_WITH: NativeFunction = NativeFunction { }, }; +static SUBSTRING: NativeFunction = NativeFunction { + fn_: |this: &mut Val, params: Vec| -> Val { + match this { + Val::String(string_data) => { + let string_bytes = string_data.as_bytes(); + + let start = match params.get(0) { + Some(v) => match v.to_index() { + Some(i) => std::cmp::min(i, string_bytes.len()), + None => 0, + }, + None => 0, + }; + + let end = match params.get(1) { + Some(v) => match v.to_index() { + Some(i) => std::cmp::min(i, string_bytes.len()), + None => string_bytes.len(), + }, + None => string_bytes.len(), + }; + + let substring_start = std::cmp::min(start, end); + let substring_end = std::cmp::max(start, end); + + let mut new_string = String::new(); + + // FIXME: This is a slow way of doing it. Part of the reason is that we're using rust's + // string type, so we can't just find the relevant byte range and copy it in one go. + for i in substring_start..substring_end { + match unicode_at(string_bytes, substring_end, i) { + Some(c) => new_string.push(c), + None => {} + } + } + + Val::String(Rc::new(new_string)) + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + +static TO_LOWER_CASE: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Val { + match this { + Val::String(string_data) => { + let lowercased_string = string_data.to_lowercase(); + Val::String(Rc::new(lowercased_string)) + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + +static TO_STRING: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Val { + match this { + Val::String(string_data) => Val::String(string_data.clone()), + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + +static TO_UPPER_CASE: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Val { + match this { + Val::String(string_data) => { + let uppercased_string = string_data.to_uppercase(); + Val::String(Rc::new(uppercased_string)) + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + +static TRIM: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Val { + match this { + Val::String(string_data) => { + let trimmed_string = string_data.trim(); + Val::String(Rc::new(trimmed_string.to_owned())) + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + +static TRIM_END: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Val { + match this { + Val::String(string_data) => { + let trimmed_string = string_data.trim_end(); + Val::String(Rc::new(trimmed_string.to_owned())) + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + +static TRIM_START: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Val { + match this { + Val::String(string_data) => { + let trimmed_string = string_data.trim_start(); + Val::String(Rc::new(trimmed_string.to_owned())) + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + +static VALUE_OF: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Val { + match this { + Val::String(string_data) => Val::String(string_data.clone()), + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + /** * Tries to match str_chars_param against matcher. * - Successful match: Advances str_chars_param and returns true.