Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds String Functions #6657

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
50 changes: 50 additions & 0 deletions docs/reference/src/language/builtins/namespaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,53 @@ Square root
### `pow(float, float) -> float`

Return the value of the first value raised to the second

## `String`

### `contains(string, string) -> bool`

Return true if the first string contains the second string.

### `starts-with(string, string) -> bool`

Return true if the first string starts with the second string.

### `ends-with(string, string) -> bool`

Return true if the first string ends with the second string.

### `slice(string, int, int) -> string`

Return a substring of the first string, starting at the first index and ending at the second index.

### `slice-by-length(string, int, int) -> string`

Return a substring of the first string, starting at the first index and includes the number of characters specified by the second index.

### `replace-first(string, string, string) -> string`

Return a new string with the first string replaced by the second string.

### `replace-last(string, string, string) -> string`

Return a new string with the last occurrence of the second string replaced by the third string.

### `replace-nth(string, string, string, int) -> string`

Return a new string with the nth occurrence of the second string replaced by the third string.

### `replace-all(string, string, string) -> string`

Return a new string with all occurrences of the second string replaced by the third string.

### `trim(string) -> string`

Removes whitespace from both ends of the string including newlines, tabs, and spaces.

### `trim-start(string) -> string`

Removes whitespace from the start of the string including newlines, tabs, and spaces.

### `trim-end(string) -> string`

Removes whitespace from the end of the string including newlines, tabs, and spaces.
59 changes: 59 additions & 0 deletions internal/compiler/expression_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ pub enum BuiltinFunction {
ATan2,
Log,
Pow,
ReplaceFirst,
ReplaceNth,
ReplaceLast,
ReplaceAll,
Contains,
StartsWith,
EndsWith,
Slice,
SliceByLen,
Trim,
TrimStart,
TrimEnd,
SetFocusItem,
ClearFocusItem,
ShowPopupWindow,
Expand Down Expand Up @@ -147,6 +159,29 @@ impl BuiltinFunction {
return_type: Box::new(Type::Float32),
args: vec![Type::Float32, Type::Float32],
},
BuiltinFunction::ReplaceFirst
| BuiltinFunction::ReplaceLast
| BuiltinFunction::ReplaceAll => Type::Function {
return_type: Box::new(Type::String),
args: vec![Type::String, Type::String, Type::String],
},
BuiltinFunction::ReplaceNth => Type::Function {
return_type: Box::new(Type::String),
args: vec![Type::String, Type::String, Type::String, Type::Int32],
},
BuiltinFunction::Contains | BuiltinFunction::StartsWith | BuiltinFunction::EndsWith => {
Type::Function {
return_type: Box::new(Type::Bool),
args: vec![Type::String, Type::String],
}
}
BuiltinFunction::Slice | BuiltinFunction::SliceByLen => Type::Function {
return_type: Box::new(Type::String),
args: vec![Type::String, Type::Int32, Type::Int32],
},
BuiltinFunction::Trim | BuiltinFunction::TrimStart | BuiltinFunction::TrimEnd => {
Type::Function { return_type: Box::new(Type::String), args: vec![Type::String] }
}
BuiltinFunction::SetFocusItem => Type::Function {
return_type: Box::new(Type::Void),
args: vec![Type::ElementReference],
Expand Down Expand Up @@ -355,6 +390,18 @@ impl BuiltinFunction {
| BuiltinFunction::Pow
| BuiltinFunction::ATan
| BuiltinFunction::ATan2 => true,
BuiltinFunction::ReplaceFirst
| BuiltinFunction::ReplaceLast
| BuiltinFunction::ReplaceNth
| BuiltinFunction::ReplaceAll
| BuiltinFunction::Contains
| BuiltinFunction::StartsWith
| BuiltinFunction::EndsWith
| BuiltinFunction::Slice
| BuiltinFunction::SliceByLen
| BuiltinFunction::Trim
| BuiltinFunction::TrimStart
| BuiltinFunction::TrimEnd => false,
BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => false,
BuiltinFunction::ShowPopupWindow | BuiltinFunction::ClosePopupWindow => false,
BuiltinFunction::SetSelectionOffsets => false,
Expand Down Expand Up @@ -422,6 +469,18 @@ impl BuiltinFunction {
| BuiltinFunction::Pow
| BuiltinFunction::ATan
| BuiltinFunction::ATan2 => true,
BuiltinFunction::ReplaceFirst
| BuiltinFunction::ReplaceLast
| BuiltinFunction::ReplaceNth
| BuiltinFunction::ReplaceAll
| BuiltinFunction::Contains
| BuiltinFunction::StartsWith
| BuiltinFunction::EndsWith
| BuiltinFunction::Slice
| BuiltinFunction::SliceByLen
| BuiltinFunction::Trim
| BuiltinFunction::TrimStart
| BuiltinFunction::TrimEnd => false,
BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => false,
BuiltinFunction::ShowPopupWindow | BuiltinFunction::ClosePopupWindow => false,
BuiltinFunction::SetSelectionOffsets => false,
Expand Down
49 changes: 49 additions & 0 deletions internal/compiler/generator/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ struct ConditionalIncludes {
iostream: Cell<bool>,
cstdlib: Cell<bool>,
cmath: Cell<bool>,
string: Cell<bool>,
}

#[derive(Clone)]
Expand Down Expand Up @@ -3442,6 +3443,54 @@ fn compile_builtin_function_call(
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::atan2({}, {}) / {}", a.next().unwrap(), a.next().unwrap(), pi_180)
}
BuiltinFunction::ReplaceFirst => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::replace_first({}, {}, {})", a.next().unwrap(), a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::ReplaceLast => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::replace_last({}, {}, {})", a.next().unwrap(), a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::ReplaceNth => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::replace_nth({}, {}, {}, {})", a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::ReplaceAll => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::replace_all({}, {}, {})", a.next().unwrap(), a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::Contains => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::contains({}, {})", a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::StartsWith => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::starts_with({}, {})", a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::EndsWith => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::ends_with({}, {})", a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::Trim => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::trim({})", a.next().unwrap())
}
BuiltinFunction::TrimStart => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::trim_start({})", a.next().unwrap())
}
BuiltinFunction::TrimEnd => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::trim_end({})", a.next().unwrap())
}
BuiltinFunction::SliceByLen => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::substr({}, {}, {})", a.next().unwrap(), a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::Slice => {
ctx.generator_state.conditional_includes.string.set(true);
format!("slint::private_api::substring({}, {}, {})", a.next().unwrap(), a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::SetFocusItem => {
if let [llr::Expression::PropertyReference(pr)] = arguments {
let window = access_window_field(ctx);
Expand Down
40 changes: 40 additions & 0 deletions internal/compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2781,6 +2781,46 @@ fn compile_builtin_function_call(
BuiltinFunction::StringToFloat => {
quote!(#(#a)*.as_str().parse::<f64>().unwrap_or_default())
}
BuiltinFunction::ReplaceFirst => {
let (s, from, to) = (a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
quote!(#s.replace_first(#from.as_str(), #to.as_str()))
}
BuiltinFunction::ReplaceLast => {
let (s, from, to) = (a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
quote!(#s.replace_last(#from.as_str(), #to.as_str()))
}
BuiltinFunction::ReplaceNth => {
let (s, from, to, n) =
(a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
quote!(#s.replace_nth(#from.as_str(), #to.as_str(), #n as usize))
}
BuiltinFunction::ReplaceAll => {
let (s, from, to) = (a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
quote!(#s.replace_all(#from.as_str(), #to.as_str()))
}
BuiltinFunction::Contains => {
let (s, sub) = (a.next().unwrap(), a.next().unwrap());
quote!(#s.as_str().contains(#sub.as_str()))
}
BuiltinFunction::StartsWith => {
let (s, sub) = (a.next().unwrap(), a.next().unwrap());
quote!(#s.starts_with_str(#sub.as_str()))
}
BuiltinFunction::EndsWith => {
let (s, sub) = (a.next().unwrap(), a.next().unwrap());
quote!(#s.ends_with_str(#sub.as_str()))
}
BuiltinFunction::Slice => {
let (s, start, end) = (a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
quote!(#s.slice(#start as usize, #end as usize))
}
BuiltinFunction::SliceByLen => {
let (s, start, len) = (a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
quote!(#s.slice_by_len(#start as usize, #len as usize))
}
BuiltinFunction::Trim => quote!(#(#a)*.trim()),
BuiltinFunction::TrimStart => quote!(#(#a)*.trim_start()),
BuiltinFunction::TrimEnd => quote!(#(#a)*.trim_end()),
BuiltinFunction::StringIsFloat => quote!(#(#a)*.as_str().parse::<f64>().is_ok()),
BuiltinFunction::ColorRgbaStruct => quote!( #(#a)*.to_argb_u8()),
BuiltinFunction::ColorHsvaStruct => quote!( #(#a)*.to_hsva()),
Expand Down
12 changes: 12 additions & 0 deletions internal/compiler/llr/optim_passes/inline_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ fn builtin_function_cost(function: &BuiltinFunction) -> isize {
BuiltinFunction::ATan2 => 10,
BuiltinFunction::Log => 10,
BuiltinFunction::Pow => 10,
BuiltinFunction::ReplaceFirst
| BuiltinFunction::ReplaceLast
| BuiltinFunction::ReplaceNth
| BuiltinFunction::ReplaceAll
| BuiltinFunction::Contains
| BuiltinFunction::StartsWith
| BuiltinFunction::EndsWith
| BuiltinFunction::Slice
| BuiltinFunction::SliceByLen
| BuiltinFunction::Trim
| BuiltinFunction::TrimStart
| BuiltinFunction::TrimEnd => 10,
BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => isize::MAX,
BuiltinFunction::ShowPopupWindow | BuiltinFunction::ClosePopupWindow => isize::MAX,
BuiltinFunction::SetSelectionOffsets => isize::MAX,
Expand Down
37 changes: 36 additions & 1 deletion internal/compiler/lookup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ pub enum LookupResult {
pub enum BuiltinNamespace {
Colors,
Math,
String,
Key,
SlintInternal,
}
Expand Down Expand Up @@ -165,6 +166,9 @@ impl LookupObject for LookupResult {
(ColorSpecific, ColorFunctions).for_each_entry(ctx, f)
}
LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.for_each_entry(ctx, f),
LookupResult::Namespace(BuiltinNamespace::String) => {
StringFunctions.for_each_entry(ctx, f)
}
LookupResult::Namespace(BuiltinNamespace::Key) => KeysLookup.for_each_entry(ctx, f),
LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
SlintInternal.for_each_entry(ctx, f)
Expand All @@ -180,6 +184,7 @@ impl LookupObject for LookupResult {
(ColorSpecific, ColorFunctions).lookup(ctx, name)
}
LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.lookup(ctx, name),
LookupResult::Namespace(BuiltinNamespace::String) => StringFunctions.lookup(ctx, name),
LookupResult::Namespace(BuiltinNamespace::Key) => KeysLookup.lookup(ctx, name),
LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
SlintInternal.lookup(ctx, name)
Expand Down Expand Up @@ -810,6 +815,36 @@ impl LookupObject for MathFunctions {
}
}

struct StringFunctions;
impl LookupObject for StringFunctions {
fn for_each_entry<R>(
&self,
ctx: &LookupCtx,
f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
) -> Option<R> {
use Expression::BuiltinFunctionReference;
let t = &ctx.current_token;
let sl = || t.as_ref().map(|t| t.to_source_location());
let mut f = |n, e: Expression| f(n, e.into());
None.or_else(|| {
f("replace-first", BuiltinFunctionReference(BuiltinFunction::ReplaceFirst, sl()))
})
.or_else(|| f("replace-last", BuiltinFunctionReference(BuiltinFunction::ReplaceLast, sl())))
.or_else(|| f("replace-nth", BuiltinFunctionReference(BuiltinFunction::ReplaceNth, sl())))
.or_else(|| f("replace-all", BuiltinFunctionReference(BuiltinFunction::ReplaceAll, sl())))
.or_else(|| f("contains", BuiltinFunctionReference(BuiltinFunction::Contains, sl())))
.or_else(|| f("slice", BuiltinFunctionReference(BuiltinFunction::Slice, sl())))
.or_else(|| {
f("slice-by-length", BuiltinFunctionReference(BuiltinFunction::SliceByLen, sl()))
})
.or_else(|| f("starts-with", BuiltinFunctionReference(BuiltinFunction::StartsWith, sl())))
.or_else(|| f("ends-with", BuiltinFunctionReference(BuiltinFunction::EndsWith, sl())))
.or_else(|| f("trim", BuiltinFunctionReference(BuiltinFunction::Trim, sl())))
.or_else(|| f("trim-start", BuiltinFunctionReference(BuiltinFunction::TrimStart, sl())))
.or_else(|| f("trim-end", BuiltinFunctionReference(BuiltinFunction::TrimEnd, sl())))
}
}

struct SlintInternal;
impl LookupObject for SlintInternal {
fn for_each_entry<R>(
Expand Down Expand Up @@ -882,7 +917,7 @@ impl LookupObject for BuiltinFunctionLookup {
ctx: &LookupCtx,
f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
) -> Option<R> {
(MathFunctions, ColorFunctions)
((MathFunctions, ColorFunctions), StringFunctions)
.for_each_entry(ctx, f)
.or_else(|| {
f(
Expand Down
Loading
Loading