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