Skip to main content

skullbrain_parser/cst/
expr.rs

1use crate::cst::utils::token_with;
2
3use super::*;
4
5/// The set of all expression-like AST nodes and tokens.
6pub enum Expr {
7    /// Literal: numbers, strings, etc.
8    Literal(LiteralToken),
9
10    /// Identifier.
11    Ident(IdentToken),
12
13    /// Parenthesized sub-expression: `( ... )`
14    Paren(ParenExpr),
15
16    /// Brace block as an expression: `{ ... }`
17    Block(BlockExpr),
18    /// Binary operator expression.
19    Binary(BinaryExpr),
20    ///
21    Unary(UnaryExpr),
22    ///
23    Chain(ChainExpr),
24
25    /// Control-flow expressions/statements:
26    Break(Break),
27    ///
28    Continue(Continue),
29    ///
30    Return(Return),
31    ///
32    Throw(Throw),
33
34    /// Loops:
35    While(While),
36    ///
37    For(ForLoop),
38
39    /// Try / Catch:
40    TryCatch(TryCatch),
41
42    /// Conditional:
43    IfElse(IfElse),
44
45    /// Match:
46    Match(Match),
47
48    /// Declarations as expressions (e.g., in REPL contexts):
49    Variable(VariableStmt),
50    ///
51    Import(ImportStmt),
52    ///
53    TypeAlias(TypeAlias),
54    ///
55    Function(Function),
56    ///
57    Trait(Trait),
58    ///
59    TraitImpl(TraitImpl),
60    ///
61    Class(Class),
62    ///
63    Enum(Enum),
64
65    ///
66    Fragment(Fragment),
67}
68
69impl CstNode for Expr {
70    fn can_cast(_kind: SyntaxKind) -> bool {
71        true
72    }
73
74    fn syntax(&self) -> &SyntaxNode {
75        match self {
76            Expr::Break(n) => n.syntax(),
77            Expr::Continue(n) => n.syntax(),
78            Expr::Return(n) => n.syntax(),
79            Expr::Throw(n) => n.syntax(),
80            Expr::While(n) => n.syntax(),
81            Expr::For(n) => n.syntax(),
82            Expr::TryCatch(n) => n.syntax(),
83            Expr::IfElse(n) => n.syntax(),
84            Expr::Match(n) => n.syntax(),
85            Expr::Variable(n) => n.syntax(),
86            Expr::Import(n) => n.syntax(),
87            Expr::TypeAlias(n) => n.syntax(),
88            Expr::Function(n) => n.syntax(),
89            Expr::Trait(n) => n.syntax(),
90            Expr::TraitImpl(n) => n.syntax(),
91            Expr::Class(n) => n.syntax(),
92            Expr::Enum(n) => n.syntax(),
93
94            Expr::Binary(n) => &n.syntax,
95            Expr::Unary(n) => &n.syntax,
96            Expr::Chain(n) => &n.syntax,
97            Expr::Paren(n) => &n.syntax,
98            Expr::Block(n) => &n.syntax,
99            Expr::Fragment(n) => match n {
100                Fragment::EnumVariant(enum_variant_decl) => &enum_variant_decl.syntax,
101                Fragment::Modifiers(modifiers) => &modifiers.syntax,
102                Fragment::Generic { syntax } => &syntax,
103                Fragment::Unknown(_) => todo!(),
104                Fragment::MatchArm(match_arm) => match match_arm {
105                    MatchArm::WithCondition(match_arm_with_condition) => {
106                        &match_arm_with_condition.syntax
107                    }
108                    MatchArm::Default(match_arm_default) => &match_arm_default.syntax,
109                },
110            },
111            Expr::Literal(_) | Expr::Ident(_) => todo!(),
112        }
113    }
114
115    fn cast(syntax: SyntaxNode) -> Option<Self> {
116        let kind = syntax.kind();
117        Some(match kind {
118            SyntaxKind::DelimitedRange => disambiguate_delimited(syntax),
119            SyntaxKind::Operator => disambiguate_operator(syntax),
120            SyntaxKind::LoopBreak => Expr::Break(Break { syntax }),
121            SyntaxKind::LoopContinue => Expr::Continue(Continue { syntax }),
122            SyntaxKind::Return => Expr::Return(Return { syntax }),
123            SyntaxKind::ThrowError => Expr::Throw(Throw { syntax }),
124
125            SyntaxKind::WhileLoop => Expr::While(While { syntax }),
126            SyntaxKind::ForLoop => Expr::For(ForLoop { syntax }),
127            SyntaxKind::TryCatch => Expr::TryCatch(TryCatch { syntax }),
128            SyntaxKind::IfElse => Expr::IfElse(IfElse { syntax }),
129            SyntaxKind::Match => Expr::Match(Match { syntax }),
130            SyntaxKind::VariableDeclaration => Expr::Variable(VariableStmt { syntax }),
131            SyntaxKind::Import => Expr::Import(ImportStmt { syntax }),
132            SyntaxKind::TypeAlias => Expr::TypeAlias(TypeAlias { syntax }),
133            SyntaxKind::Function => Expr::Function(Function { syntax }),
134            SyntaxKind::Trait => Expr::Trait(Trait { syntax }),
135            SyntaxKind::TraitImpl => Expr::TraitImpl(TraitImpl { syntax }),
136            SyntaxKind::Class => Expr::Class(Class { syntax }),
137            SyntaxKind::Enum => Expr::Enum(Enum { syntax }),
138            SyntaxKind::EnumVariant => {
139                Expr::Fragment(Fragment::EnumVariant(EnumVariant { syntax }))
140            }
141            SyntaxKind::Modifiers => Expr::Fragment(Fragment::Modifiers(Modifiers { syntax })),
142            SyntaxKind::MatchCondition => Expr::Fragment(Fragment::MatchArm(
143                MatchArm::WithCondition(MatchArmWithCondition { syntax }),
144            )),
145            SyntaxKind::MatchConditionDefault => {
146                Expr::Fragment(Fragment::MatchArm(MatchArm::Default(MatchArmDefault {
147                    syntax,
148                })))
149            }
150            _ => Expr::Fragment(Fragment::Generic { syntax }),
151        })
152    }
153}
154
155fn disambiguate_delimited(syntax: SyntaxNode) -> Expr {
156    let para = ParenRange { syntax };
157
158    if para.open_token().is_some() || para.close_token().is_some() {
159        Expr::Paren(ParenExpr {
160            syntax: para.syntax,
161        })
162    } else {
163        Expr::Block(BlockExpr {
164            syntax: para.syntax,
165        })
166    }
167}
168
169fn disambiguate_operator(syntax: SyntaxNode) -> Expr {
170    const fn is(kind: SyntaxKind) -> bool {
171        matches!(
172            kind,
173            SyntaxKind::OpMethodChaining
174                | OpMinus
175                | OpNot
176                | OpEq
177                | OpNotEq
178                | OpLt
179                | OpGt
180                | OpLtEq
181                | OpGtEq
182                | OpAnd
183                | OpOr
184                | OpPlus
185                | OpMul
186                | OpDivide
187                | OpMod
188        )
189    }
190
191    let Some(token) = token_with(&syntax, is) else {
192        unreachable!()
193    };
194
195    use SyntaxKind::*;
196    match token.kind() {
197        OpMethodChaining => Expr::Chain(ChainExpr { syntax }),
198        OpEq | OpNotEq | OpLt | OpGt | OpLtEq | OpGtEq | OpAnd | OpOr | OpPlus | OpMul
199        | OpDivide | OpMod => Expr::Binary(BinaryExpr { syntax }),
200        OpNot => Expr::Unary(UnaryExpr { syntax }),
201        OpMinus => {
202            let binary = BinaryExpr { syntax };
203
204            // Since we dont create a lhs node when its unary-minus
205            match binary.lhs() {
206                None => Expr::Unary(UnaryExpr {
207                    syntax: binary.syntax,
208                }),
209                _ => Expr::Binary(binary),
210            }
211        }
212        _ => unreachable!(),
213    }
214}
215
216impl Expr {
217    pub(crate) fn cast_element(element: SyntaxElementRef) -> Expr {
218        match element {
219            cstree::util::NodeOrToken::Node(node) => {
220                let Some(expr) = Expr::cast(node.clone()) else {
221                    unreachable!()
222                };
223
224                expr
225            }
226            cstree::util::NodeOrToken::Token(token) => {
227                let token = token.clone();
228                match token.kind() {
229                    SyntaxKind::Unknown => {
230                        Expr::Fragment(Fragment::Unknown(UnknownToken { token }))
231                    }
232                    SyntaxKind::Literal => Expr::Literal(LiteralToken { token }),
233                    SyntaxKind::Ident => Expr::Ident(IdentToken { token }),
234
235                    _ => unreachable!(),
236                }
237            }
238        }
239    }
240}