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/// /* 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/// interface I { property<int> xx; }
103/// component E implements I { }
104/// component F uses { I from A } { }
105/// component F uses { I from A } implements J { }
106/// component F uses { I from A } inherits B { }
107/// component F uses { I from A, J from B } { }
108/// component F uses { I from A, J from B } implements J { }
109/// component F uses { I from A, J from B } inherits C { }
110/// ```
111pub fn parse_component(p: &mut impl Parser) -> bool {
112    let simple_component = p.nth(1).kind() == SyntaxKind::ColonEqual;
113    let is_global = !simple_component && p.peek().as_str() == "global";
114    let is_interface = !simple_component && p.peek().as_str() == "interface";
115    let is_new_component = !simple_component && p.peek().as_str() == "component";
116    if !is_global && !simple_component && !is_new_component && !is_interface {
117        p.error(
118            "Parse error: expected a top-level item such as a component, a struct, or a global",
119        );
120        return false;
121    }
122    let mut p = p.start_node(SyntaxKind::Component);
123    if is_global || is_new_component || is_interface {
124        p.consume();
125    }
126    if !p.start_node(SyntaxKind::DeclaredIdentifier).expect(SyntaxKind::Identifier) {
127        drop(p.start_node(SyntaxKind::Element));
128        return false;
129    }
130    if p.peek().as_str() == "uses" {
131        if !is_new_component {
132            p.error("Only components can have 'uses' clauses");
133            drop(p.start_node(SyntaxKind::Element));
134            return false;
135        }
136
137        if !parse_uses_specifier(&mut *p) {
138            drop(p.start_node(SyntaxKind::Element));
139            return false;
140        }
141    }
142    if is_global {
143        if p.peek().kind() == SyntaxKind::ColonEqual {
144            p.warning("':=' to declare a global is deprecated. Remove the ':='");
145            p.consume();
146        }
147    } else if is_interface {
148        if p.peek().kind() == SyntaxKind::ColonEqual {
149            p.error("':=' to declare an interface is not supported. Remove the ':='");
150            p.consume();
151        }
152    } else if !is_new_component {
153        if p.peek().kind() == SyntaxKind::ColonEqual {
154            p.warning("':=' to declare a component is deprecated. The new syntax declare components with 'component MyComponent {'. Read the documentation for more info");
155        }
156        if !p.expect(SyntaxKind::ColonEqual) {
157            drop(p.start_node(SyntaxKind::Element));
158            return false;
159        }
160    } else if p.peek().as_str() == "implements" || p.peek().as_str() == "inherits" {
161        p.consume();
162    } else if p.peek().kind() == SyntaxKind::LBrace {
163        let mut p = p.start_node(SyntaxKind::Element);
164        p.consume();
165        parse_element_content(&mut *p);
166        return p.expect(SyntaxKind::RBrace);
167    } else {
168        p.error("Expected '{', keyword 'implements' or keyword 'inherits'");
169        drop(p.start_node(SyntaxKind::Element));
170        return false;
171    }
172
173    if (is_global || is_interface) && p.peek().kind() == SyntaxKind::LBrace {
174        let mut p = p.start_node(SyntaxKind::Element);
175        p.consume();
176        parse_element_content(&mut *p);
177        return p.expect(SyntaxKind::RBrace);
178    }
179
180    parse_element(&mut *p)
181}
182
183#[cfg_attr(test, parser_test)]
184/// ```test,QualifiedName
185/// Rectangle
186/// MyModule.Rectangle
187/// Deeply.Nested.MyModule.Rectangle
188/// ```
189pub fn parse_qualified_name(p: &mut impl Parser) -> bool {
190    let mut p = p.start_node(SyntaxKind::QualifiedName);
191    if !p.expect(SyntaxKind::Identifier) {
192        return false;
193    }
194
195    loop {
196        if p.nth(0).kind() != SyntaxKind::Dot {
197            break;
198        }
199        p.consume();
200        p.expect(SyntaxKind::Identifier);
201    }
202
203    true
204}
205
206#[cfg_attr(test, parser_test)]
207/// ```test,ExportsList
208/// export { Type }
209/// export { Type, AnotherType, }
210/// export { Type as Foo, AnotherType }
211/// export Foo := Item { }
212/// export struct Foo := { foo: bar }
213/// export enum Foo { bar }
214/// export * from "foo";
215/// export { Abc } from "foo";
216/// export { Abc, Efg } from "foo";
217/// ```
218fn parse_export<P: Parser>(p: &mut P, checkpoint: Option<P::Checkpoint>) -> bool {
219    debug_assert_eq!(p.peek().as_str(), "export");
220    let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::ExportsList);
221
222    p.expect(SyntaxKind::Identifier); // "export"
223    if p.test(SyntaxKind::LBrace) {
224        loop {
225            if p.test(SyntaxKind::RBrace) {
226                break;
227            }
228            parse_export_specifier(&mut *p);
229            match p.nth(0).kind() {
230                SyntaxKind::RBrace => {
231                    p.consume();
232                    break;
233                }
234                SyntaxKind::Eof => {
235                    p.error("Expected comma");
236                    return false;
237                }
238                SyntaxKind::Comma => {
239                    p.consume();
240                }
241                _ => {
242                    p.consume();
243                    p.error("Expected comma");
244                    return false;
245                }
246            }
247        }
248        if p.peek().as_str() == "from" {
249            let mut p = p.start_node(SyntaxKind::ExportModule);
250            p.consume(); // "from"
251            p.expect(SyntaxKind::StringLiteral);
252            p.expect(SyntaxKind::Semicolon);
253        }
254        true
255    } else if p.peek().as_str() == "struct" {
256        parse_struct_declaration(&mut *p, checkpoint)
257    } else if p.peek().as_str() == "enum" {
258        parse_enum_declaration(&mut *p, checkpoint)
259    } else if p.peek().kind == SyntaxKind::Star {
260        let mut p = p.start_node(SyntaxKind::ExportModule);
261        p.consume(); // *
262        if p.peek().as_str() != "from" {
263            p.error("Expected from keyword for export statement");
264            return false;
265        }
266        p.consume();
267        let peek = p.peek();
268        if peek.kind != SyntaxKind::StringLiteral
269            || !peek.as_str().starts_with('"')
270            || !peek.as_str().ends_with('"')
271        {
272            p.error("Expected plain string literal");
273            return false;
274        }
275        p.consume();
276        p.expect(SyntaxKind::Semicolon)
277    } else {
278        parse_component(&mut *p)
279    }
280}
281
282#[cfg_attr(test, parser_test)]
283/// ```test,ExportSpecifier
284/// Type
285/// Type as Something
286/// ```
287fn parse_export_specifier(p: &mut impl Parser) -> bool {
288    let mut p = p.start_node(SyntaxKind::ExportSpecifier);
289    {
290        let mut p = p.start_node(SyntaxKind::ExportIdentifier);
291        if !p.expect(SyntaxKind::Identifier) {
292            return false;
293        }
294    }
295    if p.peek().as_str() == "as" {
296        p.consume();
297        let mut p = p.start_node(SyntaxKind::ExportName);
298        if !p.expect(SyntaxKind::Identifier) {
299            return false;
300        }
301    }
302
303    true
304}
305
306#[cfg_attr(test, parser_test)]
307/// ```test,ImportSpecifier
308/// import { Type1, Type2 } from "somewhere";
309/// import "something.ttf";
310/// ```
311fn parse_import_specifier(p: &mut impl Parser) -> bool {
312    debug_assert_eq!(p.peek().as_str(), "import");
313    let mut p = p.start_node(SyntaxKind::ImportSpecifier);
314    p.expect(SyntaxKind::Identifier); // "import"
315    if p.peek().kind != SyntaxKind::StringLiteral {
316        if !parse_import_identifier_list(&mut *p) {
317            return false;
318        }
319        if p.peek().as_str() != "from" {
320            p.error("Expected from keyword for import statement");
321            return false;
322        }
323        if !p.expect(SyntaxKind::Identifier) {
324            return false;
325        }
326    }
327    let peek = p.peek();
328    if peek.kind != SyntaxKind::StringLiteral
329        || !peek.as_str().starts_with('"')
330        || !peek.as_str().ends_with('"')
331    {
332        p.error("Expected plain string literal");
333        return false;
334    }
335    p.consume();
336    p.expect(SyntaxKind::Semicolon)
337}
338
339#[cfg_attr(test, parser_test)]
340/// ```test,ImportIdentifierList
341/// { Type1 }
342/// { Type2, }
343/// { Type3, Type4 }
344/// { Type5, Type6, }
345/// { Type as Alias1, Type as AnotherAlias1 }
346/// { Type as Alias2, Type as AnotherAlias2, }
347/// {}
348/// ```
349fn parse_import_identifier_list(p: &mut impl Parser) -> bool {
350    let mut p = p.start_node(SyntaxKind::ImportIdentifierList);
351    if !p.expect(SyntaxKind::LBrace) {
352        return false;
353    }
354    loop {
355        if p.test(SyntaxKind::RBrace) {
356            return true;
357        }
358        parse_import_identifier(&mut *p);
359        if !p.test(SyntaxKind::Comma) && p.nth(0).kind() != SyntaxKind::RBrace {
360            p.error("Expected comma or brace");
361            return false;
362        }
363    }
364}
365
366#[cfg_attr(test, parser_test)]
367/// ```test,ImportIdentifier
368/// Type
369/// Type as Alias1
370/// ```
371fn parse_import_identifier(p: &mut impl Parser) -> bool {
372    let mut p = p.start_node(SyntaxKind::ImportIdentifier);
373    {
374        let mut p = p.start_node(SyntaxKind::ExternalName);
375        if !p.expect(SyntaxKind::Identifier) {
376            return false;
377        }
378    }
379    if p.nth(0).kind() == SyntaxKind::Identifier && p.peek().as_str() == "as" {
380        p.consume();
381        let mut p = p.start_node(SyntaxKind::InternalName);
382        if !p.expect(SyntaxKind::Identifier) {
383            return false;
384        }
385    }
386    true
387}
388
389#[cfg_attr(test, parser_test)]
390/// ```test,UsesSpecifier
391/// uses { Interface from child }
392/// uses { Interface from child, }
393/// uses { Interface1 from child1, Interface2 from child2 }
394/// uses { Interface1 from child2, Qualified.Interface from child2 }
395/// uses { Qualified.Interface from child }
396/// uses { Qualified.Interface from child, }
397/// uses { Qualified.Interface from child1, Interface from child2 }
398/// uses { Interface from child1, Qualified.Interface from child2 }
399/// ```
400fn parse_uses_specifier(p: &mut impl Parser) -> bool {
401    debug_assert_eq!(p.peek().as_str(), "uses");
402    let mut p = p.start_node(SyntaxKind::UsesSpecifier);
403    p.expect(SyntaxKind::Identifier); // "uses"
404    if !p.expect(SyntaxKind::LBrace) {
405        return false;
406    }
407    loop {
408        if p.test(SyntaxKind::RBrace) {
409            return true;
410        }
411        if !parse_uses_identifier(&mut *p) {
412            return false;
413        }
414        if !p.test(SyntaxKind::Comma) && p.nth(0).kind() != SyntaxKind::RBrace {
415            p.error("Expected comma or brace");
416            return false;
417        }
418    }
419}
420
421#[cfg_attr(test, parser_test)]
422/// ```test,UsesIdentifier
423/// Interface from child
424/// Fully.Qualified.Interface from child-component
425/// ```
426fn parse_uses_identifier(p: &mut impl Parser) -> bool {
427    let mut p = p.start_node(SyntaxKind::UsesIdentifier);
428
429    if !parse_qualified_name(&mut *p) {
430        drop(p.start_node(SyntaxKind::DeclaredIdentifier));
431        return false;
432    }
433
434    if !(p.nth(0).kind() == SyntaxKind::Identifier && p.peek().as_str() == "from") {
435        p.error("Expected 'from' keyword in uses specifier");
436        drop(p.start_node(SyntaxKind::DeclaredIdentifier));
437        return false;
438    }
439    p.consume();
440
441    let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
442    p.expect(SyntaxKind::Identifier)
443}