diff --git a/api/sixtyfps-rs/sixtyfps-rs-macro/lib.rs b/api/sixtyfps-rs/sixtyfps-rs-macro/lib.rs index 54b7d234e..66fe788aa 100644 --- a/api/sixtyfps-rs/sixtyfps-rs-macro/lib.rs +++ b/api/sixtyfps-rs/sixtyfps-rs-macro/lib.rs @@ -34,6 +34,8 @@ fn fill_token_vec(stream: TokenStream, vec: &mut Vec) { ';' => SyntaxKind::Semicolon, '!' => SyntaxKind::Bang, '.' => SyntaxKind::Dot, + '<' => SyntaxKind::LAngle, + '>' => SyntaxKind::RAngle, _ => SyntaxKind::Error, }; vec.push(parser::Token { diff --git a/sixtyfps_compiler/object_tree.rs b/sixtyfps_compiler/object_tree.rs index fb58690f5..f57b0f59e 100644 --- a/sixtyfps_compiler/object_tree.rs +++ b/sixtyfps_compiler/object_tree.rs @@ -74,6 +74,7 @@ pub struct Element { /// This should probably be in the Component instead pub signals_declaration: Vec, + pub property_declarations: Vec, } impl Element { @@ -169,6 +170,10 @@ impl Element { r.signals_declaration.push(name); } + for prop_decl in node.children().filter(|n| n.kind() == SyntaxKind::PropertyDeclaration) { + r.property_declarations.push(PropertyDeclaration::from_node(prop_decl)); + } + for se in node.children() { if se.kind() == SyntaxKind::SubElement { let id = se.child_text(SyntaxKind::Identifier).unwrap_or_default(); @@ -185,6 +190,27 @@ impl Element { } } +#[derive(Default, Debug, Clone)] +pub struct PropertyDeclaration { + property_type: QualifiedTypeName, + name: String, +} + +impl PropertyDeclaration { + pub fn from_node(node: SyntaxNode) -> Self { + debug_assert_eq!(node.kind(), SyntaxKind::PropertyDeclaration); + Self { + property_type: QualifiedTypeName::from_node( + node.children() + .filter(|n| n.kind() == SyntaxKind::QualifiedTypeName) + .nth(0) + .unwrap(), + ), + name: node.child_text(SyntaxKind::Identifier).unwrap(), + } + } +} + #[derive(Default, Debug, Clone)] pub struct QualifiedTypeName { members: Vec, diff --git a/sixtyfps_compiler/parser.rs b/sixtyfps_compiler/parser.rs index c40a0fc6a..34234582c 100644 --- a/sixtyfps_compiler/parser.rs +++ b/sixtyfps_compiler/parser.rs @@ -45,6 +45,7 @@ macro_rules! declare_token_kind { RepeatedElement, SignalDeclaration, SignalConnection, + PropertyDeclaration, Binding, CodeStatement, CodeBlock, @@ -74,6 +75,8 @@ declare_token_kind! { RBrace -> r"\}", LParent -> r"\(", RParent -> r"\)", + LAngle -> r"<", + RAngle -> r">", ColonEqual -> ":=", FatArrow -> r"=>", Equal -> r"=", diff --git a/sixtyfps_compiler/parser/document.rs b/sixtyfps_compiler/parser/document.rs index 326f77e9a..1fe1827c8 100644 --- a/sixtyfps_compiler/parser/document.rs +++ b/sixtyfps_compiler/parser/document.rs @@ -66,6 +66,7 @@ pub fn parse_element(p: &mut impl Parser) -> bool { /// for xx in model: Sub {} /// clicked => {} /// signal foobar; +/// property width; /// ``` fn parse_element_content(p: &mut impl Parser) { loop { @@ -82,6 +83,9 @@ fn parse_element_content(p: &mut impl Parser) { SyntaxKind::Identifier if p.peek().as_str() == "signal" => { parse_signal_declaration(&mut *p); } + SyntaxKind::LAngle if p.peek().as_str() == "property" => { + parse_property_declaration(&mut *p); + } _ => { p.consume(); p.error("FIXME"); @@ -219,3 +223,18 @@ fn parse_signal_declaration(p: &mut impl Parser) { p.expect(SyntaxKind::Identifier); p.expect(SyntaxKind::Semicolon); } + +#[cfg_attr(test, parser_test)] +/// ```test +/// property foobar; +/// ``` +fn parse_property_declaration(p: &mut impl Parser) { + debug_assert_eq!(p.peek().as_str(), "property"); + let mut p = p.start_node(SyntaxKind::PropertyDeclaration); + p.consume(); // property + p.expect(SyntaxKind::LAngle); + parse_qualified_type_name(&mut *p); + p.expect(SyntaxKind::RAngle); + p.expect(SyntaxKind::Identifier); + p.expect(SyntaxKind::Semicolon); +} diff --git a/sixtyfps_compiler/tests/basic/property_declaration.60 b/sixtyfps_compiler/tests/basic/property_declaration.60 new file mode 100644 index 000000000..060a7de4c --- /dev/null +++ b/sixtyfps_compiler/tests/basic/property_declaration.60 @@ -0,0 +1,4 @@ + +Test := Rectangle { + property foo; +} \ No newline at end of file