sixtyfps_compilerlib/llr/
expression.rs

1// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
2// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
3
4use super::PropertyReference;
5use crate::expression_tree::{BuiltinFunction, OperatorClass};
6use crate::langtype::Type;
7use crate::layout::Orientation;
8use itertools::Either;
9use std::collections::HashMap;
10
11#[derive(Debug, Clone)]
12pub enum Expression {
13    /// A string literal. The .0 is the content of the string, without the quotes
14    StringLiteral(String),
15    /// Number
16    NumberLiteral(f64),
17    /// Bool
18    BoolLiteral(bool),
19
20    /// Reference to a property (which can also be a callback) or an element (property name is empty then).
21    PropertyReference(PropertyReference),
22
23    /// Reference the parameter at the given index of the current function.
24    FunctionParameterReference {
25        index: usize,
26        //ty: Type,
27    },
28
29    /// Should be directly within a CodeBlock expression, and store the value of the expression in a local variable
30    StoreLocalVariable {
31        name: String,
32        value: Box<Expression>,
33    },
34
35    /// a reference to the local variable with the given name. The type system should ensure that a variable has been stored
36    /// with this name and this type before in one of the statement of an enclosing codeblock
37    ReadLocalVariable {
38        name: String,
39        ty: Type,
40    },
41
42    /// Access to a field of the given name within a struct.
43    StructFieldAccess {
44        /// This expression should have [`Type::Struct`] type
45        base: Box<Expression>,
46        name: String,
47    },
48
49    /// Access to a index within an array.
50    ArrayIndex {
51        /// This expression should have [`Type::Array`] type
52        array: Box<Expression>,
53        index: Box<Expression>,
54    },
55
56    /// Cast an expression to the given type
57    Cast {
58        from: Box<Expression>,
59        to: Type,
60    },
61
62    /// a code block with different expression
63    CodeBlock(Vec<Expression>),
64
65    /// A function call
66    BuiltinFunctionCall {
67        function: BuiltinFunction,
68        arguments: Vec<Expression>,
69    },
70    CallBackCall {
71        callback: PropertyReference,
72        arguments: Vec<Expression>,
73    },
74
75    /// A BuiltinFunctionCall, but the function is not yet in the `BuiltinFunction` enum
76    /// TODO: merge in BuiltinFunctionCall
77    ExtraBuiltinFunctionCall {
78        return_ty: Type,
79        function: String,
80        arguments: Vec<Expression>,
81    },
82
83    /// An assignment of a value to a property
84    PropertyAssignment {
85        property: PropertyReference,
86        value: Box<Expression>,
87    },
88    /// an assignment of a value to the model data
89    ModelDataAssignment {
90        // how deep in the parent hierarchy we go
91        level: usize,
92        value: Box<Expression>,
93    },
94    /// An assignement done with the `foo[idx] = ...`
95    ArrayIndexAssignment {
96        array: Box<Expression>,
97        index: Box<Expression>,
98        value: Box<Expression>,
99    },
100
101    BinaryExpression {
102        lhs: Box<Expression>,
103        rhs: Box<Expression>,
104        /// '+', '-', '/', '*', '=', '!', '<', '>', '≤', '≥', '&', '|'
105        op: char,
106    },
107
108    UnaryOp {
109        sub: Box<Expression>,
110        /// '+', '-', '!'
111        op: char,
112    },
113
114    ImageReference {
115        resource_ref: crate::expression_tree::ImageReference,
116    },
117
118    Condition {
119        condition: Box<Expression>,
120        true_expr: Box<Expression>,
121        false_expr: Box<Expression>,
122    },
123
124    Array {
125        element_ty: Type,
126        values: Vec<Expression>,
127        /// When true, this should be converted to a model. When false, this should stay as a slice
128        as_model: bool,
129    },
130    Struct {
131        ty: Type,
132        values: HashMap<String, Expression>,
133    },
134
135    EasingCurve(crate::expression_tree::EasingCurve),
136
137    LinearGradient {
138        angle: Box<Expression>,
139        /// First expression in the tuple is a color, second expression is the stop position
140        stops: Vec<(Expression, Expression)>,
141    },
142
143    EnumerationValue(crate::langtype::EnumerationValue),
144
145    ReturnStatement(Option<Box<Expression>>),
146
147    LayoutCacheAccess {
148        layout_cache_prop: PropertyReference,
149        index: usize,
150        /// When set, this is the index within a repeater, and the index is then the location of another offset.
151        /// So this looks like `layout_cache_prop[layout_cache_prop[index] + repeater_index]`
152        repeater_index: Option<Box<Expression>>,
153    },
154    /// Will call the sub_expression, with the cell variable set to the
155    /// array the array of BoxLayoutCellData form the elements
156    BoxLayoutFunction {
157        /// The local variable (as read with [`Self::ReadLocalVariable`]) that contains the sell
158        cells_variable: String,
159        /// The name for the local variable that contains the repeater indices
160        repeater_indices: Option<String>,
161        /// Either an expression of type BoxLayoutCellData, or an index to the repeater
162        elements: Vec<Either<Expression, usize>>,
163        orientation: Orientation,
164        sub_expression: Box<Expression>,
165    },
166
167    ComputeDialogLayoutCells {
168        /// The local variable where the slice of cells is going to be stored
169        cells_variable: String,
170        roles: Box<Expression>,
171        /// This is an Expression::Array
172        unsorted_cells: Box<Expression>,
173    },
174}
175
176impl Expression {
177    pub fn default_value_for_type(ty: &Type) -> Option<Self> {
178        Some(match ty {
179            Type::Invalid
180            | Type::Component(_)
181            | Type::Builtin(_)
182            | Type::Native(_)
183            | Type::Callback { .. }
184            | Type::Function { .. }
185            | Type::Void
186            | Type::InferredProperty
187            | Type::InferredCallback
188            | Type::ElementReference
189            | Type::LayoutCache => return None,
190            Type::Float32
191            | Type::Duration
192            | Type::Int32
193            | Type::Angle
194            | Type::PhysicalLength
195            | Type::LogicalLength
196            | Type::UnitProduct(_) => Expression::NumberLiteral(0.),
197            Type::Percent => Expression::NumberLiteral(1.),
198            Type::String => Expression::StringLiteral(String::new()),
199            Type::Color => {
200                Expression::Cast { from: Box::new(Expression::NumberLiteral(0.)), to: ty.clone() }
201            }
202            Type::Image => Expression::ImageReference {
203                resource_ref: crate::expression_tree::ImageReference::None,
204            },
205            Type::Bool => Expression::BoolLiteral(false),
206            Type::Model => return None,
207            Type::PathData => return None,
208            Type::Array(element_ty) => Expression::Array {
209                element_ty: (**element_ty).clone(),
210                values: vec![],
211                as_model: true,
212            },
213            Type::Struct { fields, .. } => Expression::Struct {
214                ty: ty.clone(),
215                values: fields
216                    .iter()
217                    .map(|(k, v)| Some((k.clone(), Expression::default_value_for_type(v)?)))
218                    .collect::<Option<_>>()?,
219            },
220            Type::Easing => Expression::EasingCurve(crate::expression_tree::EasingCurve::default()),
221            Type::Brush => Expression::Cast {
222                from: Box::new(Expression::default_value_for_type(&Type::Color)?),
223                to: Type::Brush,
224            },
225            Type::Enumeration(enumeration) => {
226                Expression::EnumerationValue(enumeration.clone().default_value())
227            }
228        })
229    }
230
231    pub fn ty(&self, ctx: &dyn TypeResolutionContext) -> Type {
232        match self {
233            Self::StringLiteral(_) => Type::String,
234            Self::NumberLiteral(_) => Type::Float32,
235            Self::BoolLiteral(_) => Type::Bool,
236            Self::PropertyReference(prop) => ctx.property_ty(prop).clone(),
237            Self::FunctionParameterReference { index } => ctx.arg_type(*index).clone(),
238            Self::StoreLocalVariable { .. } => Type::Void,
239            Self::ReadLocalVariable { ty, .. } => ty.clone(),
240            Self::StructFieldAccess { base, name } => match base.ty(ctx) {
241                Type::Struct { fields, .. } => fields[name].clone(),
242                _ => unreachable!(),
243            },
244            Self::ArrayIndex { array, .. } => array.ty(ctx),
245            Self::Cast { to, .. } => to.clone(),
246            Self::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty(ctx)),
247            Self::BuiltinFunctionCall { function, .. } => match function.ty() {
248                Type::Function { return_type, .. } => *return_type,
249                _ => unreachable!(),
250            },
251            Self::CallBackCall { callback, .. } => {
252                if let Type::Callback { return_type, .. } = ctx.property_ty(callback) {
253                    return_type.as_ref().map_or(Type::Void, |x| (**x).clone())
254                } else {
255                    Type::Invalid
256                }
257            }
258            Self::ExtraBuiltinFunctionCall { return_ty, .. } => return_ty.clone(),
259            Self::PropertyAssignment { .. } => Type::Void,
260            Self::ModelDataAssignment { .. } => Type::Void,
261            Self::ArrayIndexAssignment { .. } => Type::Void,
262            Self::BinaryExpression { lhs, rhs: _, op } => {
263                if crate::expression_tree::operator_class(*op) != OperatorClass::ArithmeticOp {
264                    Type::Bool
265                } else {
266                    lhs.ty(ctx)
267                }
268            }
269            Self::UnaryOp { sub, .. } => sub.ty(ctx),
270            Self::ImageReference { .. } => Type::Image,
271            Self::Condition { true_expr, .. } => true_expr.ty(ctx),
272            Self::Array { element_ty, .. } => Type::Array(element_ty.clone().into()),
273            Self::Struct { ty, .. } => ty.clone(),
274            Self::EasingCurve(_) => Type::Easing,
275            Self::LinearGradient { .. } => Type::Brush,
276            Self::EnumerationValue(e) => Type::Enumeration(e.enumeration.clone()),
277            Self::ReturnStatement(_) => Type::Invalid,
278            Self::LayoutCacheAccess { .. } => Type::Array(Type::Int32.into()),
279            Self::BoxLayoutFunction { sub_expression, .. } => sub_expression.ty(ctx),
280            Self::ComputeDialogLayoutCells { .. } => {
281                Type::Array(super::lower_expression::grid_layout_cell_data_ty().into())
282            }
283        }
284    }
285
286    /// Call the visitor for each sub-expression (not recursive)
287    fn visit(&self, mut visitor: impl FnMut(&Self)) {
288        match self {
289            Expression::StringLiteral(_) => {}
290            Expression::NumberLiteral(_) => {}
291            Expression::BoolLiteral(_) => {}
292            Expression::PropertyReference(_) => {}
293            Expression::FunctionParameterReference { .. } => {}
294            Expression::StoreLocalVariable { value, .. } => visitor(&value),
295            Expression::ReadLocalVariable { .. } => {}
296            Expression::StructFieldAccess { base, .. } => visitor(&base),
297            Expression::ArrayIndex { array, index } => {
298                (visitor(array), visitor(index));
299            }
300            Expression::Cast { from, .. } => visitor(from),
301            Expression::CodeBlock(b) => b.iter().for_each(visitor),
302            Expression::BuiltinFunctionCall { arguments, .. } => arguments.iter().for_each(visitor),
303            Expression::CallBackCall { arguments, .. } => arguments.iter().for_each(visitor),
304            Expression::ExtraBuiltinFunctionCall { arguments, .. } => {
305                arguments.iter().for_each(visitor)
306            }
307            Expression::PropertyAssignment { value, .. } => visitor(&value),
308            Expression::ModelDataAssignment { value, .. } => visitor(&value),
309            Expression::ArrayIndexAssignment { array, index, value } => {
310                (visitor(array), visitor(index), visitor(value));
311            }
312            Expression::BinaryExpression { lhs, rhs, .. } => {
313                (visitor(lhs), visitor(rhs));
314            }
315            Expression::UnaryOp { sub, .. } => {
316                visitor(sub);
317            }
318            Expression::ImageReference { .. } => {}
319            Expression::Condition { condition, true_expr, false_expr } => {
320                visitor(&condition);
321                visitor(&true_expr);
322                visitor(&false_expr);
323            }
324            Expression::Array { values, .. } => values.iter().for_each(visitor),
325            Expression::Struct { values, .. } => values.values().for_each(visitor),
326            Expression::EasingCurve(_) => {}
327            Expression::LinearGradient { angle, stops } => {
328                visitor(&angle);
329                for (a, b) in stops {
330                    visitor(a);
331                    visitor(b);
332                }
333            }
334            Expression::EnumerationValue(_) => {}
335            Expression::ReturnStatement(_) => {}
336            Expression::LayoutCacheAccess { repeater_index, .. } => {
337                if let Some(repeater_index) = repeater_index {
338                    visitor(&repeater_index);
339                }
340            }
341            Expression::BoxLayoutFunction { elements, sub_expression, .. } => {
342                visitor(&sub_expression);
343                elements.iter().filter_map(|x| x.as_ref().left()).for_each(visitor);
344            }
345            Expression::ComputeDialogLayoutCells { roles, unsorted_cells, .. } => {
346                visitor(&roles);
347                visitor(&unsorted_cells);
348            }
349        }
350    }
351
352    /// Visit itself and each sub expression recursively
353    pub fn visit_recursive(&self, visitor: &mut dyn FnMut(&Self)) {
354        visitor(self);
355        self.visit(|e| e.visit_recursive(visitor));
356    }
357}
358
359pub trait TypeResolutionContext {
360    fn property_ty(&self, _: &PropertyReference) -> &Type;
361    // The type of the specified argument when evaluating a callback
362    fn arg_type(&self, _index: usize) -> &Type {
363        unimplemented!()
364    }
365}
366
367#[derive(Clone, Copy)]
368pub struct ParentCtx<'a, T> {
369    pub ctx: &'a EvaluationContext<'a, T>,
370    // Index of the repeater within the ctx.current_sub_component
371    pub repeater_index: Option<usize>,
372}
373
374impl<'a, T> ParentCtx<'a, T> {
375    pub fn new(ctx: &'a EvaluationContext<'a, T>, repeater_index: Option<usize>) -> Self {
376        Self { ctx, repeater_index }
377    }
378}
379
380#[derive(Clone)]
381pub struct EvaluationContext<'a, T> {
382    pub public_component: &'a super::PublicComponent,
383    pub current_sub_component: Option<&'a super::SubComponent>,
384    pub current_global: Option<&'a super::GlobalComponent>,
385    /// path to access the public_component (so one can access the globals).
386    /// e.g: `_self` in case we already are the root
387    pub generator_state: T,
388    /// The repeater parent
389    pub parent: Option<ParentCtx<'a, T>>,
390
391    /// The callback argument types
392    pub argument_types: &'a [Type],
393}
394
395impl<'a, T> EvaluationContext<'a, T> {
396    pub fn new_sub_component(
397        public_component: &'a super::PublicComponent,
398        sub_component: &'a super::SubComponent,
399        generator_state: T,
400        parent: Option<ParentCtx<'a, T>>,
401    ) -> Self {
402        /*let generator_state = if let Some(parent) = &parent {
403            let p = &parent.ctx.generator_state;
404            quote!(parent.)
405        } else {
406            quote!(_self)
407        };*/
408        Self {
409            public_component,
410            current_sub_component: Some(sub_component),
411            current_global: None,
412            generator_state,
413            parent,
414            argument_types: &[],
415        }
416    }
417}
418
419impl<'a, T> TypeResolutionContext for EvaluationContext<'a, T> {
420    fn property_ty(&self, prop: &PropertyReference) -> &Type {
421        match prop {
422            PropertyReference::Local { sub_component_path, property_index } => {
423                if let Some(mut sub_component) = self.current_sub_component {
424                    for i in sub_component_path {
425                        sub_component = &sub_component.sub_components[*i].ty;
426                    }
427                    &sub_component.properties[*property_index].ty
428                } else if let Some(current_global) = self.current_global {
429                    &current_global.properties[*property_index].ty
430                } else {
431                    unreachable!()
432                }
433            }
434            PropertyReference::InNativeItem { sub_component_path, item_index, prop_name } => {
435                if prop_name == "elements" {
436                    // The `Path::elements` property is not in the NativeClasss
437                    return &Type::PathData;
438                }
439
440                let mut sub_component = self.current_sub_component.unwrap();
441                for i in sub_component_path {
442                    sub_component = &sub_component.sub_components[*i].ty;
443                }
444                sub_component.items[*item_index].ty.lookup_property(prop_name).unwrap()
445            }
446            PropertyReference::InParent { level, parent_reference } => {
447                let mut ctx = self;
448                for _ in 0..level.get() {
449                    ctx = ctx.parent.as_ref().unwrap().ctx;
450                }
451                ctx.property_ty(parent_reference)
452            }
453            PropertyReference::Global { global_index, property_index } => {
454                &self.public_component.globals[*global_index].properties[*property_index].ty
455            }
456        }
457    }
458
459    fn arg_type(&self, index: usize) -> &Type {
460        &self.argument_types[index]
461    }
462}