Skip to main content

reqlang_expr/
types.rs

1use std::fmt::{Debug, Display};
2
3use regex::Regex;
4
5use crate::{prelude::BuiltinFn, value::Value};
6
7#[derive(Clone, PartialEq)]
8pub enum Type {
9    Value,
10    String,
11    Number,
12    Fn {
13        args: Vec<Type>,
14        variadic_arg: Option<Box<Type>>,
15        returns: Box<Type>,
16    },
17    Bool,
18    Type(Box<Type>),
19    Unknown,
20}
21
22impl Type {
23    pub fn from(name: &str) -> Self {
24        match name {
25            "String" => Type::String,
26            "Bool" => Type::Bool,
27            "Value" => Type::Value,
28            _ => {
29                if let Some(captures) = Regex::new(r"^Type<(.+)>$").unwrap().captures(name) {
30                    return Type::Type(Type::from(&captures[1]).into());
31                }
32
33                if let Some(captures) = Regex::new(
34                    r"^Fn\((?P<args>(?:\w+(?:,\s*)?)*)\s*(?:\.\.\.(?P<varg>\w+))?\)\s*->\s*(?P<return>\w+)$",
35                ).unwrap().captures(name) {
36                    let args_str = captures.name("args").map_or("", |m| m.as_str());
37                    let variadic_str = captures.name("varg").map_or("", |m| m.as_str());
38                    let return_type_str = captures.name("return").unwrap().as_str();
39
40                    let args: Vec<Type> = if args_str.trim().is_empty() {
41                        vec![]
42                    } else {
43                        args_str.split(',')
44                            .filter_map(|s| {
45                                let trimmed = s.trim();
46                                if trimmed.is_empty() { None } else { Some(Type::from(trimmed)) }
47                            })
48                            .collect()
49                    };
50
51                    let variadic_arg = if variadic_str.is_empty() {
52                        None
53                    } else {
54                        Some(Type::from(variadic_str).into())
55                    };
56
57                    let returns = Type::from(return_type_str).into();
58
59                    return Type::Fn {
60                        args,
61                        variadic_arg,
62                        returns,
63                    };
64                }
65
66                Type::Unknown
67            }
68        }
69    }
70
71    pub fn name(&self) -> String {
72        match self {
73            Type::Value => "Value".to_string(),
74            Type::String => "String".to_string(),
75            Type::Number => "Number".to_string(),
76            Type::Fn {
77                args,
78                variadic_arg,
79                returns,
80            } => {
81                let mut args: Vec<String> = args.iter().map(|arg| arg.name()).collect();
82
83                if let Some(varg) = variadic_arg {
84                    args.push(format!("...{}", varg.name()));
85                }
86
87                let args = args.join(", ");
88
89                let returns = returns.name();
90
91                format!("Fn({args}) -> {returns}")
92            }
93            Type::Bool => "Bool".to_string(),
94            Type::Type(ty) => ty.name().to_string(),
95            Type::Unknown => "Unknown".to_string(),
96        }
97    }
98
99    pub fn is_type(&self) -> bool {
100        matches!(self, Type::Type(_))
101    }
102}
103
104impl Display for Type {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        write!(f, "{}", self.name())
107    }
108}
109
110impl Debug for Type {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        write!(f, "{}", self.name())
113    }
114}
115
116impl From<Value> for Type {
117    fn from(value: Value) -> Self {
118        match value {
119            Value::String(_) => Type::String,
120            Value::Number(_) => Type::Number,
121            Value::Fn(builtin_fn) => {
122                let mut args: Vec<Type> =
123                    builtin_fn.args.iter().map(|arg| arg.ty.clone()).collect();
124
125                let variadic_arg = builtin_fn
126                    .args
127                    .last()
128                    .filter(|arg| arg.variadic)
129                    .map(|arg| Box::new(arg.ty.clone()));
130
131                if variadic_arg.is_some() {
132                    args.pop();
133                }
134
135                Type::Fn {
136                    args,
137                    variadic_arg,
138                    returns: Box::new(builtin_fn.return_type.clone()),
139                }
140            }
141            Value::Bool(_) => Type::Bool,
142            Value::Type(ty) => *ty.clone(),
143        }
144    }
145}
146
147impl From<BuiltinFn<'static>> for Type {
148    fn from(value: BuiltinFn) -> Self {
149        let args: Vec<Type> = value
150            .args
151            .iter()
152            .filter(|x| !x.variadic)
153            .map(|x| x.ty.clone())
154            .collect();
155        let varg = value
156            .args
157            .iter()
158            .find(|x| x.variadic)
159            .map(|x| Box::new(x.ty.clone()));
160        let returns = value.return_type.clone();
161
162        Self::Fn {
163            args,
164            variadic_arg: varg,
165            returns: returns.into(),
166        }
167    }
168}
169
170#[cfg(test)]
171mod from_tests {
172    use crate::prelude::BuiltinFn;
173
174    use super::*;
175
176    #[test]
177    fn test_from_type_value() {
178        let type_value = Value::Type(Type::String.into());
179        let ty: Type = type_value.into();
180        assert_eq!(Type::String, ty);
181    }
182
183    #[test]
184    fn test_get_type_type_value() {
185        let string_value = Value::Type(Type::String.into());
186        let ty: Type = string_value.get_type();
187        assert_eq!(Type::String, ty);
188    }
189
190    #[test]
191    fn test_from_string_value() {
192        let string_value = Value::String("test".to_string());
193        let ty: Type = string_value.into();
194        assert_eq!(Type::String, ty);
195    }
196
197    #[test]
198    fn test_get_type_string_value() {
199        let string_value = Value::String("test".to_string());
200        let ty: Type = string_value.get_type();
201        assert_eq!(Type::String, ty);
202    }
203
204    #[test]
205    fn test_from_bool_value() {
206        let bool_value = Value::Bool(true);
207        let ty: Type = bool_value.into();
208        assert_eq!(Type::Bool, ty);
209    }
210
211    #[test]
212    fn test_get_type_bool_value() {
213        let bool_value = Value::Bool(true);
214        let ty: Type = bool_value.get_type();
215        assert_eq!(Type::Bool, ty);
216    }
217
218    #[test]
219    fn test_from_fn_value() {
220        let builtin_fn = Value::Fn(BuiltinFn::ID.into());
221
222        let ty: Type = builtin_fn.into();
223
224        assert_eq!(
225            Type::Fn {
226                args: vec![Type::Value],
227                variadic_arg: None,
228                returns: Box::new(Type::Value),
229            },
230            ty
231        );
232    }
233
234    #[test]
235    fn test_get_type_fn_value() {
236        let builtin_fn = Value::Fn(BuiltinFn::ID.into());
237
238        let ty: Type = builtin_fn.get_type();
239
240        assert_eq!(
241            Type::Fn {
242                args: vec![Type::Value],
243                variadic_arg: None,
244                returns: Box::new(Type::Value),
245            },
246            ty
247        );
248    }
249}
250
251#[cfg(test)]
252mod name_and_display_tests {
253    use super::*;
254
255    #[test]
256    fn test_name_value() {
257        assert_eq!("Value", Type::Value.name());
258    }
259
260    #[test]
261    fn test_name_string() {
262        assert_eq!("String", Type::String.name());
263    }
264
265    #[test]
266    fn test_name_fn_0_args() {
267        assert_eq!(
268            "Fn() -> String",
269            Type::Fn {
270                args: vec![],
271                variadic_arg: None,
272                returns: Type::String.into(),
273            }
274            .name()
275        );
276    }
277
278    #[test]
279    fn test_name_fn_0_args_and_varadic() {
280        assert_eq!(
281            "Fn(...Value) -> String",
282            Type::Fn {
283                args: vec![],
284                variadic_arg: Some(Type::Value.into()),
285                returns: Type::String.into(),
286            }
287            .name()
288        );
289    }
290
291    #[test]
292    fn test_name_fn_1_args() {
293        assert_eq!(
294            "Fn(Value) -> String",
295            Type::Fn {
296                args: vec![Type::Value],
297                variadic_arg: None,
298                returns: Type::String.into(),
299            }
300            .name()
301        );
302    }
303
304    #[test]
305    fn test_name_fn_2_args() {
306        assert_eq!(
307            "Fn(Value, String) -> String",
308            Type::Fn {
309                args: vec![Type::Value, Type::String],
310                variadic_arg: None,
311                returns: Type::String.into(),
312            }
313            .name()
314        );
315    }
316
317    #[test]
318    fn test_name_fn_2_args_and_varadic() {
319        assert_eq!(
320            "Fn(Value, String, ...Value) -> String",
321            Type::Fn {
322                args: vec![Type::Value, Type::String],
323                variadic_arg: Some(Type::Value.into()),
324                returns: Type::String.into(),
325            }
326            .name()
327        );
328    }
329
330    #[test]
331    fn test_name_bool() {
332        assert_eq!("Bool", Type::Bool.name());
333    }
334}
335
336#[cfg(test)]
337mod from_string_tests {
338    use super::*;
339
340    use pretty_assertions::assert_eq;
341
342    #[test]
343    fn from_string_to_string() {
344        let ty = Type::from("String");
345
346        assert_eq!(Type::String, ty);
347    }
348
349    #[test]
350    fn from_string_to_bool() {
351        let ty = Type::from("Bool");
352
353        assert_eq!(Type::Bool, ty);
354    }
355
356    #[test]
357    fn from_string_to_value() {
358        let ty = Type::from("Value");
359
360        assert_eq!(Type::Value, ty);
361    }
362
363    #[test]
364    fn from_string_to_unknown() {
365        let ty = Type::from("Unknown");
366
367        assert_eq!(Type::Unknown, ty);
368    }
369
370    #[test]
371    fn from_string_to_type_string() {
372        let ty = Type::from("Type<String>");
373
374        assert_eq!(Type::Type(Type::String.into()), ty);
375    }
376
377    #[test]
378    fn from_string_to_type_fn_zero_args() {
379        let ty = Type::from("Fn() -> Value");
380
381        assert_eq!(
382            Type::Fn {
383                args: vec![],
384                variadic_arg: None,
385                returns: Type::Value.into()
386            },
387            ty
388        );
389    }
390
391    #[test]
392    fn from_string_to_type_fn_single_arg() {
393        let ty = Type::from("Fn(String) -> Value");
394
395        assert_eq!(
396            Type::Fn {
397                args: vec![Type::String],
398                variadic_arg: None,
399                returns: Type::Value.into()
400            },
401            ty
402        );
403    }
404
405    #[test]
406    fn from_string_to_type_fn_single_varg() {
407        let ty = Type::from("Fn(...String) -> Value");
408
409        assert_eq!(
410            Type::Fn {
411                args: vec![],
412                variadic_arg: Some(Type::String.into()),
413                returns: Type::Value.into()
414            },
415            ty
416        );
417    }
418
419    #[test]
420    fn from_string_to_type_fn_multiple_args() {
421        let ty = Type::from("Fn(String, Bool, String) -> String");
422
423        assert_eq!(
424            Type::Fn {
425                args: vec![Type::String, Type::Bool, Type::String],
426                variadic_arg: None,
427                returns: Type::String.into()
428            },
429            ty
430        );
431    }
432
433    #[test]
434    fn from_string_to_type_fn_multiple_args_and_varg() {
435        let ty = Type::from("Fn(String, Bool, String, ...Value) -> String");
436
437        assert_eq!(
438            Type::Fn {
439                args: vec![Type::String, Type::Bool, Type::String],
440                variadic_arg: Some(Type::Value.into()),
441                returns: Type::String.into()
442            },
443            ty
444        );
445    }
446}