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
This commit is contained in:
Olivier Goffart 2020-07-20 15:04:06 +02:00
parent 0dff3f5f78
commit c0fab1c3e9
5 changed files with 238 additions and 47 deletions

View file

@ -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<Self> {
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 <xxx> in <model>:
pub repeated: Option<RepeatedElementInfo>,
pub states: Vec<State>,
pub transitions: Vec<Transition>,
/// The AST node, if available
pub node: Option<syntax_nodes::Element>,
}
@ -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<RefCell<Element>>,
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<ElementRc> {
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<State>(
recurse_elem(sub, &state, vis);
}
}
#[derive(Debug, Clone)]
pub struct State {
pub id: String,
pub condition: Option<Expression>,
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)>,
}

View file

@ -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<usize, ElementRc>,
root_component: &Rc<Component>,
) {
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<usize, ElementRc>) {
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<usize, ElementRc>,
root_component: &Rc<Component>,
) -> Expression {
fn fold_binding(val: &Expression, mapping: &HashMap<usize, ElementRc>) -> 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<usize, Rc<RefCell<Element>>>,
root_component: &Rc<Component>,
) -> 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(),
}
}

View file

@ -24,6 +24,8 @@ pub fn create_repeater_components(component: &Rc<Component>) {
repeated: None,
node: elem.node.clone(),
enclosing_component: Default::default(),
states: Default::default(),
transitions: Default::default(),
})),
parent_element,
..Component::default()

View file

@ -1,5 +1,4 @@
SuperSimple := Rectangle {
animate x {

View file

@ -0,0 +1,40 @@
TestCase := Rectangle {
property<bool> checked;
property <int32> 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 {}
}