From 48314c692da9fb84a1d50232683182ae0e526761 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Wed, 15 Mar 2023 09:21:58 +1100 Subject: [PATCH] slice (string) --- inputs/passing/stringMethods/slice.ts | 21 ++++++++ valuescript_vm/src/helpers.rs | 16 +++++++ valuescript_vm/src/string_methods.rs | 69 +++++++++++++++++++++------ valuescript_vm/src/vs_array.rs | 18 +------ 4 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 inputs/passing/stringMethods/slice.ts diff --git a/inputs/passing/stringMethods/slice.ts b/inputs/passing/stringMethods/slice.ts new file mode 100644 index 0000000..001bff1 --- /dev/null +++ b/inputs/passing/stringMethods/slice.ts @@ -0,0 +1,21 @@ +// test_output! ["","","","🎨","🎨","🎨","한🎨","한🎨","🍹abc£한","🚀🍹abc£한🎨","🚀🍹abc£한🎨","🚀🍹abc£한🎨",""] + +export default function () { + const str = "🚀🍹abc£한🎨"; + + return [ + str.slice(-1), + str.slice(-2), + str.slice(-3), + str.slice(-4), + str.slice(-5), + str.slice(-6), + str.slice(-7), + str.slice(-8), + str.slice(1, -1), + str.slice(), + str.slice(0, 100), + str.slice(0, 20), + str.slice(10, -11), + ]; +} diff --git a/valuescript_vm/src/helpers.rs b/valuescript_vm/src/helpers.rs index 5ee971b..8b6fa09 100644 --- a/valuescript_vm/src/helpers.rs +++ b/valuescript_vm/src/helpers.rs @@ -15,6 +15,22 @@ pub fn to_wrapping_index(index: Option<&Val>, len: usize) -> Option { return Some(unchecked as usize); } +pub fn to_wrapping_index_clamped(index: &Val, len: usize) -> isize { + let wrapping_index = to_unchecked_wrapping_index(index, len); + + if wrapping_index < 0 { + return 0; + } + + if wrapping_index > len as isize { + // len-1 would be a mistake. The end of the array is a meaningful index even + // though there is no data there. + return len as isize; + } + + return wrapping_index; +} + pub fn to_unchecked_wrapping_index(index: &Val, len: usize) -> isize { let index_num = index.to_number(); diff --git a/valuescript_vm/src/string_methods.rs b/valuescript_vm/src/string_methods.rs index 8cd7855..74549b3 100644 --- a/valuescript_vm/src/string_methods.rs +++ b/valuescript_vm/src/string_methods.rs @@ -1,6 +1,11 @@ use std::rc::Rc; -use crate::{helpers::to_wrapping_index, native_function::NativeFunction, vs_value::Val, ValTrait}; +use crate::{ + helpers::{to_wrapping_index, to_wrapping_index_clamped}, + native_function::NativeFunction, + vs_value::Val, + ValTrait, +}; pub fn op_sub_string(string_data: &Rc, subscript: &Val) -> Val { let right_index = match subscript.to_index() { @@ -22,8 +27,8 @@ pub fn op_sub_string(string_data: &Rc, subscript: &Val) -> Val { return Val::Undefined; } - match unicode_at(string_bytes, right_index) { - Some(string) => Val::String(Rc::new(string)), + match unicode_at(string_bytes, string_bytes.len(), right_index) { + Some(char) => Val::String(Rc::new(char.to_string())), None => Val::String(Rc::new(String::from(""))), } } @@ -57,6 +62,10 @@ pub fn get_string_method(method: &str) -> Val { "padEnd" => Val::Static(&PAD_END), "padStart" => Val::Static(&PAD_START), "repeat" => Val::Static(&REPEAT), + "replace" => Val::Static(&TODO_REGEXES), // (TODO: regex) + "replaceAll" => Val::Static(&TODO_REGEXES), // (TODO: regex) + "search" => Val::Static(&TODO_REGEXES), // (TODO: regex) + "slice" => Val::Static(&SLICE), _ => Val::Undefined, } } @@ -72,8 +81,8 @@ static AT: NativeFunction = NativeFunction { Some(i) => i, }; - match unicode_at(string_bytes, index) { - Some(string) => Val::String(Rc::new(string)), + match unicode_at(string_bytes, string_bytes.len(), index) { + Some(char) => Val::String(Rc::new(char.to_string())), None => Val::String(Rc::new(String::from(""))), } } @@ -96,7 +105,7 @@ static CODE_POINT_AT: NativeFunction = NativeFunction { _ => return Val::Undefined, }; - match code_point_at(string_bytes, index) { + match code_point_at(string_bytes, string_bytes.len(), index) { Some(code_point) => Val::Number(code_point as f64), None => Val::Undefined, } @@ -431,6 +440,40 @@ static REPEAT: NativeFunction = NativeFunction { }, }; +static SLICE: 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) { + None => 0, + Some(v) => to_wrapping_index_clamped(v, string_bytes.len()), + }; + + let end = match params.get(1) { + None => string_bytes.len() as isize, + Some(v) => to_wrapping_index_clamped(v, string_bytes.len()), + }; + + 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 start..end { + match unicode_at(string_bytes, end as usize, i as usize) { + Some(c) => new_string.push(c), + None => {} + } + } + + Val::String(Rc::new(new_string)) + } + _ => panic!("TODO: exceptions/string indirection"), + } + }, +}; + fn index_of(string_bytes: &[u8], search_bytes: &[u8], start_pos: usize) -> Option { let search_length = search_bytes.len(); @@ -481,19 +524,17 @@ fn last_index_of(string_bytes: &[u8], search_bytes: &[u8], at_least_pos: usize) None } -fn unicode_at(bytes: &[u8], index: usize) -> Option { - match code_point_at(bytes, index) { +fn unicode_at(bytes: &[u8], len: usize, index: usize) -> Option { + match code_point_at(bytes, len, index) { Some(code_point) => Some( - std::char::from_u32(code_point) - .expect("Invalid code point") // TODO: Find out if this is reachable and what to do about it - .to_string(), + std::char::from_u32(code_point).expect("Invalid code point"), // TODO: Find out if this is reachable and what to do about it ), None => None, } } -fn code_point_at(bytes: &[u8], index: usize) -> Option { - if index >= bytes.len() { +fn code_point_at(bytes: &[u8], len: usize, index: usize) -> Option { + if index >= len { return None; } @@ -505,7 +546,7 @@ fn code_point_at(bytes: &[u8], index: usize) -> Option { return Some(byte as u32); } - if leading_ones == 1 || leading_ones > 4 || index + leading_ones > bytes.len() { + if leading_ones == 1 || leading_ones > 4 || index + leading_ones > len { return None; } diff --git a/valuescript_vm/src/vs_array.rs b/valuescript_vm/src/vs_array.rs index 585ee80..9e73454 100644 --- a/valuescript_vm/src/vs_array.rs +++ b/valuescript_vm/src/vs_array.rs @@ -1,7 +1,7 @@ use std::cmp::{max, min}; use std::rc::Rc; -use crate::helpers::{to_unchecked_wrapping_index, to_wrapping_index}; +use crate::helpers::{to_wrapping_index, to_wrapping_index_clamped}; use super::array_higher_functions::array_every::EVERY; use super::array_higher_functions::array_filter::FILTER; @@ -137,22 +137,6 @@ impl ValTrait for ArrayPrototype { } } -fn to_wrapping_index_clamped(index: &Val, len: usize) -> isize { - let wrapping_index = to_unchecked_wrapping_index(index, len); - - if wrapping_index < 0 { - return 0; - } - - if wrapping_index > len as isize { - // len-1 would be a mistake. The end of the array is a meaningful index even - // though there is no data there. - return len as isize; - } - - return wrapping_index; -} - static AT: NativeFunction = NativeFunction { fn_: |this: &mut Val, params: Vec| -> Val { match this {