1use super::{
2 BoolExpr, CallArg, CustomFieldAccess, FloatExpr, IntExpr, NilFunctionExpr, NilListExpr,
3 PanicExpr, StringExpr, TupleExpr,
4};
5use crate::plan::{ConstantNilReference, FunctionInstantiation, HostCallSite, NilLocalId, Step};
6use ecow::EcoString;
7use num_bigint::BigInt;
8
9#[derive(Debug, Clone, PartialEq)]
10pub struct NilExpr {
11 kind: NilExprKind,
12}
13
14#[derive(Debug, Clone, PartialEq)]
15pub(crate) enum NilExprKind {
16 Value,
17 Constant(ConstantNilReference),
18 LocalGet {
19 local: NilLocalId,
20 name: EcoString,
21 },
22 Call {
23 function: FunctionInstantiation,
24 args: Vec<CallArg>,
25 site: HostCallSite,
26 },
27 FunctionCall {
28 function: Box<NilFunctionExpr>,
29 args: Vec<CallArg>,
30 site: HostCallSite,
31 },
32 TupleIndex {
33 tuple: Box<TupleExpr>,
34 index: usize,
35 },
36 CustomField(CustomFieldAccess),
37 ListIndex {
38 list: Box<NilListExpr>,
39 index: usize,
40 },
41 Panic(PanicExpr),
42 BoolCase {
43 subject: Box<BoolExpr>,
44 true_: Box<NilExpr>,
45 false_: Box<NilExpr>,
46 },
47 IntCase {
48 subject: Box<IntExpr>,
49 clauses: Vec<(BigInt, NilExpr)>,
50 fallback: Box<NilExpr>,
51 },
52 StringCase {
53 subject: Box<StringExpr>,
54 clauses: Vec<(EcoString, NilExpr)>,
55 fallback: Box<NilExpr>,
56 },
57 FloatCase {
58 subject: Box<FloatExpr>,
59 clauses: Vec<(f64, NilExpr)>,
60 fallback: Box<NilExpr>,
61 },
62 Block {
63 steps: Vec<Step>,
64 return_: Box<NilExpr>,
65 },
66}
67
68impl NilExpr {
69 pub(crate) fn value() -> Self {
70 Self {
71 kind: NilExprKind::Value,
72 }
73 }
74
75 pub(in crate::plan::module) fn constant(reference: ConstantNilReference) -> Self {
76 Self {
77 kind: NilExprKind::Constant(reference),
78 }
79 }
80
81 pub(crate) fn local_get(local: NilLocalId, name: EcoString) -> Self {
82 Self {
83 kind: NilExprKind::LocalGet { local, name },
84 }
85 }
86
87 #[cfg(test)]
88 pub(crate) fn call(function: FunctionInstantiation, args: Vec<CallArg>) -> Self {
89 Self::call_at(function, args, HostCallSite::unknown())
90 }
91
92 pub(crate) fn call_at(
93 function: FunctionInstantiation,
94 args: Vec<CallArg>,
95 site: HostCallSite,
96 ) -> Self {
97 Self {
98 kind: NilExprKind::Call {
99 function,
100 args,
101 site,
102 },
103 }
104 }
105
106 #[cfg(test)]
107 pub(crate) fn function_call(function: NilFunctionExpr, args: Vec<CallArg>) -> Self {
108 Self::function_call_at(function, args, HostCallSite::unknown())
109 }
110
111 pub(crate) fn function_call_at(
112 function: NilFunctionExpr,
113 args: Vec<CallArg>,
114 site: HostCallSite,
115 ) -> Self {
116 Self {
117 kind: NilExprKind::FunctionCall {
118 function: Box::new(function),
119 args,
120 site,
121 },
122 }
123 }
124
125 pub(crate) fn tuple_index(tuple: TupleExpr, index: usize) -> Self {
126 Self {
127 kind: NilExprKind::TupleIndex {
128 tuple: Box::new(tuple),
129 index,
130 },
131 }
132 }
133
134 pub(crate) fn custom_field(access: CustomFieldAccess) -> Self {
135 Self {
136 kind: NilExprKind::CustomField(access),
137 }
138 }
139
140 pub(crate) fn list_index(list: impl Into<NilListExpr>, index: usize) -> Self {
141 Self {
142 kind: NilExprKind::ListIndex {
143 list: Box::new(list.into()),
144 index,
145 },
146 }
147 }
148
149 pub(crate) fn panic(panic: PanicExpr) -> Self {
150 Self {
151 kind: NilExprKind::Panic(panic),
152 }
153 }
154
155 pub(crate) fn bool_case(subject: BoolExpr, true_: NilExpr, false_: NilExpr) -> Self {
156 Self {
157 kind: NilExprKind::BoolCase {
158 subject: Box::new(subject),
159 true_: Box::new(true_),
160 false_: Box::new(false_),
161 },
162 }
163 }
164
165 pub(crate) fn int_case(
166 subject: IntExpr,
167 clauses: Vec<(BigInt, NilExpr)>,
168 fallback: NilExpr,
169 ) -> Self {
170 Self {
171 kind: NilExprKind::IntCase {
172 subject: Box::new(subject),
173 clauses,
174 fallback: Box::new(fallback),
175 },
176 }
177 }
178
179 pub(crate) fn string_case(
180 subject: StringExpr,
181 clauses: Vec<(EcoString, NilExpr)>,
182 fallback: NilExpr,
183 ) -> Self {
184 Self {
185 kind: NilExprKind::StringCase {
186 subject: Box::new(subject),
187 clauses,
188 fallback: Box::new(fallback),
189 },
190 }
191 }
192
193 pub(crate) fn float_case(
194 subject: FloatExpr,
195 clauses: Vec<(f64, NilExpr)>,
196 fallback: NilExpr,
197 ) -> Self {
198 Self {
199 kind: NilExprKind::FloatCase {
200 subject: Box::new(subject),
201 clauses,
202 fallback: Box::new(fallback),
203 },
204 }
205 }
206
207 pub(crate) fn block(steps: Vec<Step>, return_: NilExpr) -> Self {
208 Self {
209 kind: NilExprKind::Block {
210 steps,
211 return_: Box::new(return_),
212 },
213 }
214 }
215
216 pub(crate) fn kind(&self) -> &NilExprKind {
217 &self.kind
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::{NilExpr, NilExprKind};
224 use crate::plan::{
225 BoolExpr, Expr, FunctionInstantiation, FunctionShape, IntExpr, NilFunctionReference,
226 NilLocalId, Step, TupleExpr, ValueShape, ValueType, monomorphic_function_instantiation,
227 };
228 use num_bigint::BigInt;
229
230 #[test]
231 fn nil_expr_kind_accessors() {
232 assert_eq!(NilExpr::value().kind(), &NilExprKind::Value);
233 assert_eq!(
234 NilExpr::local_get(NilLocalId(0), "unit".into()).kind(),
235 &NilExprKind::LocalGet {
236 local: NilLocalId(0),
237 name: "unit".into(),
238 },
239 );
240 assert_eq!(
241 NilExpr::call(function_instantiation(), Vec::new()).kind(),
242 &NilExprKind::Call {
243 function: function_instantiation(),
244 args: Vec::new(),
245 site: crate::plan::HostCallSite::unknown(),
246 },
247 );
248 assert_eq!(
249 NilExpr::function_call(function_expr(), Vec::new()).kind(),
250 &NilExprKind::FunctionCall {
251 function: Box::new(function_expr()),
252 args: Vec::new(),
253 site: crate::plan::HostCallSite::unknown(),
254 },
255 );
256 assert_eq!(
257 NilExpr::tuple_index(tuple_expr(), 0).kind(),
258 &NilExprKind::TupleIndex {
259 tuple: Box::new(tuple_expr()),
260 index: 0,
261 },
262 );
263 assert_eq!(
264 NilExpr::bool_case(BoolExpr::value(true), NilExpr::value(), NilExpr::value()).kind(),
265 &NilExprKind::BoolCase {
266 subject: Box::new(BoolExpr::value(true)),
267 true_: Box::new(NilExpr::value()),
268 false_: Box::new(NilExpr::value()),
269 },
270 );
271 assert_eq!(
272 NilExpr::int_case(
273 IntExpr::value(1.into()),
274 vec![(1.into(), NilExpr::value())],
275 NilExpr::value()
276 )
277 .kind(),
278 &NilExprKind::IntCase {
279 subject: Box::new(IntExpr::value(1.into())),
280 clauses: vec![(BigInt::from(1), NilExpr::value())],
281 fallback: Box::new(NilExpr::value()),
282 },
283 );
284 assert_eq!(
285 NilExpr::string_case(
286 crate::plan::StringExpr::value("a".into()),
287 vec![("a".into(), NilExpr::value())],
288 NilExpr::value()
289 )
290 .kind(),
291 &NilExprKind::StringCase {
292 subject: Box::new(crate::plan::StringExpr::value("a".into())),
293 clauses: vec![("a".into(), NilExpr::value())],
294 fallback: Box::new(NilExpr::value()),
295 },
296 );
297 assert_eq!(
298 NilExpr::float_case(
299 crate::plan::FloatExpr::value(1.0),
300 vec![(1.0, NilExpr::value())],
301 NilExpr::value()
302 )
303 .kind(),
304 &NilExprKind::FloatCase {
305 subject: Box::new(crate::plan::FloatExpr::value(1.0)),
306 clauses: vec![(1.0, NilExpr::value())],
307 fallback: Box::new(NilExpr::value()),
308 },
309 );
310 assert_eq!(
311 NilExpr::block(
312 vec![Step::evaluate(Expr::nil(NilExpr::value()))],
313 NilExpr::value(),
314 )
315 .kind(),
316 &NilExprKind::Block {
317 steps: vec![Step::evaluate(Expr::nil(NilExpr::value()))],
318 return_: Box::new(NilExpr::value()),
319 },
320 );
321 }
322
323 fn function_expr() -> crate::plan::NilFunctionExpr {
324 crate::plan::NilFunctionExpr::reference(NilFunctionReference::new(function_instantiation()))
325 }
326
327 fn function_instantiation() -> FunctionInstantiation {
328 monomorphic_function_instantiation(0, FunctionShape::new(Vec::new(), ValueShape::Nil))
329 }
330
331 fn tuple_expr() -> TupleExpr {
332 TupleExpr::value(vec![Expr::nil(NilExpr::value())], vec![ValueType::Nil])
333 }
334}