mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
implemented Str.trimRight
This commit is contained in:
parent
f92a1f83e3
commit
8d7c252fce
12 changed files with 237 additions and 1 deletions
|
@ -127,6 +127,7 @@ comptime {
|
|||
exportStrFn(str.repeat, "repeat");
|
||||
exportStrFn(str.strTrim, "trim");
|
||||
exportStrFn(str.strTrimLeft, "trim_left");
|
||||
exportStrFn(str.strTrimRight, "trim_right");
|
||||
|
||||
inline for (INTEGERS) |T| {
|
||||
str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int.");
|
||||
|
|
|
@ -1584,6 +1584,42 @@ pub fn strTrimLeft(string: RocStr) callconv(.C) RocStr {
|
|||
return RocStr.empty();
|
||||
}
|
||||
|
||||
pub fn strTrimRight(string: RocStr) callconv(.C) RocStr {
|
||||
if (string.str_bytes) |bytes_ptr| {
|
||||
const leading_bytes = countLeadingWhitespaceBytes(string);
|
||||
const trailing_bytes = countTrailingWhitespaceBytes(string);
|
||||
const original_len = string.len();
|
||||
|
||||
if (original_len == trailing_bytes) {
|
||||
string.deinit();
|
||||
return RocStr.empty();
|
||||
}
|
||||
|
||||
const new_len = original_len - trailing_bytes;
|
||||
|
||||
const small_or_shared = new_len <= SMALL_STR_MAX_LENGTH or !string.isRefcountOne();
|
||||
if (small_or_shared) {
|
||||
return RocStr.init(string.asU8ptr(), new_len);
|
||||
}
|
||||
|
||||
// nonempty, large, and unique:
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < new_len) : (i += 1) {
|
||||
const dest = bytes_ptr + i;
|
||||
const source = dest;
|
||||
@memcpy(dest, source, 1);
|
||||
}
|
||||
|
||||
var new_string = string;
|
||||
new_string.str_len = new_len;
|
||||
|
||||
return new_string;
|
||||
}
|
||||
|
||||
return RocStr.empty();
|
||||
}
|
||||
|
||||
fn countLeadingWhitespaceBytes(string: RocStr) usize {
|
||||
var byte_count: usize = 0;
|
||||
|
||||
|
@ -1820,6 +1856,77 @@ test "strTrimLeft: small to small" {
|
|||
try expect(trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "strTrimRight: empty" {
|
||||
const trimmedEmpty = strTrimRight(RocStr.empty());
|
||||
try expect(trimmedEmpty.eq(RocStr.empty()));
|
||||
}
|
||||
|
||||
test "strTrimRight: blank" {
|
||||
const original_bytes = " ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.deinit();
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
|
||||
try expect(trimmed.eq(RocStr.empty()));
|
||||
}
|
||||
|
||||
test "strTrimRight: large to large" {
|
||||
const original_bytes = " hello giant world ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.deinit();
|
||||
|
||||
try expect(!original.isSmallStr());
|
||||
|
||||
const expected_bytes = " hello giant world";
|
||||
const expected = RocStr.init(expected_bytes, expected_bytes.len);
|
||||
defer expected.deinit();
|
||||
|
||||
try expect(!expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
}
|
||||
|
||||
test "strTrimRight: large to small" {
|
||||
const original_bytes = " hello world ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.deinit();
|
||||
|
||||
try expect(!original.isSmallStr());
|
||||
|
||||
const expected_bytes = " hello world";
|
||||
const expected = RocStr.init(expected_bytes, expected_bytes.len);
|
||||
defer expected.deinit();
|
||||
|
||||
try expect(expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
try expect(trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "strTrimRight: small to small" {
|
||||
const original_bytes = " hello world ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.deinit();
|
||||
|
||||
try expect(original.isSmallStr());
|
||||
|
||||
const expected_bytes = " hello world";
|
||||
const expected = RocStr.init(expected_bytes, expected_bytes.len);
|
||||
defer expected.deinit();
|
||||
|
||||
try expect(expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
try expect(trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "ReverseUtf8View: hello world" {
|
||||
const original_bytes = "hello world";
|
||||
const expected_bytes = "dlrow olleh";
|
||||
|
|
|
@ -149,6 +149,7 @@ pub const STR_FROM_UTF8_RANGE: &str = "roc_builtins.str.from_utf8_range";
|
|||
pub const STR_REPEAT: &str = "roc_builtins.str.repeat";
|
||||
pub const STR_TRIM: &str = "roc_builtins.str.trim";
|
||||
pub const STR_TRIM_LEFT: &str = "roc_builtins.str.trim_left";
|
||||
pub const STR_TRIM_RIGHT: &str = "roc_builtins.str.trim_right";
|
||||
|
||||
pub const DICT_HASH: &str = "roc_builtins.dict.hash";
|
||||
pub const DICT_HASH_STR: &str = "roc_builtins.dict.hash_str";
|
||||
|
|
|
@ -639,6 +639,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
Box::new(str_type())
|
||||
);
|
||||
|
||||
// trimRight : Str -> Str
|
||||
add_top_level_function_type!(
|
||||
Symbol::STR_TRIM_RIGHT,
|
||||
vec![str_type()],
|
||||
Box::new(str_type())
|
||||
);
|
||||
|
||||
// trim : Str -> Str
|
||||
add_top_level_function_type!(Symbol::STR_TRIM, vec![str_type()], Box::new(str_type()));
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
|||
STR_REPEAT => str_repeat,
|
||||
STR_TRIM => str_trim,
|
||||
STR_TRIM_LEFT => str_trim_left,
|
||||
STR_TRIM_RIGHT => str_trim_right,
|
||||
LIST_LEN => list_len,
|
||||
LIST_GET => list_get,
|
||||
LIST_SET => list_set,
|
||||
|
@ -1295,6 +1296,11 @@ fn str_trim_left(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
lowlevel_1(symbol, LowLevel::StrTrimLeft, var_store)
|
||||
}
|
||||
|
||||
/// Str.trimRight : Str -> Str
|
||||
fn str_trim_right(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_1(symbol, LowLevel::StrTrimRight, var_store)
|
||||
}
|
||||
|
||||
/// Str.repeat : Str, Nat -> Str
|
||||
fn str_repeat(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let str_var = var_store.fresh();
|
||||
|
|
|
@ -18,6 +18,7 @@ use crate::llvm::build_str::{
|
|||
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
||||
str_from_utf8, str_from_utf8_range, str_join_with, str_number_of_bytes, str_repeat, str_split,
|
||||
str_starts_with, str_starts_with_code_point, str_to_utf8, str_trim, str_trim_left,
|
||||
str_trim_right,
|
||||
};
|
||||
use crate::llvm::compare::{generic_eq, generic_neq};
|
||||
use crate::llvm::convert::{
|
||||
|
@ -5070,6 +5071,12 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
str_trim_left(env, scope, args[0])
|
||||
}
|
||||
StrTrimRight => {
|
||||
// Str.trim : Str -> Str
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
str_trim_right(env, scope, args[0])
|
||||
}
|
||||
ListLen => {
|
||||
// List.len : List * -> Int
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
|
|
@ -269,6 +269,16 @@ pub fn str_trim_left<'a, 'ctx, 'env>(
|
|||
call_bitcode_fn(env, &[str_i128.into()], bitcode::STR_TRIM_LEFT)
|
||||
}
|
||||
|
||||
/// Str.trimRight : Str -> Str
|
||||
pub fn str_trim_right<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
str_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
||||
call_bitcode_fn(env, &[str_i128.into()], bitcode::STR_TRIM_RIGHT)
|
||||
}
|
||||
|
||||
/// Str.fromInt : Int -> Str
|
||||
pub fn str_from_int<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
|
|
@ -28,7 +28,7 @@ pub fn build_call_low_level<'a>(
|
|||
|
||||
match lowlevel {
|
||||
StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt
|
||||
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft
|
||||
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft | StrTrimRight
|
||||
| StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim | ListLen
|
||||
| ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat
|
||||
| ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2
|
||||
|
|
|
@ -19,6 +19,7 @@ pub enum LowLevel {
|
|||
StrFromFloat,
|
||||
StrTrim,
|
||||
StrTrimLeft,
|
||||
StrTrimRight,
|
||||
ListLen,
|
||||
ListGetUnsafe,
|
||||
ListSet,
|
||||
|
@ -131,6 +132,7 @@ macro_rules! first_order {
|
|||
| StrRepeat
|
||||
| StrTrim
|
||||
| StrTrimLeft
|
||||
| StrTrimRight
|
||||
| StrFromFloat
|
||||
| ListLen
|
||||
| ListGetUnsafe
|
||||
|
|
|
@ -1019,6 +1019,7 @@ define_builtins! {
|
|||
19 STR_REPEAT: "repeat"
|
||||
20 STR_TRIM: "trim"
|
||||
21 STR_TRIM_LEFT: "trimLeft"
|
||||
22 STR_TRIM_RIGHT: "trimRight"
|
||||
}
|
||||
4 LIST: "List" => {
|
||||
0 LIST_LIST: "List" imported // the List.List type alias
|
||||
|
|
|
@ -941,6 +941,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||
StrTrim => arena.alloc_slice_copy(&[owned]),
|
||||
StrTrimLeft => arena.alloc_slice_copy(&[owned]),
|
||||
StrTrimRight => arena.alloc_slice_copy(&[owned]),
|
||||
StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||
ListSingle => arena.alloc_slice_copy(&[irrelevant]),
|
||||
ListRepeat => arena.alloc_slice_copy(&[irrelevant, borrowed]),
|
||||
|
|
|
@ -1244,3 +1244,96 @@ fn str_trim_left_small_to_small_shared() {
|
|||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_small_blank_string() {
|
||||
assert_evals_to!(indoc!(r#"Str.trimRight " ""#), RocStr::from(""), RocStr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_small_to_small() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.trimRight " hello world ""#),
|
||||
RocStr::from(" hello world"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_large_to_large_unique() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.trimRight (Str.concat " hello world from a large string" " ")"#),
|
||||
RocStr::from(" hello world from a large string"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_large_to_small_unique() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.trimRight (Str.concat " hello world" " ")"#),
|
||||
RocStr::from(" hello world"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_large_to_large_shared() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
original : Str
|
||||
original = " hello world world "
|
||||
|
||||
{ trimmed: Str.trimRight original, original: original }
|
||||
"#
|
||||
),
|
||||
(
|
||||
RocStr::from(" hello world world "),
|
||||
RocStr::from(" hello world world"),
|
||||
),
|
||||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_large_to_small_shared() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
original : Str
|
||||
original = " hello world "
|
||||
|
||||
{ trimmed: Str.trimRight original, original: original }
|
||||
"#
|
||||
),
|
||||
(
|
||||
RocStr::from(" hello world "),
|
||||
RocStr::from(" hello world"),
|
||||
),
|
||||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn str_trim_right_small_to_small_shared() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
original : Str
|
||||
original = " hello world "
|
||||
|
||||
{ trimmed: Str.trimRight original, original: original }
|
||||
"#
|
||||
),
|
||||
(RocStr::from(" hello world "), RocStr::from(" hello world"),),
|
||||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue