i_slint_compiler/parser/
document.rs1use 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)]
9pub 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 while !p.test(SyntaxKind::Eof) {
88 p.consume()
89 }
90 false
91}
92
93#[cfg_attr(test, parser_test)]
94pub 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)]
184pub 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)]
207fn 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); 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(); 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(); 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)]
283fn 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)]
307fn 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); 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)]
340fn 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)]
367fn 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)]
390fn 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); 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)]
422fn 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}