/*! This module contains the intermediate representation of the code in the form of an object tree */ use crate::diagnostics::FileDiagnostics; use crate::expression_tree::Expression; use crate::parser::{syntax_nodes, Spanned, SyntaxKind, SyntaxNodeEx}; use crate::typeregister::{Type, TypeRegister}; use std::cell::RefCell; use std::collections::HashMap; use std::rc::{Rc, Weak}; /// The full document (a complete file) #[derive(Default, Debug)] pub struct Document { // node: SyntaxNode, pub inner_components: Vec>, pub root_component: Rc, pub local_registry: TypeRegister, } impl Document { pub fn from_node( node: syntax_nodes::Document, diag: &mut FileDiagnostics, parent_registry: &Rc>, ) -> Self { debug_assert_eq!(node.kind(), SyntaxKind::Document); let mut local_registry = TypeRegister::new(parent_registry); let inner_components = node .Component() .map(|n| { let compo = Component::from_node(n, diag, &local_registry); local_registry.add(compo.clone()); compo }) .collect::>(); Document { // FIXME: one should use the `component` hint instead of always returning the last root_component: inner_components.last().cloned().unwrap_or_default(), inner_components, local_registry, } } } /// A component is a type in the language which can be instantiated, /// Or is materialized for repeated expression. #[derive(Default, Debug)] pub struct Component { // node: SyntaxNode, pub id: String, pub root_element: ElementRc, /// The parent element within the parent component if this component represents a repeated element pub parent_element: Weak>, /// List of elements that are not attached to the root anymore because they have been /// optimized away, but their properties may still be in use pub optimized_elements: RefCell>, /// Map of resources to embed in the generated binary, indexed by their absolute path on /// disk on the build system and valued by a unique integer id, that can be used by the /// generator for symbol generation. pub embedded_file_resources: RefCell>, /// LayoutConstraints pub layout_constraints: RefCell, } impl Component { pub fn from_node( node: syntax_nodes::Component, diag: &mut FileDiagnostics, tr: &TypeRegister, ) -> Rc { let c = Rc::new(Component { id: node.child_text(SyntaxKind::Identifier).unwrap_or_default(), root_element: Rc::new(RefCell::new(Element::from_node( node.Element(), "root".into(), Type::Invalid, diag, tr, ))), ..Default::default() }); let weak = Rc::downgrade(&c); recurse_elem(&c.root_element, &(), &mut |e, _| { e.borrow_mut().enclosing_component = weak.clone() }); c } } #[derive(Clone, Debug, Default)] pub struct PropertyDeclaration { pub property_type: Type, pub type_location: crate::diagnostics::Span, pub expose_in_public_api: bool, } /// An Element is an instentation of a Component #[derive(Default, Debug)] pub struct Element { /// The id as named in the original .60 file. /// /// Note that it can only be used for lookup before inlining. /// After inlining there can be duplicated id in the component. /// The id are then re-assigned unique id in the assign_id pass pub id: String, //pub base: QualifiedTypeName, pub base_type: crate::typeregister::Type, /// Currently contains also the signals. FIXME: should that be changed? pub bindings: HashMap, pub children: Vec, /// The component which contains this element. pub enclosing_component: Weak, pub property_declarations: HashMap, pub property_animations: HashMap, /// Tis element is part of a `for in : pub repeated: Option, /// The AST node, if available pub node: Option, } #[derive(Debug, Clone)] /// If the parent element is a repeated element, this has information about the models pub struct RepeatedElementInfo { pub model: Expression, pub model_data_id: String, pub index_id: String, /// A conditional element is just a for whose model is a bolean expression /// /// When this is true, the model is of type bolean instead of Model pub is_conditional_element: bool, } pub type ElementRc = Rc>; impl Element { pub fn from_node( node: syntax_nodes::Element, id: String, parent_type: Type, diag: &mut FileDiagnostics, tr: &TypeRegister, ) -> Self { let base = QualifiedTypeName::from_node(node.QualifiedName()); let mut r = Element { id, base_type: match parent_type.lookup_type_for_child_element(&base.to_string(), tr) { Ok(ty) => ty, Err(err) => { diag.push_error(err, node.QualifiedName().span()); return Element::default(); } }, node: Some(node.clone()), ..Default::default() }; assert!(r.base_type.is_object_type()); for prop_decl in node.PropertyDeclaration() { let qualified_type_node = prop_decl.QualifiedName(); let type_span = qualified_type_node.span(); let qualified_type = QualifiedTypeName::from_node(qualified_type_node); let prop_type = tr.lookup_qualified(&qualified_type.members); match prop_type { Type::Invalid => { diag.push_error( format!("Unknown property type '{}'", qualified_type.to_string()), type_span.clone(), ); } _ => (), }; let prop_name_token = prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(); let prop_name = prop_name_token.text().to_string(); if !matches!(r.lookup_property(&prop_name), Type::Invalid) { diag.push_error( format!("Cannot override property '{}'", prop_name), prop_name_token.span(), ) } r.property_declarations.insert( prop_name.clone(), PropertyDeclaration { property_type: prop_type, type_location: type_span, ..Default::default() }, ); if let Some(csn) = prop_decl.BindingExpression() { if r.bindings.insert(prop_name, Expression::Uncompiled(csn.into())).is_some() { diag.push_error("Duplicated property binding".into(), prop_name_token.span()); } } } r.parse_bindings(&base, node.Binding(), diag); for sig_decl in node.SignalDeclaration() { let name_token = sig_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(); let name = name_token.text().to_string(); r.property_declarations.insert( name, PropertyDeclaration { property_type: Type::Signal, type_location: sig_decl.span(), ..Default::default() }, ); } for con_node in node.SignalConnection() { let name_token = match con_node.child_token(SyntaxKind::Identifier) { Some(x) => x, None => continue, }; let name = name_token.text().to_string(); let prop_type = r.lookup_property(&name); if !matches!(prop_type, Type::Signal) { diag.push_error( format!("'{}' is not a signal in {}", name, base), crate::diagnostics::Span::new(name_token.text_range().start().into()), ); } if r.bindings .insert(name, Expression::Uncompiled(con_node.CodeBlock().into())) .is_some() { diag.push_error( "Duplicated signal".into(), crate::diagnostics::Span::new(name_token.text_range().start().into()), ); } } for anim in node.PropertyAnimation() { let prop_name_token = anim.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(); let prop_name = prop_name_token.text().to_string(); let prop_type = r.lookup_property(&prop_name); let anim_type = tr.property_animation_type_for_property(prop_type); if !matches!(anim_type, Type::Builtin(..)) { diag.push_error( format!("'{}' is not an animatable property", prop_name), prop_name_token.span(), ) } else { let base = QualifiedTypeName { members: vec![anim_type.as_builtin().class_name.clone()] }; let mut anim_element = Element { id: "".into(), base_type: anim_type, node: None, ..Default::default() }; anim_element.parse_bindings(&base, anim.Binding(), diag); let anim_element = Rc::new(RefCell::new(anim_element)); if r.property_animations.insert(prop_name, anim_element.clone()).is_some() { diag.push_error( "Duplicated animation".into(), crate::diagnostics::Span::new(prop_name_token.text_range().start().into()), ) } } } for se in node.children() { 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( 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( 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( se.into(), r.base_type.clone(), diag, tr, )))); } } r } fn from_repeated_node( node: syntax_nodes::RepeatedElement, parent_type: Type, diag: &mut FileDiagnostics, tr: &TypeRegister, ) -> Self { let rei = RepeatedElementInfo { model: Expression::Uncompiled(node.Expression().into()), model_data_id: node .DeclaredIdentifier() .and_then(|n| n.child_text(SyntaxKind::Identifier)) .unwrap_or_default(), index_id: node .RepeatedIndex() .and_then(|r| r.child_text(SyntaxKind::Identifier)) .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); e } fn from_conditional_node( node: syntax_nodes::ConditionalElement, parent_type: Type, diag: &mut FileDiagnostics, tr: &TypeRegister, ) -> Self { 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); e } pub fn lookup_property(&self, name: &str) -> Type { self.property_declarations .get(name) .cloned() .map(|decl| decl.property_type) .unwrap_or_else(|| self.base_type.lookup_property(name)) } /// Return the Span of this element in the AST for error reporting pub fn span(&self) -> crate::diagnostics::Span { self.node.as_ref().map(|n| n.span()).unwrap_or_default() } fn parse_bindings( &mut self, base: &QualifiedTypeName, bindings: impl Iterator, diag: &mut FileDiagnostics, ) { for b in bindings { let name_token = match b.child_token(SyntaxKind::Identifier) { Some(x) => x, None => continue, }; let name = name_token.text().to_string(); let prop_type = self.lookup_property(&name); if !prop_type.is_property_type() { diag.push_error( match prop_type { Type::Invalid => format!("Unknown property {} in {}", name, base), Type::Signal => format!("'{}' is a signal. Use `=>` to connect", name), _ => format!("Cannot assing to {} in {}", name, base), }, crate::diagnostics::Span::new(name_token.text_range().start().into()), ); } if self .bindings .insert(name, Expression::Uncompiled(b.BindingExpression().into())) .is_some() { diag.push_error( "Duplicated property binding".into(), crate::diagnostics::Span::new(name_token.text_range().start().into()), ); } } } } #[derive(Default, Debug, Clone)] pub struct QualifiedTypeName { members: Vec, } impl QualifiedTypeName { pub fn from_node(node: syntax_nodes::QualifiedName) -> Self { debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName); let members = node .children_with_tokens() .filter(|n| n.kind() == SyntaxKind::Identifier) .filter_map(|x| x.as_token().map(|x| x.text().to_string())) .collect(); Self { members } } } impl std::fmt::Display for QualifiedTypeName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.members.join(".")) } } /// 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 pub fn recurse_elem( elem: &ElementRc, state: &State, vis: &mut impl FnMut(&ElementRc, &State) -> State, ) { let state = vis(elem, state); for sub in &elem.borrow().children { recurse_elem(sub, &state, vis); } }