Skip to main content

i_slint_compiler/parser/
statements.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 crate::parser::r#type::parse_type;
5
6use super::element::parse_code_block;
7use super::expressions::parse_expression;
8use super::prelude::*;
9
10#[cfg_attr(test, parser_test)]
11/// ```test
12/// expression
13/// expression += expression
14/// expression.expression *= 45.2
15/// expression = "hello"
16/// if (true) { foo = bar; } else { bar = foo;  }
17/// return;
18/// if (true) { return 42; }
19/// let foo = 1;
20/// let bar = foo;
21/// let str: string = "hello world";
22/// ```
23pub fn parse_statement(p: &mut impl Parser) -> bool {
24    if p.nth(0).kind() == SyntaxKind::RBrace {
25        return false;
26    }
27    if p.test(SyntaxKind::Semicolon) {
28        return true;
29    }
30    let checkpoint = p.checkpoint();
31
32    if p.peek().as_str() == "if"
33        && !matches!(
34            p.nth(1).kind(),
35            SyntaxKind::Dot
36                | SyntaxKind::Comma
37                | SyntaxKind::Semicolon
38                | SyntaxKind::RBrace
39                | SyntaxKind::RBracket
40                | SyntaxKind::RParent
41        )
42    {
43        let mut p = p.start_node(SyntaxKind::Expression);
44        parse_if_statement(&mut *p);
45        return true;
46    }
47
48    if p.peek().as_str() == "return" {
49        let mut p = p.start_node_at(checkpoint, SyntaxKind::ReturnStatement);
50        p.expect(SyntaxKind::Identifier); // "return"
51        if !p.test(SyntaxKind::Semicolon) {
52            parse_expression(&mut *p);
53            p.expect(SyntaxKind::Semicolon);
54        }
55        return true;
56    }
57
58    if p.peek().as_str() == "let" && p.nth(1).kind() == SyntaxKind::Identifier {
59        parse_let_statement(p);
60        return true;
61    }
62
63    if p.nth(0).kind() == SyntaxKind::Identifier && p.nth(1).kind() == SyntaxKind::ColonEqual {
64        let name = p.nth(0).as_str().to_string();
65        // `foo := Identifier {` likely means a '}' is missing earlier; let the outer parser recover the element.
66        if p.nth(2).kind() == SyntaxKind::Identifier && p.nth(3).kind() == SyntaxKind::LBrace {
67            p.error("':=' is not valid in statements");
68            return false;
69        }
70        // Otherwise the user probably meant to declare a local variable.
71        p.error(format!(
72            "':=' is not valid in statements. Use 'let {name} = <expression>;' to declare a local variable"
73        ));
74        let mut p = p.start_node_at(checkpoint, SyntaxKind::LetStatement);
75        {
76            let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
77            p.expect(SyntaxKind::Identifier);
78        }
79        p.expect(SyntaxKind::ColonEqual);
80        parse_expression(&mut *p);
81        p.test(SyntaxKind::Semicolon);
82        return true;
83    }
84
85    parse_expression(p);
86    if matches!(
87        p.nth(0).kind(),
88        SyntaxKind::MinusEqual
89            | SyntaxKind::PlusEqual
90            | SyntaxKind::StarEqual
91            | SyntaxKind::DivEqual
92            | SyntaxKind::Equal
93    ) {
94        let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
95        let mut p = p.start_node_at(checkpoint, SyntaxKind::SelfAssignment);
96        p.consume();
97        parse_expression(&mut *p);
98    }
99    p.test(SyntaxKind::Semicolon)
100}
101
102#[cfg_attr(test, parser_test)]
103/// ```test,LetStatement
104/// let foo = 1;
105/// let bar = foo;
106/// let str: string = "hello world";
107/// ```
108fn parse_let_statement(p: &mut impl Parser) {
109    let mut p = p.start_node(SyntaxKind::LetStatement);
110    debug_assert_eq!(p.peek().as_str(), "let");
111    p.expect(SyntaxKind::Identifier); // "let"
112    {
113        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
114        p.expect(SyntaxKind::Identifier);
115    }
116
117    if p.test(SyntaxKind::Colon) {
118        parse_type(&mut *p);
119    }
120
121    p.expect(SyntaxKind::Equal);
122    parse_expression(&mut *p);
123    p.expect(SyntaxKind::Semicolon);
124}
125
126#[cfg_attr(test, parser_test)]
127/// ```test,ConditionalExpression
128/// if (true) { foo = bar; } else { bar = foo;  }
129/// if (true) { foo += bar; }
130/// if (true) { } else { ; }
131/// if (true) { } else if (false) { } else if (xxx) { }
132/// ```
133fn parse_if_statement(p: &mut impl Parser) {
134    let mut p = p.start_node(SyntaxKind::ConditionalExpression);
135    debug_assert_eq!(p.peek().as_str(), "if");
136    p.expect(SyntaxKind::Identifier);
137    parse_expression(&mut *p);
138    {
139        let mut p = p.start_node(SyntaxKind::Expression);
140        parse_code_block(&mut *p);
141    }
142    if p.peek().as_str() == "else" {
143        p.expect(SyntaxKind::Identifier);
144        let mut p = p.start_node(SyntaxKind::Expression);
145        if p.peek().as_str() == "if" {
146            parse_if_statement(&mut *p)
147        } else {
148            parse_code_block(&mut *p);
149        }
150    } else {
151        // We need an expression so fake an empty block.
152        // FIXME: this shouldn't be needed
153        let mut p = p.start_node(SyntaxKind::Expression);
154        let _ = p.start_node(SyntaxKind::CodeBlock);
155    }
156}