Skip to main content

i_slint_compiler/parser/
document.rs

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/// @rust-attr(...) @rust-attr(...) enum X {}
19/// /* empty */
20/// ```
21pub fn parse_document(p: &mut impl Parser) -> bool {
22    let mut p = p.start_node(SyntaxKind::Document);
23
24    loop {
25        if p.test(SyntaxKind::Eof) {
26            return true;
27        }
28
29        if p.peek().kind() == SyntaxKind::Semicolon {
30            p.error("Extra semicolon. Remove this semicolon");
31            p.consume();
32            continue;
33        }
34
35        match p.peek().as_str() {
36            "export" => {
37                if !parse_export(&mut *p, None) {
38                    break;
39                }
40            }
41            "import" => {
42                if !parse_import_specifier(&mut *p) {
43                    break;
44                }
45            }
46            "struct" => {
47                if !parse_struct_declaration(&mut *p, None) {
48                    break;
49                }
50            }
51            "enum" => {
52                if !parse_enum_declaration(&mut *p, None) {
53                    break;
54                }
55            }
56            "@" if p.nth(1).as_str() == "rust-attr" => {
57                let checkpoint = p.checkpoint();
58                if !parse_rustattr(&mut *p) {
59                    break;
60                }
61                while p.peek().as_str() == "@" && p.nth(1).as_str() == "rust-attr" {
62                    parse_rustattr(&mut *p);
63                }
64                let is_export = p.peek().as_str() == "export";
65                let i = if is_export { 1 } else { 0 };
66                if !matches!(p.nth(i).as_str(), "enum" | "struct") {
67                    p.error("Expected enum or struct after @rust-attr");
68                    continue;
69                }
70                let r = if is_export {
71                    parse_export(&mut *p, Some(checkpoint))
72                } else if p.peek().as_str() == "struct" {
73                    parse_struct_declaration(&mut *p, Some(checkpoint))
74                } else if p.peek().as_str() == "enum" {
75                    parse_enum_declaration(&mut *p, Some(checkpoint))
76                } else {
77                    false
78                };
79                if !r {
80                    break;
81                }
82            }
83            _ => {
84                if !parse_component(&mut *p) {
85                    break;
86                }
87            }
88        }
89    }
90    // Always consume the whole document
91    while !p.test(SyntaxKind::Eof) {
92        p.consume()
93    }
94    false
95}
96
97#[cfg_attr(test, parser_test)]
98/// ```test,Component
99/// Type := Base { }
100/// Type := Base { prop: value; }
101/// Type := Base { SubElement { } }
102/// global Struct := { }
103/// global Struct { property<int> xx; }
104/// component C { property<int> xx; }
105/// component C inherits D { }
106/// interface I { property<int> xx; }
107/// component E implements I { }
108/// component E implements I inherits D { }
109/// component F uses { I from A } { }
110/// component F uses { I from A } implements J { }
111/// component F uses { I from A } inherits B { }
112/// component F uses { I from A, J from B } { }
113/// component F uses { I from A, J from B } implements J { }
114/// component F uses { I from A, J from B } inherits C { }
115/// component F uses { I from A, J from B } implements J inherits D { }
116/// ```
117pub fn parse_component(p: &mut impl Parser) -> bool {
118    let simple_component = p.nth(1).kind() == SyntaxKind::ColonEqual;
119    let is_global = !simple_component && p.peek().as_str() == "global";
120    let is_interface = !simple_component && p.peek().as_str() == "interface";
121    let is_new_component = !simple_component && p.peek().as_str() == "component";
122    if !is_global && !simple_component && !is_new_component && !is_interface {
123        p.error(
124            "Parse error: expected a top-level item such as a component, a struct, or a global",
125        );
126        return false;
127    }
128    let mut p = p.start_node(SyntaxKind::Component);
129    if is_global || is_new_component || is_interface {
130        p.consume();
131    }
132    if !p.start_node(SyntaxKind::DeclaredIdentifier).expect(SyntaxKind::Identifier) {
133        drop(p.start_node(SyntaxKind::Element));
134        return false;
135    }
136    if p.peek().as_str() == "uses" {
137        if !is_new_component {
138            p.error("Only components can have 'uses' clauses");
139            drop(p.start_node(SyntaxKind::Element));
140            return false;
141        }
142
143        if !parse_uses_specifier(&mut *p) {
144            drop(p.start_node(SyntaxKind::Element));
145            return false;
146        }
147    }
148    if p.peek().as_str() == "implements" {
149        if is_global {
150            p.error("Globals cannot implement an interface");
151            drop(p.start_node(SyntaxKind::Element));
152            return false;
153        } else if is_interface {
154            p.error("Interfaces cannot implement another interface");
155            drop(p.start_node(SyntaxKind::Element));
156            return false;
157        }
158        if !parse_implements_specifier(&mut *p) {
159            drop(p.start_node(SyntaxKind::Element));
160            return false;
161        }
162    }
163    if is_global {
164        if p.peek().kind() == SyntaxKind::ColonEqual {
165            p.warning("':=' to declare a global is deprecated. Remove the ':='");
166            p.consume();
167        }
168    } else if is_interface {
169        if p.peek().kind() == SyntaxKind::ColonEqual {
170            p.error("':=' to declare an interface is not supported. Remove the ':='");
171            p.consume();
172        }
173    } else if !is_new_component {
174        if p.peek().kind() == SyntaxKind::ColonEqual {
175            p.warning("':=' to declare a component is deprecated. The new syntax declare components with 'component MyComponent {'. Read the documentation for more info");
176        }
177        if !p.expect(SyntaxKind::ColonEqual) {
178            drop(p.start_node(SyntaxKind::Element));
179            return false;
180        }
181    } else if p.peek().as_str() == "inherits" {
182        p.consume();
183    } else if p.peek().kind() == SyntaxKind::LBrace {
184        let mut p = p.start_node(SyntaxKind::Element);
185        p.consume();
186        parse_element_content(&mut *p);
187        return p.expect(SyntaxKind::RBrace);
188    } else {
189        p.error("Expected '{', keyword 'implements' or keyword 'inherits'");
190        drop(p.start_node(SyntaxKind::Element));
191        return false;
192    }
193
194    if (is_global || is_interface) && p.peek().kind() == SyntaxKind::LBrace {
195        let mut p = p.start_node(SyntaxKind::Element);
196        p.consume();
197        parse_element_content(&mut *p);
198        return p.expect(SyntaxKind::RBrace);
199    }
200
201    parse_element(&mut *p)
202}
203
204#[cfg_attr(test, parser_test)]
205/// ```test,QualifiedName
206/// Rectangle
207/// MyModule.Rectangle
208/// Deeply.Nested.MyModule.Rectangle
209/// ```
210pub fn parse_qualified_name(p: &mut impl Parser) -> bool {
211    let mut p = p.start_node(SyntaxKind::QualifiedName);
212    if !p.expect(SyntaxKind::Identifier) {
213        return false;
214    }
215
216    loop {
217        if p.nth(0).kind() != SyntaxKind::Dot {
218            break;
219        }
220        p.consume();
221        p.expect(SyntaxKind::Identifier);
222    }
223
224    true
225}
226
227#[cfg_attr(test, parser_test)]
228/// ```test,ExportsList
229/// export { Type }
230/// export { Type, AnotherType, }
231/// export { Type as Foo, AnotherType }
232/// export Foo := Item { }
233/// export struct Foo := { foo: bar }
234/// export enum Foo { bar }
235/// export * from "foo";
236/// export { Abc } from "foo";
237/// export { Abc, Efg } from "foo";
238/// ```
239fn parse_export<P: Parser>(p: &mut P, checkpoint: Option<P::Checkpoint>) -> bool {
240    debug_assert_eq!(p.peek().as_str(), "export");
241    let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::ExportsList);
242
243    p.expect(SyntaxKind::Identifier); // "export"
244    if p.test(SyntaxKind::LBrace) {
245        loop {
246            if p.test(SyntaxKind::RBrace) {
247                break;
248            }
249            parse_export_specifier(&mut *p);
250            match p.nth(0).kind() {
251                SyntaxKind::RBrace => {
252                    p.consume();
253                    break;
254                }
255                SyntaxKind::Eof => {
256                    p.error("Expected comma");
257                    return false;
258                }
259                SyntaxKind::Comma => {
260                    p.consume();
261                }
262                _ => {
263                    p.consume();
264                    p.error("Expected comma");
265                    return false;
266                }
267            }
268        }
269        if p.peek().as_str() == "from" {
270            let mut p = p.start_node(SyntaxKind::ExportModule);
271            p.consume(); // "from"
272            p.expect(SyntaxKind::StringLiteral);
273            p.expect(SyntaxKind::Semicolon);
274        }
275        true
276    } else if p.peek().as_str() == "struct" {
277        parse_struct_declaration(&mut *p, checkpoint)
278    } else if p.peek().as_str() == "enum" {
279        parse_enum_declaration(&mut *p, checkpoint)
280    } else if p.peek().kind == SyntaxKind::Star {
281        let mut p = p.start_node(SyntaxKind::ExportModule);
282        p.consume(); // *
283        if p.peek().as_str() != "from" {
284            p.error("Expected from keyword for export statement");
285            return false;
286        }
287        p.consume();
288        let peek = p.peek();
289        if peek.kind != SyntaxKind::StringLiteral
290            || !peek.as_str().starts_with('"')
291            || !peek.as_str().ends_with('"')
292        {
293            p.error("Expected plain string literal");
294            return false;
295        }
296        p.consume();
297        p.expect(SyntaxKind::Semicolon)
298    } else {
299        parse_component(&mut *p)
300    }
301}
302
303#[cfg_attr(test, parser_test)]
304/// ```test,ExportSpecifier
305/// Type
306/// Type as Something
307/// ```
308fn parse_export_specifier(p: &mut impl Parser) -> bool {
309    let mut p = p.start_node(SyntaxKind::ExportSpecifier);
310    {
311        let mut p = p.start_node(SyntaxKind::ExportIdentifier);
312        if !p.expect(SyntaxKind::Identifier) {
313            return false;
314        }
315    }
316    if p.peek().as_str() == "as" {
317        p.consume();
318        let mut p = p.start_node(SyntaxKind::ExportName);
319        if !p.expect(SyntaxKind::Identifier) {
320            return false;
321        }
322    }
323
324    true
325}
326
327#[cfg_attr(test, parser_test)]
328/// ```test,ImportSpecifier
329/// import { Type1, Type2 } from "somewhere";
330/// import "something.ttf";
331/// ```
332fn parse_import_specifier(p: &mut impl Parser) -> bool {
333    debug_assert_eq!(p.peek().as_str(), "import");
334    let mut p = p.start_node(SyntaxKind::ImportSpecifier);
335    p.expect(SyntaxKind::Identifier); // "import"
336    if p.peek().kind != SyntaxKind::StringLiteral {
337        if !parse_import_identifier_list(&mut *p) {
338            return false;
339        }
340        if p.peek().as_str() != "from" {
341            p.error("Expected from keyword for import statement");
342            return false;
343        }
344        if !p.expect(SyntaxKind::Identifier) {
345            return false;
346        }
347    }
348    let peek = p.peek();
349    if peek.kind != SyntaxKind::StringLiteral
350        || !peek.as_str().starts_with('"')
351        || !peek.as_str().ends_with('"')
352    {
353        p.error("Expected plain string literal");
354        return false;
355    }
356    p.consume();
357    p.expect(SyntaxKind::Semicolon)
358}
359
360#[cfg_attr(test, parser_test)]
361/// ```test,ImportIdentifierList
362/// { Type1 }
363/// { Type2, }
364/// { Type3, Type4 }
365/// { Type5, Type6, }
366/// { Type as Alias1, Type as AnotherAlias1 }
367/// { Type as Alias2, Type as AnotherAlias2, }
368/// {}
369/// ```
370fn parse_import_identifier_list(p: &mut impl Parser) -> bool {
371    let mut p = p.start_node(SyntaxKind::ImportIdentifierList);
372    if !p.expect(SyntaxKind::LBrace) {
373        return false;
374    }
375    loop {
376        if p.test(SyntaxKind::RBrace) {
377            return true;
378        }
379        parse_import_identifier(&mut *p);
380        if !p.test(SyntaxKind::Comma) && p.nth(0).kind() != SyntaxKind::RBrace {
381            p.error("Expected comma or brace");
382            return false;
383        }
384    }
385}
386
387#[cfg_attr(test, parser_test)]
388/// ```test,ImportIdentifier
389/// Type
390/// Type as Alias1
391/// ```
392fn parse_import_identifier(p: &mut impl Parser) -> bool {
393    let mut p = p.start_node(SyntaxKind::ImportIdentifier);
394    {
395        let mut p = p.start_node(SyntaxKind::ExternalName);
396        if !p.expect(SyntaxKind::Identifier) {
397            return false;
398        }
399    }
400    if p.nth(0).kind() == SyntaxKind::Identifier && p.peek().as_str() == "as" {
401        p.consume();
402        let mut p = p.start_node(SyntaxKind::InternalName);
403        if !p.expect(SyntaxKind::Identifier) {
404            return false;
405        }
406    }
407    true
408}
409
410#[cfg_attr(test, parser_test)]
411/// ```test,UsesSpecifier
412/// uses { Interface from child }
413/// uses { Interface from child, }
414/// uses { Interface1 from child1, Interface2 from child2 }
415/// uses { Interface1 from child2, Qualified.Interface from child2 }
416/// uses { Qualified.Interface from child }
417/// uses { Qualified.Interface from child, }
418/// uses { Qualified.Interface from child1, Interface from child2 }
419/// uses { Interface from child1, Qualified.Interface from child2 }
420/// ```
421fn parse_uses_specifier(p: &mut impl Parser) -> bool {
422    debug_assert_eq!(p.peek().as_str(), "uses");
423    let mut p = p.start_node(SyntaxKind::UsesSpecifier);
424    p.expect(SyntaxKind::Identifier); // "uses"
425    if !p.expect(SyntaxKind::LBrace) {
426        return false;
427    }
428    loop {
429        if p.test(SyntaxKind::RBrace) {
430            return true;
431        }
432        if !parse_uses_identifier(&mut *p) {
433            return false;
434        }
435        if !p.test(SyntaxKind::Comma) && p.nth(0).kind() != SyntaxKind::RBrace {
436            p.error("Expected comma or brace");
437            return false;
438        }
439    }
440}
441
442#[cfg_attr(test, parser_test)]
443/// ```test,UsesIdentifier
444/// Interface from child
445/// Fully.Qualified.Interface from child-component
446/// ```
447fn parse_uses_identifier(p: &mut impl Parser) -> bool {
448    let mut p = p.start_node(SyntaxKind::UsesIdentifier);
449
450    if !parse_qualified_name(&mut *p) {
451        drop(p.start_node(SyntaxKind::DeclaredIdentifier));
452        return false;
453    }
454
455    if !(p.nth(0).kind() == SyntaxKind::Identifier && p.peek().as_str() == "from") {
456        p.error("Expected 'from' keyword in uses specifier");
457        drop(p.start_node(SyntaxKind::DeclaredIdentifier));
458        return false;
459    }
460    p.consume();
461
462    let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
463    p.expect(SyntaxKind::Identifier)
464}
465
466#[cfg_attr(test, parser_test)]
467/// ```test,ImplementsSpecifier
468/// implements Foo
469/// implements Foo.Bar
470/// ```
471fn parse_implements_specifier(p: &mut impl Parser) -> bool {
472    debug_assert_eq!(p.peek().as_str(), "implements");
473    let mut p = p.start_node(SyntaxKind::ImplementsSpecifier);
474    p.expect(SyntaxKind::Identifier); // "implements"
475    parse_qualified_name(&mut *p)
476}