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