From c0fab1c3e94f56b7d8f59fa17dbce4c7f236421c Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 20 Jul 2020 15:04:06 +0200 Subject: [PATCH] More progress in states and transition parsing Fill the object_tree with states, and part of the transition Also make sure to duplicate animations properly in inlining --- sixtyfps_compiler/object_tree.rs | 153 ++++++++++++++++-- sixtyfps_compiler/passes/inlining.rs | 89 ++++++---- .../passes/repeater_component.rs | 2 + sixtyfps_compiler/tests/basic/animate.60 | 1 - .../tests/basic/states_transitions.60 | 40 +++++ 5 files changed, 238 insertions(+), 47 deletions(-) create mode 100644 sixtyfps_compiler/tests/basic/states_transitions.60 diff --git a/sixtyfps_compiler/object_tree.rs b/sixtyfps_compiler/object_tree.rs index 424485828..0c91824c0 100644 --- a/sixtyfps_compiler/object_tree.rs +++ b/sixtyfps_compiler/object_tree.rs @@ -3,7 +3,7 @@ */ use crate::diagnostics::FileDiagnostics; -use crate::expression_tree::Expression; +use crate::expression_tree::{Expression, NamedReference}; use crate::parser::{syntax_nodes, Spanned, SyntaxKind, SyntaxNodeEx}; use crate::typeregister::{Type, TypeRegister}; use std::cell::RefCell; @@ -80,13 +80,13 @@ impl Component { ) -> Rc { let c = Rc::new(Component { id: node.child_text(SyntaxKind::Identifier).unwrap_or_default(), - root_element: Rc::new(RefCell::new(Element::from_node( + root_element: Element::from_node( node.Element(), "root".into(), Type::Invalid, diag, tr, - ))), + ), ..Default::default() }); let weak = Rc::downgrade(&c); @@ -128,6 +128,9 @@ pub struct Element { /// Tis element is part of a `for in : pub repeated: Option, + pub states: Vec, + pub transitions: Vec, + /// The AST node, if available pub node: Option, } @@ -153,7 +156,7 @@ impl Element { parent_type: Type, diag: &mut FileDiagnostics, tr: &TypeRegister, - ) -> Self { + ) -> ElementRc { let base = QualifiedTypeName::from_node(node.QualifiedName()); let mut r = Element { id, @@ -161,7 +164,7 @@ impl Element { Ok(ty) => ty, Err(err) => { diag.push_error(err, node.QualifiedName().span()); - return Element::default(); + return ElementRc::default(); } }, node: Some(node.clone()), @@ -290,32 +293,80 @@ impl Element { if se.kind() == SyntaxKind::SubElement { let id = se.child_text(SyntaxKind::Identifier).unwrap_or_default(); if let Some(element_node) = se.child_node(SyntaxKind::Element) { - r.children.push(Rc::new(RefCell::new(Element::from_node( + r.children.push(Element::from_node( element_node.into(), id, r.base_type.clone(), diag, tr, - )))); + )); } else { assert!(diag.has_error()); } } else if se.kind() == SyntaxKind::RepeatedElement { - r.children.push(Rc::new(RefCell::new(Element::from_repeated_node( + r.children.push(Element::from_repeated_node( se.into(), r.base_type.clone(), diag, tr, - )))); + )); } else if se.kind() == SyntaxKind::ConditionalElement { - r.children.push(Rc::new(RefCell::new(Element::from_conditional_node( + r.children.push(Element::from_conditional_node( se.into(), r.base_type.clone(), diag, tr, - )))); + )); } } + + let r = ElementRc::new(RefCell::new(r)); + + for state in node.States().flat_map(|s| s.State()) { + let s = State { + id: state + .DeclaredIdentifier() + .child_text(SyntaxKind::Identifier) + .unwrap_or_default(), + condition: state.Expression().map(|e| Expression::Uncompiled(e.into())), + property_changes: state + .StatePropertyChange() + .map(|s| { + let ne = lookup_property_from_qualified_name(s.QualifiedName(), &r, diag); + (ne, Expression::Uncompiled(s.BindingExpression().into())) + }) + .collect(), + }; + r.borrow_mut().states.push(s); + } + + for trs in node.Transitions().flat_map(|s| s.Transition()) { + let trans = Transition { + is_out: trs.child_text(SyntaxKind::Identifier).unwrap_or_default() == "out", + state_id: trs + .DeclaredIdentifier() + .child_text(SyntaxKind::Identifier) + .unwrap_or_default(), + property_animations: trs + .PropertyAnimation() + .map(|pa| { + // TODO: do that properly + ( + NamedReference { + element: Rc::downgrade(&r), + name: pa + .DeclaredIdentifier() + .child_text(SyntaxKind::Identifier) + .unwrap_or_default(), + }, + Default::default(), + ) + }) + .collect(), + }; + r.borrow_mut().transitions.push(trans); + } + r } @@ -324,7 +375,7 @@ impl Element { parent_type: Type, diag: &mut FileDiagnostics, tr: &TypeRegister, - ) -> Self { + ) -> ElementRc { let rei = RepeatedElementInfo { model: Expression::Uncompiled(node.Expression().into()), model_data_id: node @@ -337,8 +388,8 @@ impl Element { .unwrap_or_default(), is_conditional_element: false, }; - let mut e = Element::from_node(node.Element(), String::new(), parent_type, diag, tr); - e.repeated = Some(rei); + let e = Element::from_node(node.Element(), String::new(), parent_type, diag, tr); + e.borrow_mut().repeated = Some(rei); e } @@ -347,15 +398,15 @@ impl Element { parent_type: Type, diag: &mut FileDiagnostics, tr: &TypeRegister, - ) -> Self { + ) -> ElementRc { let rei = RepeatedElementInfo { model: Expression::Uncompiled(node.Expression().into()), model_data_id: String::new(), index_id: String::new(), is_conditional_element: true, }; - let mut e = Element::from_node(node.Element(), String::new(), parent_type, diag, tr); - e.repeated = Some(rei); + let e = Element::from_node(node.Element(), String::new(), parent_type, diag, tr); + e.borrow_mut().repeated = Some(rei); e } @@ -432,6 +483,59 @@ impl std::fmt::Display for QualifiedTypeName { } } +/// Return a NamedReference, if the reference is invalid, there will be a diagnostic +fn lookup_property_from_qualified_name( + node: syntax_nodes::QualifiedName, + r: &Rc>, + diag: &mut FileDiagnostics, +) -> NamedReference { + let qualname = QualifiedTypeName::from_node(node.clone()); + match qualname.members.as_slice() { + [prop_name] => { + if !r.borrow().lookup_property(prop_name.as_ref()).is_property_type() { + diag.push_error(format!("'{}' is not a valid property", qualname), node.span()); + } + NamedReference { element: Rc::downgrade(&r), name: String::default() } + } + [elem_id, prop_name] => { + let element = if let Some(element) = find_element_by_id(&r, elem_id.as_ref()) { + if !element.borrow().lookup_property(prop_name.as_ref()).is_property_type() { + diag.push_error( + format!("'{}' not found in '{}'", prop_name, elem_id), + node.span(), + ); + } + Rc::downgrade(&element) + } else { + diag.push_error(format!("'{}' is not a valid element id", elem_id), node.span()); + Weak::new() + }; + NamedReference { element, name: prop_name.clone() } + } + _ => { + diag.push_error(format!("'{}' is not a valid property", qualname), node.span()); + NamedReference { element: Default::default(), name: String::default() } + } + } +} + +/// FIXME: this is duplicated the resolving pass. Also, we should use a hash table +fn find_element_by_id(e: &ElementRc, name: &str) -> Option { + if e.borrow().id == name { + return Some(e.clone()); + } + for x in &e.borrow().children { + if x.borrow().repeated.is_some() { + continue; + } + if let Some(x) = find_element_by_id(x, name) { + return Some(x); + } + } + + None +} + /// Call the visitor for each children of the element recursively, starting with the element itself /// /// The state returned by the visitor is passed to the children @@ -445,3 +549,18 @@ pub fn recurse_elem( recurse_elem(sub, &state, vis); } } + +#[derive(Debug, Clone)] +pub struct State { + pub id: String, + pub condition: Option, + pub property_changes: Vec<(NamedReference, Expression)>, +} + +#[derive(Debug)] +pub struct Transition { + /// false for 'to', true for 'out' + pub is_out: bool, + pub state_id: String, + pub property_animations: Vec<(NamedReference, ElementRc)>, +} diff --git a/sixtyfps_compiler/passes/inlining.rs b/sixtyfps_compiler/passes/inlining.rs index 0fe6383fc..cdd8307e7 100644 --- a/sixtyfps_compiler/passes/inlining.rs +++ b/sixtyfps_compiler/passes/inlining.rs @@ -80,20 +80,35 @@ fn inline_element( .borrow() .bindings .iter() - .map(|(k, val)| (k.clone(), fold_binding(val, &mapping, root_component))), + .map(|(k, val)| (k.clone(), fold_binding(val, &mapping))), ); //core::mem::drop(elem_mut); + // Now fixup all binding and reference for (key, e) in &mapping { if *key == element_key(&inlined_component.root_element) { continue; // the root has been processed } for (_, expr) in &mut e.borrow_mut().bindings { - fixup_binding(expr, &mapping, root_component); + fixup_binding(expr, &mapping); } if let Some(ref mut r) = &mut e.borrow_mut().repeated { - fixup_binding(&mut r.model, &mapping, root_component); + fixup_binding(&mut r.model, &mapping); + } + for s in &mut e.borrow_mut().states { + if let Some(cond) = s.condition.as_mut() { + fixup_binding(cond, &mapping) + } + for (r, e) in &mut s.property_changes { + fixup_reference(r, &mapping); + fixup_binding(e, &mapping); + } + } + for t in &mut e.borrow_mut().transitions { + for (r, _) in &mut t.property_animations { + fixup_reference(r, &mapping) + } } } } @@ -109,8 +124,12 @@ fn duplicate_element_with_mapping( base_type: elem.base_type.clone(), id: elem.id.clone(), property_declarations: elem.property_declarations.clone(), - property_animations: elem.property_animations.clone(), - // We will do the mapping of the binding later + property_animations: elem + .property_animations + .iter() + .map(|(k, v)| (k.clone(), duplicate_element_with_mapping(v, mapping, root_component))) + .collect(), + // We will do the fixup of the bindings later bindings: elem.bindings.clone(), children: elem .children @@ -120,42 +139,54 @@ fn duplicate_element_with_mapping( repeated: elem.repeated.clone(), node: elem.node.clone(), enclosing_component: Rc::downgrade(root_component), + states: elem.states.clone(), + transitions: elem + .transitions + .iter() + .map(|t| duplicate_transition(t, mapping, root_component)) + .collect(), })); mapping.insert(element_key(element), new.clone()); new } -fn fixup_binding( - val: &mut Expression, +fn fixup_reference( + NamedReference { element, .. }: &mut NamedReference, mapping: &HashMap, - root_component: &Rc, ) { - val.visit_mut(|sub| fixup_binding(sub, mapping, root_component)); + *element = + element.upgrade().and_then(|e| mapping.get(&element_key(&e))).map(Rc::downgrade).unwrap(); +} + +fn fixup_binding(val: &mut Expression, mapping: &HashMap) { + val.visit_mut(|sub| fixup_binding(sub, mapping)); match val { - Expression::PropertyReference(NamedReference { element, .. }) => { - *element = element - .upgrade() - .and_then(|e| mapping.get(&element_key(&e))) - .map(Rc::downgrade) - .unwrap(); - } - Expression::SignalReference(NamedReference { element, .. }) => { - *element = element - .upgrade() - .and_then(|e| mapping.get(&element_key(&e))) - .map(Rc::downgrade) - .unwrap(); - } + Expression::PropertyReference(r) => fixup_reference(r, mapping), + Expression::SignalReference(r) => fixup_reference(r, mapping), _ => {} } } -fn fold_binding( - val: &Expression, - mapping: &HashMap, - root_component: &Rc, -) -> Expression { +fn fold_binding(val: &Expression, mapping: &HashMap) -> Expression { let mut new_val = val.clone(); - fixup_binding(&mut new_val, mapping, root_component); + fixup_binding(&mut new_val, mapping); new_val } + +fn duplicate_transition( + t: &Transition, + mapping: &mut HashMap>>, + root_component: &Rc, +) -> Transition { + Transition { + is_out: t.is_out, + state_id: t.state_id.clone(), + property_animations: t + .property_animations + .iter() + .map(|(r, anim)| { + (r.clone(), duplicate_element_with_mapping(anim, mapping, root_component)) + }) + .collect(), + } +} diff --git a/sixtyfps_compiler/passes/repeater_component.rs b/sixtyfps_compiler/passes/repeater_component.rs index 9be024aff..e6505d5c1 100644 --- a/sixtyfps_compiler/passes/repeater_component.rs +++ b/sixtyfps_compiler/passes/repeater_component.rs @@ -24,6 +24,8 @@ pub fn create_repeater_components(component: &Rc) { repeated: None, node: elem.node.clone(), enclosing_component: Default::default(), + states: Default::default(), + transitions: Default::default(), })), parent_element, ..Component::default() diff --git a/sixtyfps_compiler/tests/basic/animate.60 b/sixtyfps_compiler/tests/basic/animate.60 index cc48165c5..4c3a222dd 100644 --- a/sixtyfps_compiler/tests/basic/animate.60 +++ b/sixtyfps_compiler/tests/basic/animate.60 @@ -1,5 +1,4 @@ - SuperSimple := Rectangle { animate x { diff --git a/sixtyfps_compiler/tests/basic/states_transitions.60 b/sixtyfps_compiler/tests/basic/states_transitions.60 new file mode 100644 index 000000000..91e32e734 --- /dev/null +++ b/sixtyfps_compiler/tests/basic/states_transitions.60 @@ -0,0 +1,40 @@ +TestCase := Rectangle { + property checked; + property border; + states [ + checked when checked: { + color: blue; // same as root.color + text.color: red; + border: 42; + } + pressed when touch.pressed: { + color: green; + border: 88; + text.foo.bar: 0; +/// ^error{'text.foo.bar' is not a valid property} + colour: yellow; +/// ^error{'colour' is not a valid property} + fox.color: yellow; +/// ^error{'fox' is not a valid element id} + text.fox: yellow; +/// ^error{'fox' not found in 'text'} + } + ] + + transitions [ + to pressed: { + //animate * { duration: 88ms } + animate color { duration: 88ms; } + } + out pressed: { + //animate color, foo.x { duration: 300ms; } + //pause: 20ms; + animate border { duration: 120ms; } + } + ] + + text := Text {} + touch := TouchArea {} + +} +