mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
add Str.endsWith
This commit is contained in:
parent
e8583a72fb
commit
22d798b7c7
12 changed files with 121 additions and 15 deletions
|
@ -19,6 +19,7 @@ comptime {
|
||||||
exportStrFn(str.countSegments, "count_segments");
|
exportStrFn(str.countSegments, "count_segments");
|
||||||
exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters");
|
exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters");
|
||||||
exportStrFn(str.startsWith, "starts_with");
|
exportStrFn(str.startsWith, "starts_with");
|
||||||
|
exportStrFn(str.endsWith, "ends_with");
|
||||||
exportStrFn(str.strConcat, "concat");
|
exportStrFn(str.strConcat, "concat");
|
||||||
exportStrFn(str.strLen, "len");
|
exportStrFn(str.strLen, "len");
|
||||||
}
|
}
|
||||||
|
|
|
@ -582,18 +582,53 @@ test "startsWith: 12345678912345678910 starts with 123456789123456789" {
|
||||||
expect(startsWith(str, prefix));
|
expect(startsWith(str, prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Str.split
|
// Str.endsWith
|
||||||
|
|
||||||
pub fn strSplit(ptr_size: u32, result_in_place: InPlace, string: RocStr, pattern: RocStr) callconv(.C) [*]RocStr {
|
pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool {
|
||||||
return switch (ptr_size) {
|
const bytes_len = string.len();
|
||||||
// 4 => strSplitHelp(i32, result_in_place, arg1, arg2),
|
const bytes_ptr = string.as_u8_ptr();
|
||||||
8 => strSplitHelp(i64, result_in_place, arg1, arg2),
|
|
||||||
else => unreachable,
|
const suffix_len = suffix.len();
|
||||||
};
|
const suffix_ptr = suffix.as_u8_ptr();
|
||||||
|
|
||||||
|
if (suffix_len > bytes_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset: usize = bytes_len - suffix_len;
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < suffix_len) {
|
||||||
|
if (bytes_ptr[i + offset] != suffix_ptr[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strSplitHelp(comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) [*]RocStr {
|
test "endsWith: foo ends with oo" {
|
||||||
return [_]u8{};
|
const foo = RocStr.init("foo", 3);
|
||||||
|
const oo = RocStr.init("oo", 2);
|
||||||
|
expect(endsWith(foo, oo));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "endsWith: 123456789123456789 ends with 123456789123456789" {
|
||||||
|
const str = RocStr.init("123456789123456789", 18);
|
||||||
|
expect(endsWith(str, str));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "endsWith: 12345678912345678910 ends with 345678912345678910" {
|
||||||
|
const str = RocStr.init("12345678912345678910", 20);
|
||||||
|
const suffix = RocStr.init("345678912345678910", 18);
|
||||||
|
|
||||||
|
expect(endsWith(str, suffix));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "endsWith: hello world ends with world" {
|
||||||
|
const str = RocStr.init("hello world", 11);
|
||||||
|
const suffix = RocStr.init("world", 5);
|
||||||
|
|
||||||
|
expect(endsWith(str, suffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Str.concat
|
// Str.concat
|
||||||
|
|
|
@ -28,4 +28,5 @@ pub const STR_CONCAT: &str = "roc_builtins.str.concat";
|
||||||
pub const STR_STR_SPLIT_IN_PLACE: &str = "roc_builtins.str.str_split_in_place";
|
pub const STR_STR_SPLIT_IN_PLACE: &str = "roc_builtins.str.str_split_in_place";
|
||||||
pub const STR_COUNT_GRAPEHEME_CLUSTERS: &str = "roc_builtins.str.count_grapheme_clusters";
|
pub const STR_COUNT_GRAPEHEME_CLUSTERS: &str = "roc_builtins.str.count_grapheme_clusters";
|
||||||
pub const STR_STARTS_WITH: &str = "roc_builtins.str.starts_with";
|
pub const STR_STARTS_WITH: &str = "roc_builtins.str.starts_with";
|
||||||
|
pub const STR_ENDS_WITH: &str = "roc_builtins.str.ends_with";
|
||||||
pub const STR_LEN: &str = "roc_builtins.str.len";
|
pub const STR_LEN: &str = "roc_builtins.str.len";
|
||||||
|
|
|
@ -417,6 +417,12 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
top_level_function(vec![str_type(), str_type()], Box::new(bool_type())),
|
top_level_function(vec![str_type(), str_type()], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// endsWith : Str, Str -> Bool
|
||||||
|
add_type(
|
||||||
|
Symbol::STR_ENDS_WITH,
|
||||||
|
top_level_function(vec![str_type(), str_type()], Box::new(bool_type())),
|
||||||
|
);
|
||||||
|
|
||||||
// countGraphemes : Str -> Int
|
// countGraphemes : Str -> Int
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::STR_COUNT_GRAPHEMES,
|
Symbol::STR_COUNT_GRAPHEMES,
|
||||||
|
|
|
@ -1096,6 +1096,12 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
unique_function(vec![str_type(star1), str_type(star2)], bool_type(star3))
|
unique_function(vec![str_type(star1), str_type(star2)], bool_type(star3))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Str.endsWith : Attr * Str, Attr * Str -> Attr * Bool
|
||||||
|
add_type(Symbol::STR_ENDS_WITH, {
|
||||||
|
let_tvars! { star1, star2, star3 };
|
||||||
|
unique_function(vec![str_type(star1), str_type(star2)], bool_type(star3))
|
||||||
|
});
|
||||||
|
|
||||||
// Str.countGraphemes : Attr * Str, -> Attr * Int
|
// Str.countGraphemes : Attr * Str, -> Attr * Int
|
||||||
add_type(Symbol::STR_COUNT_GRAPHEMES, {
|
add_type(Symbol::STR_COUNT_GRAPHEMES, {
|
||||||
let_tvars! { star1, star2 };
|
let_tvars! { star1, star2 };
|
||||||
|
|
|
@ -54,6 +54,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||||
Symbol::STR_SPLIT => str_split,
|
Symbol::STR_SPLIT => str_split,
|
||||||
Symbol::STR_IS_EMPTY => str_is_empty,
|
Symbol::STR_IS_EMPTY => str_is_empty,
|
||||||
Symbol::STR_STARTS_WITH => str_starts_with,
|
Symbol::STR_STARTS_WITH => str_starts_with,
|
||||||
|
Symbol::STR_ENDS_WITH => str_ends_with,
|
||||||
Symbol::STR_COUNT_GRAPHEMES => str_count_graphemes,
|
Symbol::STR_COUNT_GRAPHEMES => str_count_graphemes,
|
||||||
Symbol::LIST_LEN => list_len,
|
Symbol::LIST_LEN => list_len,
|
||||||
Symbol::LIST_GET => list_get,
|
Symbol::LIST_GET => list_get,
|
||||||
|
@ -989,6 +990,26 @@ fn str_starts_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Str.endsWith : Str, Str -> Bool
|
||||||
|
fn str_ends_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let str_var = var_store.fresh();
|
||||||
|
let bool_var = var_store.fresh();
|
||||||
|
|
||||||
|
let body = RunLowLevel {
|
||||||
|
op: LowLevel::StrEndsWith,
|
||||||
|
args: vec![(str_var, Var(Symbol::ARG_1)), (str_var, Var(Symbol::ARG_2))],
|
||||||
|
ret_var: bool_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(str_var, Symbol::ARG_1), (str_var, Symbol::ARG_2)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
bool_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Str.countGraphemes : Str -> Int
|
/// Str.countGraphemes : Str -> Int
|
||||||
fn str_count_graphemes(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn str_count_graphemes(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let str_var = var_store.fresh();
|
let str_var = var_store.fresh();
|
||||||
|
|
|
@ -4,7 +4,8 @@ use crate::llvm::build_list::{
|
||||||
list_reverse, list_set, list_single, list_sum, list_walk, list_walk_backwards,
|
list_reverse, list_set, list_single, list_sum, list_walk, list_walk_backwards,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_str::{
|
use crate::llvm::build_str::{
|
||||||
str_concat, str_count_graphemes, str_len, str_split, str_starts_with, CHAR_LAYOUT,
|
str_concat, str_count_graphemes, str_ends_with, str_len, str_split, str_starts_with,
|
||||||
|
CHAR_LAYOUT,
|
||||||
};
|
};
|
||||||
use crate::llvm::compare::{build_eq, build_neq};
|
use crate::llvm::compare::{build_eq, build_neq};
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{
|
||||||
|
@ -2436,6 +2437,14 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
str_starts_with(env, inplace, scope, parent, args[0], args[1])
|
str_starts_with(env, inplace, scope, parent, args[0], args[1])
|
||||||
}
|
}
|
||||||
|
StrEndsWith => {
|
||||||
|
// Str.startsWith : Str, Str -> Bool
|
||||||
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
|
let inplace = get_inplace_from_layout(layout);
|
||||||
|
|
||||||
|
str_ends_with(env, inplace, scope, parent, args[0], args[1])
|
||||||
|
}
|
||||||
StrSplit => {
|
StrSplit => {
|
||||||
// Str.split : Str, Str -> List Str
|
// Str.split : Str, Str -> List Str
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use crate::llvm::build::{
|
use crate::llvm::build::{
|
||||||
call_bitcode_fn, call_void_bitcode_fn, ptr_from_symbol, Env, InPlace, Scope,
|
call_bitcode_fn, call_void_bitcode_fn, ptr_from_symbol, Env, InPlace, Scope,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_list::{allocate_list, build_basic_phi2, store_list};
|
use crate::llvm::build_list::{allocate_list, store_list};
|
||||||
use crate::llvm::convert::collection;
|
use crate::llvm::convert::collection;
|
||||||
use inkwell::builder::Builder;
|
|
||||||
use inkwell::types::BasicTypeEnum;
|
use inkwell::types::BasicTypeEnum;
|
||||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, StructValue};
|
||||||
use inkwell::{AddressSpace, IntPredicate};
|
use inkwell::AddressSpace;
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
|
@ -208,6 +207,25 @@ pub fn str_starts_with<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Str.endsWith : Str, Str -> Bool
|
||||||
|
pub fn str_ends_with<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
_inplace: InPlace,
|
||||||
|
scope: &Scope<'a, 'ctx>,
|
||||||
|
_parent: FunctionValue<'ctx>,
|
||||||
|
str_symbol: Symbol,
|
||||||
|
prefix_symbol: Symbol,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let str_i128 = str_symbol_to_i128(env, scope, str_symbol);
|
||||||
|
let prefix_i128 = str_symbol_to_i128(env, scope, prefix_symbol);
|
||||||
|
|
||||||
|
call_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[str_i128.into(), prefix_i128.into()],
|
||||||
|
&bitcode::STR_ENDS_WITH,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Str.countGraphemes : Str -> Int
|
/// Str.countGraphemes : Str -> Int
|
||||||
pub fn str_count_graphemes<'a, 'ctx, 'env>(
|
pub fn str_count_graphemes<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
|
|
@ -433,6 +433,13 @@ mod gen_str {
|
||||||
assert_evals_to!(r#"Str.startsWith "" "hello world""#, false, bool);
|
assert_evals_to!(r#"Str.startsWith "" "hello world""#, false, bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_ends_with() {
|
||||||
|
assert_evals_to!(r#"Str.endsWith "hello world" "world""#, true, bool);
|
||||||
|
assert_evals_to!(r#"Str.endsWith "nope" "hello world""#, false, bool);
|
||||||
|
assert_evals_to!(r#"Str.endsWith "" "hello world""#, false, bool);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn str_count_graphemes_small_str() {
|
fn str_count_graphemes_small_str() {
|
||||||
assert_evals_to!(r#"Str.countGraphemes "å🤔""#, 2, usize);
|
assert_evals_to!(r#"Str.countGraphemes "å🤔""#, 2, usize);
|
||||||
|
|
|
@ -6,6 +6,7 @@ pub enum LowLevel {
|
||||||
StrConcat,
|
StrConcat,
|
||||||
StrIsEmpty,
|
StrIsEmpty,
|
||||||
StrStartsWith,
|
StrStartsWith,
|
||||||
|
StrEndsWith,
|
||||||
StrSplit,
|
StrSplit,
|
||||||
StrCountGraphemes,
|
StrCountGraphemes,
|
||||||
ListLen,
|
ListLen,
|
||||||
|
|
|
@ -673,6 +673,7 @@ define_builtins! {
|
||||||
5 STR_SPLIT: "split"
|
5 STR_SPLIT: "split"
|
||||||
6 STR_COUNT_GRAPHEMES: "countGraphemes"
|
6 STR_COUNT_GRAPHEMES: "countGraphemes"
|
||||||
7 STR_STARTS_WITH: "startsWith"
|
7 STR_STARTS_WITH: "startsWith"
|
||||||
|
8 STR_ENDS_WITH: "endsWith"
|
||||||
}
|
}
|
||||||
4 LIST: "List" => {
|
4 LIST: "List" => {
|
||||||
0 LIST_LIST: "List" imported // the List.List type alias
|
0 LIST_LIST: "List" imported // the List.List type alias
|
||||||
|
|
|
@ -547,6 +547,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
| NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
| NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
||||||
arena.alloc_slice_copy(&[irrelevant])
|
arena.alloc_slice_copy(&[irrelevant])
|
||||||
}
|
}
|
||||||
StrStartsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue