1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use super::element::{parse_element, parse_element_content};
5use super::prelude::*;
6use super::r#type::{parse_enum_declaration, parse_rustattr, parse_struct_declaration};
7
8#[cfg_attr(test, parser_test)]
9/// ```test,Document
10/// component Type { }
11/// Type := Base { SubElement { } }
12/// Comp := Base {} Type := Base {}
13/// component Q {} Type := Base {} export { Type }
14/// import { Base } from "somewhere"; Type := Base {}
15/// struct Foo { foo: foo }
16/// enum Foo { hello }
17/// @rust-attr(...) struct X {}
18/// /* empty */
19/// ```
20pub fn parse_document(p: &mut impl Parser) -> bool {
21 let mut p = p.start_node(SyntaxKind::Document);
22
23 loop {
24 if p.test(SyntaxKind::Eof) {
25 return true;
26 }
27
28 if p.peek().kind() == SyntaxKind::Semicolon {
29 p.error("Extra semicolon. Remove this semicolon");
30 p.consume();
31 continue;
32 }
33
34 match p.peek().as_str() {
35 "export" => {
36 if !parse_export(&mut *p, None) {
37 break;
38 }
39 }
40 "import" => {
41 if !parse_import_specifier(&mut *p) {
42 break;
43 }
44 }
45 "struct" => {
46 if !parse_struct_declaration(&mut *p, None) {
47 break;
48 }
49 }
50 "enum" => {
51 if !parse_enum_declaration(&mut *p, None) {
52 break;
53 }
54 }
55 "@" if p.nth(1).as_str() == "rust-attr" => {
56 let checkpoint = p.checkpoint();
57 if !parse_rustattr(&mut *p) {
58 break;
59 }
60 let is_export = p.nth(0).as_str() == "export";
61 let i = if is_export { 1 } else { 0 };
62 if !matches!(p.nth(i).as_str(), "enum" | "struct") {
63 p.error("Expected enum or struct after @rust-attr");
64 continue;
65 }
66 let r = if is_export {
67 parse_export(&mut *p, Some(checkpoint))
68 } else if p.nth(0).as_str() == "struct" {
69 parse_struct_declaration(&mut *p, Some(checkpoint))
70 } else if p.nth(0).as_str() == "enum" {
71 parse_enum_declaration(&mut *p, Some(checkpoint))
72 } else {
73 false
74 };
75 if !r {
76 break;
77 }
78 }
79 _ => {
80 if !parse_component(&mut *p) {
81 break;
82 }
83 }
84 }
85 }
86 // Always consume the whole document
87 while !p.test(SyntaxKind::Eof) {
88 p.consume()
89 }
90 false
91}
92
93#[cfg_attr(test, parser_test)]
94/// ```test,Component
95/// Type := Base { }
96/// Type := Base { prop: value; }
97/// Type := Base { SubElement { } }
98/// global Struct := { }
99/// global Struct { property<int> xx; }
100/// component C { property<int> xx; }
101/// component C inherits D { }
102/// ```
103pub fn parse_component(p: &mut impl Parser) -> bool {
104 let simple_component = p.nth(1).kind() == SyntaxKind::ColonEqual;
105 let is_global = !simple_component && p.peek().as_str() == "global";
106 let is_new_component = !simple_component && p.peek().as_str() == "component";
107 if !is_global && !simple_component && !is_new_component {
108 p.error(
109 "Parse error: expected a top-level item such as a component, a struct, or a global",
110 );
111 return false;
112 }
113 let mut p = p.start_node(SyntaxKind::Component);
114 if is_global || is_new_component {
115 p.consume();
116 }
117 if !p.start_node(SyntaxKind::DeclaredIdentifier).expect(SyntaxKind::Identifier) {
118 drop(p.start_node(SyntaxKind::Element));
119 return false;
120 }
121 if is_global {
122 if p.peek().kind() == SyntaxKind::ColonEqual {
123 p.warning("':=' to declare a global is deprecated. Remove the ':='");
124 p.consume();
125 }
126 } else if !is_new_component {
127 if p.peek().kind() == SyntaxKind::ColonEqual {
128 p.warning("':=' to declare a component is deprecated. The new syntax declare components with 'component MyComponent {'. Read the documentation for more info");
129 }
130 if !p.expect(SyntaxKind::ColonEqual) {
131 drop(p.start_node(SyntaxKind::Element));
132 return false;
133 }
134 } else if p.peek().as_str() == "inherits" {
135 p.consume();
136 } else if p.peek().kind() == SyntaxKind::LBrace {
137 let mut p = p.start_node(SyntaxKind::Element);
138 p.consume();
139 parse_element_content(&mut *p);
140 return p.expect(SyntaxKind::RBrace);
141 } else {
142 p.error("Expected '{' or keyword 'inherits'");
143 drop(p.start_node(SyntaxKind::Element));
144 return false;
145 }
146
147 if is_global && p.peek().kind() == SyntaxKind::LBrace {
148 let mut p = p.start_node(SyntaxKind::Element);
149 p.consume();
150 parse_element_content(&mut *p);
151 return p.expect(SyntaxKind::RBrace);
152 }
153
154 parse_element(&mut *p)
155}
156
157#[cfg_attr(test, parser_test)]
158/// ```test,QualifiedName
159/// Rectangle
160/// MyModule.Rectangle
161/// Deeply.Nested.MyModule.Rectangle
162/// ```
163pub fn parse_qualified_name(p: &mut impl Parser) -> bool {
164 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::QualifiedName);
165 if !p.expect(kind:SyntaxKind::Identifier) {
166 return false;
167 }
168
169 loop {
170 if p.nth(0).kind() != SyntaxKind::Dot {
171 break;
172 }
173 p.consume();
174 p.expect(kind:SyntaxKind::Identifier);
175 }
176
177 true
178}
179
180#[cfg_attr(test, parser_test)]
181/// ```test,ExportsList
182/// export { Type }
183/// export { Type, AnotherType, }
184/// export { Type as Foo, AnotherType }
185/// export Foo := Item { }
186/// export struct Foo := { foo: bar }
187/// export enum Foo { bar }
188/// export * from "foo";
189/// export { Abc } from "foo";
190/// export { Abc, Efg } from "foo";
191/// ```
192fn parse_export<P: Parser>(p: &mut P, checkpoint: Option<P::Checkpoint>) -> bool {
193 debug_assert_eq!(p.peek().as_str(), "export");
194 let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::ExportsList);
195
196 p.expect(SyntaxKind::Identifier); // "export"
197 if p.test(SyntaxKind::LBrace) {
198 loop {
199 if p.test(SyntaxKind::RBrace) {
200 break;
201 }
202 parse_export_specifier(&mut *p);
203 match p.nth(0).kind() {
204 SyntaxKind::RBrace => {
205 p.consume();
206 break;
207 }
208 SyntaxKind::Eof => {
209 p.error("Expected comma");
210 return false;
211 }
212 SyntaxKind::Comma => {
213 p.consume();
214 }
215 _ => {
216 p.consume();
217 p.error("Expected comma");
218 return false;
219 }
220 }
221 }
222 if p.peek().as_str() == "from" {
223 let mut p = p.start_node(SyntaxKind::ExportModule);
224 p.consume(); // "from"
225 p.expect(SyntaxKind::StringLiteral);
226 p.expect(SyntaxKind::Semicolon);
227 }
228 true
229 } else if p.peek().as_str() == "struct" {
230 parse_struct_declaration(&mut *p, checkpoint)
231 } else if p.peek().as_str() == "enum" {
232 parse_enum_declaration(&mut *p, checkpoint)
233 } else if p.peek().kind == SyntaxKind::Star {
234 let mut p = p.start_node(SyntaxKind::ExportModule);
235 p.consume(); // *
236 if p.peek().as_str() != "from" {
237 p.error("Expected from keyword for export statement");
238 return false;
239 }
240 p.consume();
241 let peek = p.peek();
242 if peek.kind != SyntaxKind::StringLiteral
243 || !peek.as_str().starts_with('"')
244 || !peek.as_str().ends_with('"')
245 {
246 p.error("Expected plain string literal");
247 return false;
248 }
249 p.consume();
250 p.expect(SyntaxKind::Semicolon)
251 } else {
252 parse_component(&mut *p)
253 }
254}
255
256#[cfg_attr(test, parser_test)]
257/// ```test,ExportSpecifier
258/// Type
259/// Type as Something
260/// ```
261fn parse_export_specifier(p: &mut impl Parser) -> bool {
262 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::ExportSpecifier);
263 {
264 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::ExportIdentifier);
265 if !p.expect(kind:SyntaxKind::Identifier) {
266 return false;
267 }
268 }
269 if p.peek().as_str() == "as" {
270 p.consume();
271 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::ExportName);
272 if !p.expect(kind:SyntaxKind::Identifier) {
273 return false;
274 }
275 }
276
277 true
278}
279
280#[cfg_attr(test, parser_test)]
281/// ```test,ImportSpecifier
282/// import { Type1, Type2 } from "somewhere";
283/// import "something.ttf";
284/// ```
285fn parse_import_specifier(p: &mut impl Parser) -> bool {
286 debug_assert_eq!(p.peek().as_str(), "import");
287 let mut p = p.start_node(SyntaxKind::ImportSpecifier);
288 p.expect(SyntaxKind::Identifier); // "import"
289 if p.peek().kind != SyntaxKind::StringLiteral {
290 if !parse_import_identifier_list(&mut *p) {
291 return false;
292 }
293 if p.peek().as_str() != "from" {
294 p.error("Expected from keyword for import statement");
295 return false;
296 }
297 if !p.expect(SyntaxKind::Identifier) {
298 return false;
299 }
300 }
301 let peek = p.peek();
302 if peek.kind != SyntaxKind::StringLiteral
303 || !peek.as_str().starts_with('"')
304 || !peek.as_str().ends_with('"')
305 {
306 p.error("Expected plain string literal");
307 return false;
308 }
309 p.consume();
310 p.expect(SyntaxKind::Semicolon)
311}
312
313#[cfg_attr(test, parser_test)]
314/// ```test,ImportIdentifierList
315/// { Type1 }
316/// { Type2, }
317/// { Type3, Type4 }
318/// { Type5, Type6, }
319/// { Type as Alias1, Type as AnotherAlias1 }
320/// { Type as Alias2, Type as AnotherAlias2, }
321/// {}
322/// ```
323fn parse_import_identifier_list(p: &mut impl Parser) -> bool {
324 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::ImportIdentifierList);
325 if !p.expect(kind:SyntaxKind::LBrace) {
326 return false;
327 }
328 loop {
329 if p.test(kind:SyntaxKind::RBrace) {
330 return true;
331 }
332 parse_import_identifier(&mut *p);
333 if !p.test(kind:SyntaxKind::Comma) && p.nth(0).kind() != SyntaxKind::RBrace {
334 p.error("Expected comma or brace");
335 return false;
336 }
337 }
338}
339
340#[cfg_attr(test, parser_test)]
341/// ```test,ImportIdentifier
342/// Type
343/// Type as Alias1
344/// ```
345fn parse_import_identifier(p: &mut impl Parser) -> bool {
346 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::ImportIdentifier);
347 {
348 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::ExternalName);
349 if !p.expect(kind:SyntaxKind::Identifier) {
350 return false;
351 }
352 }
353 if p.nth(0).kind() == SyntaxKind::Identifier && p.peek().as_str() == "as" {
354 p.consume();
355 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::InternalName);
356 if !p.expect(kind:SyntaxKind::Identifier) {
357 return false;
358 }
359 }
360 true
361}
362