sqparse/parser/
control.rs

1use crate::ast::{
2    ForDefinition, ForeachIndex, Identifier, IfStatementType, Precedence, Statement, SwitchCase,
3    SwitchCaseCondition, Type,
4};
5use crate::parser::expression::expression;
6use crate::parser::identifier::identifier;
7use crate::parser::parse_result_ext::ParseResultExt;
8use crate::parser::statement::{statement, statement_type, typed_var_definition_statement};
9use crate::parser::token_list::TokenList;
10use crate::parser::token_list_ext::TokenListExt;
11use crate::parser::type_::type_;
12use crate::parser::ParseResult;
13use crate::token::{TerminalToken, Token};
14use crate::ContextType;
15
16pub fn if_statement_type(tokens: TokenList) -> ParseResult<IfStatementType> {
17    let (tokens, body) = statement_type(tokens)?;
18
19    // Consume a `;` if this is followed by an `else`.
20    let (next_tokens, semicolon) = tokens.terminal(TerminalToken::Semicolon).maybe(tokens)?;
21
22    let Ok((tokens, else_)) = next_tokens.terminal(TerminalToken::Else) else {
23        // Return the body WITHOUT consuming the `;`.
24        return Ok((tokens, IfStatementType::NoElse { body: Box::new(body) }))
25    };
26
27    let (tokens, else_body) = statement_type(tokens).definite()?;
28    Ok((
29        tokens,
30        IfStatementType::Else {
31            body: Box::new(Statement {
32                ty: body,
33                semicolon,
34            }),
35            else_,
36            else_body: Box::new(else_body),
37        },
38    ))
39}
40
41pub fn switch_case(tokens: TokenList) -> ParseResult<SwitchCase> {
42    switch_case_condition(tokens).determines(|tokens, condition| {
43        let (tokens, colon) = tokens.terminal(TerminalToken::Colon)?;
44        let (tokens, body) = tokens.many(statement)?;
45        Ok((
46            tokens,
47            SwitchCase {
48                condition,
49                colon,
50                body,
51            },
52        ))
53    })
54}
55
56pub fn switch_case_condition(tokens: TokenList) -> ParseResult<SwitchCaseCondition> {
57    default_switch_case_condition(tokens).or_try(|| case_switch_case_condition(tokens))
58}
59
60fn default_switch_case_condition(tokens: TokenList) -> ParseResult<SwitchCaseCondition> {
61    tokens
62        .terminal(TerminalToken::Default)
63        .map_val(|default| SwitchCaseCondition::Default { default })
64}
65
66fn case_switch_case_condition(tokens: TokenList) -> ParseResult<SwitchCaseCondition> {
67    tokens
68        .terminal(TerminalToken::Case)
69        .determines(|tokens, case| {
70            let (tokens, value) = expression(tokens, Precedence::None)?;
71            Ok((tokens, SwitchCaseCondition::Case { case, value }))
72        })
73}
74
75pub fn for_definition(tokens: TokenList) -> ParseResult<ForDefinition> {
76    var_for_definition(tokens).or_try(|| expression_for_definition(tokens))
77}
78
79fn var_for_definition(tokens: TokenList) -> ParseResult<ForDefinition> {
80    type_(tokens)
81        .not_definite()
82        .and_then(|(tokens, type_)| typed_var_definition_statement(tokens, type_))
83        .with_context_from(ContextType::VarDefinition, tokens)
84        .map_val(ForDefinition::Definition)
85}
86
87fn expression_for_definition(tokens: TokenList) -> ParseResult<ForDefinition> {
88    expression(tokens, Precedence::None)
89        .with_context_from(ContextType::Expression, tokens)
90        .map_val(ForDefinition::Expression)
91}
92
93pub fn foreach_index(tokens: TokenList) -> ParseResult<ForeachIndex> {
94    untyped_foreach_index(tokens).or_try(|| typed_foreach_index(tokens))
95}
96
97fn untyped_foreach_index(tokens: TokenList) -> ParseResult<ForeachIndex> {
98    let (tokens, name) = identifier(tokens)?;
99    let (tokens, comma) = tokens.terminal(TerminalToken::Comma)?;
100    Ok((
101        tokens,
102        ForeachIndex {
103            type_: None,
104            name,
105            comma,
106        },
107    ))
108}
109
110fn typed_foreach_index(tokens: TokenList) -> ParseResult<ForeachIndex> {
111    let (tokens, type_) = type_(tokens)?;
112    let (tokens, name) = identifier(tokens)?;
113    let (tokens, comma) = tokens.terminal(TerminalToken::Comma)?;
114    Ok((
115        tokens,
116        ForeachIndex {
117            type_: Some(type_),
118            name,
119            comma,
120        },
121    ))
122}
123
124pub fn foreach_value(tokens: TokenList) -> ParseResult<(Option<Type>, Identifier, &Token)> {
125    untyped_foreach_value(tokens).or_try(|| typed_foreach_value(tokens))
126}
127
128fn typed_foreach_value(tokens: TokenList) -> ParseResult<(Option<Type>, Identifier, &Token)> {
129    type_(tokens)
130        .and_then(|(tokens, type_)| identifier(tokens).map_val(|name| (type_, name)))
131        .determines(|tokens, (type_, name)| {
132            let (tokens, in_) = tokens.terminal(TerminalToken::In)?;
133            Ok((tokens, (Some(type_), name, in_)))
134        })
135}
136
137fn untyped_foreach_value(tokens: TokenList) -> ParseResult<(Option<Type>, Identifier, &Token)> {
138    let (tokens, name) = identifier(tokens)?;
139    let (tokens, in_) = tokens.terminal(TerminalToken::In)?;
140    Ok((tokens, (None, name, in_)))
141}