mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
Make mod()
a macro that can take generic types instead of just integer
This commit is contained in:
parent
d48c590346
commit
92b4f52556
9 changed files with 78 additions and 15 deletions
|
@ -3,6 +3,9 @@ All notable changes to this project are documented in this file.
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Changed
|
||||
- `mod` now works on any numeric type, not only integers.
|
||||
|
||||
### Added
|
||||
|
||||
- `Button`: Add a `checkable` property that turns the button into a toggle
|
||||
|
|
|
@ -1067,9 +1067,9 @@ These functions are available both in the global scope and in the `Math` namespa
|
|||
|
||||
Return the arguments with the minimum (or maximum) value. All arguments must be of the same numeric type
|
||||
|
||||
* **`mod(int, int) -> int`**
|
||||
* **`mod(T, T) -> T`**
|
||||
|
||||
Perform a modulo operation.
|
||||
Perform a modulo operation, where T is some numeric type.
|
||||
|
||||
* **`abs(float) -> float`**
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ pub fn lower_macro(
|
|||
match mac {
|
||||
BuiltinMacroFunction::Min => min_max_macro(n, '<', sub_expr.collect(), diag),
|
||||
BuiltinMacroFunction::Max => min_max_macro(n, '>', sub_expr.collect(), diag),
|
||||
BuiltinMacroFunction::Mod => mod_macro(n, sub_expr.collect(), diag),
|
||||
BuiltinMacroFunction::Debug => debug_macro(n, sub_expr.collect(), diag),
|
||||
BuiltinMacroFunction::CubicBezier => {
|
||||
let mut has_error = None;
|
||||
|
@ -90,6 +91,51 @@ fn min_max_macro(
|
|||
base
|
||||
}
|
||||
|
||||
fn mod_macro(
|
||||
node: Option<NodeOrToken>,
|
||||
args: Vec<(Expression, Option<NodeOrToken>)>,
|
||||
diag: &mut BuildDiagnostics,
|
||||
) -> Expression {
|
||||
if args.len() != 2 {
|
||||
diag.push_error("Needs 2 arguments".into(), &node);
|
||||
return Expression::Invalid;
|
||||
}
|
||||
let (lhs_ty, rhs_ty) = (args[0].0.ty(), args[1].0.ty());
|
||||
let common_ty = if lhs_ty.default_unit().is_some() {
|
||||
lhs_ty
|
||||
} else if rhs_ty.default_unit().is_some() {
|
||||
rhs_ty
|
||||
} else if matches!(lhs_ty, Type::UnitProduct(_)) {
|
||||
lhs_ty
|
||||
} else if matches!(rhs_ty, Type::UnitProduct(_)) {
|
||||
rhs_ty
|
||||
} else {
|
||||
Type::Float32
|
||||
};
|
||||
|
||||
let source_location = node.map(|n| n.to_source_location());
|
||||
let function = Box::new(Expression::BuiltinFunctionReference(
|
||||
BuiltinFunction::Mod,
|
||||
source_location.clone(),
|
||||
));
|
||||
let arguments = args.into_iter().map(|(e, n)| e.maybe_convert_to(common_ty.clone(), &n, diag));
|
||||
if matches!(common_ty, Type::Float32) {
|
||||
Expression::FunctionCall { function, arguments: arguments.collect(), source_location }
|
||||
} else {
|
||||
Expression::Cast {
|
||||
from: Expression::FunctionCall {
|
||||
function,
|
||||
arguments: arguments
|
||||
.map(|a| Expression::Cast { from: a.into(), to: Type::Float32 })
|
||||
.collect(),
|
||||
source_location,
|
||||
}
|
||||
.into(),
|
||||
to: common_ty.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rgb_macro(
|
||||
node: Option<NodeOrToken>,
|
||||
args: Vec<(Expression, Option<NodeOrToken>)>,
|
||||
|
|
|
@ -53,11 +53,22 @@ pub enum BuiltinFunction {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A builtin function which is handled by the compiler pass
|
||||
///
|
||||
/// Builtin function expect their arguments in one and a specific type, so that's easier
|
||||
/// for the generator. Macro however can do some transformation on their argument.
|
||||
///
|
||||
pub enum BuiltinMacroFunction {
|
||||
/// Transform `min(a, b, c, ..., z)` into a series of conditional expression and comparisons
|
||||
Min,
|
||||
/// Transform `max(a, b, c, ..., z)` into a series of conditional expression and comparisons
|
||||
Max,
|
||||
/// Add the right conversion operations so that the return type is the same as the argument type
|
||||
Mod,
|
||||
CubicBezier,
|
||||
/// The argument can be r,g,b,a or r,g,b and they can be percentages or integer.
|
||||
/// transform the argument so it is always rgb(r, g, b, a) with r, g, b between 0 and 255.
|
||||
Rgb,
|
||||
/// transform `debug(a, b, c)` into debug `a + " " + b + " " + c`
|
||||
Debug,
|
||||
}
|
||||
|
||||
|
|
|
@ -2363,11 +2363,7 @@ fn compile_builtin_function_call(
|
|||
BuiltinFunction::Debug => {
|
||||
format!("std::cout << {} << std::endl;", a.join("<<"))
|
||||
}
|
||||
BuiltinFunction::Mod => format!(
|
||||
"static_cast<int>({}) % static_cast<int>({})",
|
||||
a.next().unwrap(),
|
||||
a.next().unwrap()
|
||||
),
|
||||
BuiltinFunction::Mod => format!("std::fmod({}, {})", a.next().unwrap(), a.next().unwrap()),
|
||||
BuiltinFunction::Round => format!("std::round({})", a.next().unwrap()),
|
||||
BuiltinFunction::Ceil => format!("std::ceil({})", a.next().unwrap()),
|
||||
BuiltinFunction::Floor => format!("std::floor({})", a.next().unwrap()),
|
||||
|
|
|
@ -2072,7 +2072,7 @@ fn compile_builtin_function_call(
|
|||
}
|
||||
BuiltinFunction::AnimationTick => quote!(slint::re_exports::current_tick().0),
|
||||
BuiltinFunction::Debug => quote!(slint::internal::debug(#(#a)*)),
|
||||
BuiltinFunction::Mod => quote!((#(#a as i32)%*)),
|
||||
BuiltinFunction::Mod => quote!((#(#a as f64)%*)),
|
||||
BuiltinFunction::Round => quote!((#(#a)* as f64).round()),
|
||||
BuiltinFunction::Ceil => quote!((#(#a)* as f64).ceil()),
|
||||
BuiltinFunction::Floor => quote!((#(#a)* as f64).floor()),
|
||||
|
|
|
@ -523,7 +523,7 @@ impl LookupObject for MathFunctions {
|
|||
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("mod", BuiltinFunctionReference(BuiltinFunction::Mod, sl())))
|
||||
None.or_else(|| f("mod", BuiltinMacroReference(BuiltinMacroFunction::Mod, t.clone())))
|
||||
.or_else(|| f("round", BuiltinFunctionReference(BuiltinFunction::Round, sl())))
|
||||
.or_else(|| f("ceil", BuiltinFunctionReference(BuiltinFunction::Ceil, sl())))
|
||||
.or_else(|| f("floor", BuiltinFunctionReference(BuiltinFunction::Floor, sl())))
|
||||
|
|
|
@ -230,8 +230,8 @@ pub fn eval_expression(expression: &Expression, local_context: &mut EvalLocalCon
|
|||
Value::Void
|
||||
}
|
||||
Expression::BuiltinFunctionReference(BuiltinFunction::Mod, _) => {
|
||||
let mut to_int = |e| -> i32 { eval_expression(e, local_context).try_into().unwrap() };
|
||||
Value::Number((to_int(&arguments[0]) % to_int(&arguments[1])) as _)
|
||||
let mut to_num = |e| -> f64 { eval_expression(e, local_context).try_into().unwrap() };
|
||||
Value::Number(to_num(&arguments[0]) % to_num(&arguments[1]))
|
||||
}
|
||||
Expression::BuiltinFunctionReference(BuiltinFunction::Round, _) => {
|
||||
let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
|
||||
|
|
|
@ -3,30 +3,37 @@
|
|||
|
||||
TestCase := Rectangle {
|
||||
property<int> t1: mod(42, 2);
|
||||
property<float> t2: mod(8.3, 10);
|
||||
property<float> t2: mod(18.5, 10);
|
||||
property<int> t3: mod(153, 10);
|
||||
property <duration> t4: mod(5432ms, 1s);
|
||||
|
||||
property <bool> test: t1 == 0 && t2 == 8.5 && t3 == 3 && t4 == 432ms;
|
||||
}
|
||||
/*
|
||||
```cpp
|
||||
auto handle = TestCase::create();
|
||||
const TestCase &instance = *handle;
|
||||
assert_eq(instance.get_t1(), 0);
|
||||
assert_eq(instance.get_t2(), 8.0);
|
||||
assert_eq(instance.get_t2(), 8.5);
|
||||
assert_eq(instance.get_t3(),3);
|
||||
assert_eq(instance.get_t4(),432);
|
||||
```
|
||||
|
||||
|
||||
```rust
|
||||
let instance = TestCase::new();
|
||||
assert_eq!(instance.get_t1(), 0);
|
||||
assert_eq!(instance.get_t2(), 8.0);
|
||||
assert_eq!(instance.get_t2(), 8.5);
|
||||
assert_eq!(instance.get_t3(), 3);
|
||||
assert_eq!(instance.get_t4(),432);
|
||||
```
|
||||
|
||||
```js
|
||||
var instance = new slint.TestCase({});
|
||||
assert.equal(instance.t1, 0);
|
||||
assert.equal(instance.t2, 8.0);
|
||||
assert.equal(instance.t2, 8.5);
|
||||
assert.equal(instance.t3, 3);
|
||||
assert.equal(instance.t4, 432);
|
||||
|
||||
```
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue