lust/ast/
expr.rs

1use super::{Span, Type};
2use alloc::{boxed::Box, format, string::{String, ToString}, vec::Vec};
3use core::fmt;
4use crate::number::{LustFloat, LustInt};
5#[derive(Debug, Clone, PartialEq)]
6pub struct Expr {
7    pub kind: ExprKind,
8    pub span: Span,
9}
10
11impl Expr {
12    pub fn new(kind: ExprKind, span: Span) -> Self {
13        Self { kind, span }
14    }
15}
16
17#[derive(Debug, Clone, PartialEq)]
18pub enum ExprKind {
19    Literal(Literal),
20    Identifier(String),
21    Binary {
22        left: Box<Expr>,
23        op: BinaryOp,
24        right: Box<Expr>,
25    },
26    Unary {
27        op: UnaryOp,
28        operand: Box<Expr>,
29    },
30    Call {
31        callee: Box<Expr>,
32        args: Vec<Expr>,
33    },
34    MethodCall {
35        receiver: Box<Expr>,
36        method: String,
37        type_args: Option<Vec<Type>>,
38        args: Vec<Expr>,
39    },
40    FieldAccess {
41        object: Box<Expr>,
42        field: String,
43    },
44    Index {
45        object: Box<Expr>,
46        index: Box<Expr>,
47    },
48    Array(Vec<Expr>),
49    Map(Vec<(Expr, Expr)>),
50    Tuple(Vec<Expr>),
51    StructLiteral {
52        name: String,
53        fields: Vec<StructLiteralField>,
54    },
55    EnumConstructor {
56        enum_name: String,
57        variant: String,
58        args: Vec<Expr>,
59    },
60    Lambda {
61        params: Vec<(String, Option<Type>)>,
62        return_type: Option<Type>,
63        body: Box<Expr>,
64    },
65    Paren(Box<Expr>),
66    Cast {
67        expr: Box<Expr>,
68        target_type: Type,
69    },
70    TypeCheck {
71        expr: Box<Expr>,
72        check_type: Type,
73    },
74    IsPattern {
75        expr: Box<Expr>,
76        pattern: Pattern,
77    },
78    If {
79        condition: Box<Expr>,
80        then_branch: Box<Expr>,
81        else_branch: Option<Box<Expr>>,
82    },
83    Block(Vec<super::stmt::Stmt>),
84    Return(Vec<Expr>),
85    Range {
86        start: Box<Expr>,
87        end: Box<Expr>,
88        inclusive: bool,
89    },
90}
91
92#[derive(Debug, Clone, PartialEq)]
93pub struct StructLiteralField {
94    pub name: String,
95    pub value: Expr,
96    pub span: Span,
97}
98
99#[derive(Debug, Clone, PartialEq)]
100pub enum Pattern {
101    Wildcard,
102    Literal(Literal),
103    Identifier(String),
104    Enum {
105        enum_name: String,
106        variant: String,
107        bindings: Vec<Pattern>,
108    },
109    Struct {
110        name: String,
111        fields: Vec<(String, Pattern)>,
112    },
113    TypeCheck(Type),
114}
115
116#[derive(Debug, Clone, PartialEq)]
117pub enum Literal {
118    Integer(LustInt),
119    Float(LustFloat),
120    String(String),
121    Bool(bool),
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum BinaryOp {
126    Add,
127    Sub,
128    Mul,
129    Div,
130    Mod,
131    Pow,
132    Eq,
133    Ne,
134    Lt,
135    Le,
136    Gt,
137    Ge,
138    And,
139    Or,
140    Concat,
141    Range,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145pub enum UnaryOp {
146    Neg,
147    Not,
148}
149
150impl fmt::Display for BinaryOp {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        let symbol = match self {
153            BinaryOp::Add => "+",
154            BinaryOp::Sub => "-",
155            BinaryOp::Mul => "*",
156            BinaryOp::Div => "/",
157            BinaryOp::Mod => "%",
158            BinaryOp::Pow => "^",
159            BinaryOp::Eq => "==",
160            BinaryOp::Ne => "!=",
161            BinaryOp::Lt => "<",
162            BinaryOp::Le => "<=",
163            BinaryOp::Gt => ">",
164            BinaryOp::Ge => ">=",
165            BinaryOp::And => "and",
166            BinaryOp::Or => "or",
167            BinaryOp::Concat => "..",
168            BinaryOp::Range => "..",
169        };
170        write!(f, "{symbol}")
171    }
172}
173
174impl fmt::Display for UnaryOp {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        match self {
177            UnaryOp::Neg => write!(f, "-"),
178            UnaryOp::Not => write!(f, "not"),
179        }
180    }
181}
182
183impl fmt::Display for Literal {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        match self {
186            Literal::Integer(value) => write!(f, "{value}"),
187            Literal::Float(value) => write!(f, "{value}"),
188            Literal::String(value) => write!(f, "\"{}\"", value.escape_default()),
189            Literal::Bool(value) => write!(f, "{value}"),
190        }
191    }
192}
193
194impl fmt::Display for Pattern {
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        match self {
197            Pattern::Wildcard => write!(f, "_"),
198            Pattern::Literal(lit) => write!(f, "{lit}"),
199            Pattern::Identifier(name) => write!(f, "{name}"),
200            Pattern::Enum {
201                enum_name,
202                variant,
203                bindings,
204            } => {
205                let qualified = if enum_name.is_empty() {
206                    variant.clone()
207                } else {
208                    format!("{enum_name}.{variant}")
209                };
210                if bindings.is_empty() {
211                    write!(f, "{qualified}")
212                } else {
213                    let binding_str = bindings
214                        .iter()
215                        .map(|p| p.to_string())
216                        .collect::<Vec<_>>()
217                        .join(", ");
218                    write!(f, "{qualified}({binding_str})")
219                }
220            }
221
222            Pattern::Struct { name, fields } => {
223                let field_str = fields
224                    .iter()
225                    .map(|(field_name, pattern)| match pattern {
226                        Pattern::Identifier(id) if id == field_name => field_name.clone(),
227                        _ => format!("{field_name} = {pattern}"),
228                    })
229                    .collect::<Vec<_>>()
230                    .join(", ");
231                write!(f, "{name} {{ {field_str} }}")
232            }
233
234            Pattern::TypeCheck(ty) => write!(f, "as {ty}"),
235        }
236    }
237}