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 {
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 while !p.test(SyntaxKind::Eof) {
92 p.consume()
93 }
94 false
95}
96
97#[cfg_attr(test, parser_test)]
98pub 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)]
205pub 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)]
228fn 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); 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(); 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(); 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)]
304fn 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)]
328fn 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); 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)]
361fn 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)]
388fn 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)]
411fn 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); 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)]
443fn 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)]
467fn 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); parse_qualified_name(&mut *p)
476}