1use core::fmt;
2use std::fmt::Display;
3
4use crate::{types::Type, value::Value};
5
6#[derive(Clone)]
7pub struct FnArg {
8 pub name: String,
9 pub ty: Type,
10 pub variadic: bool,
11}
12
13impl FnArg {
14 pub fn new(name: &str, ty: Type) -> Self {
15 Self {
16 name: String::from(name),
17 ty,
18 variadic: false,
19 }
20 }
21
22 pub fn new_varadic(name: &str, ty: Type) -> Self {
23 Self {
24 name: String::from(name),
25 ty,
26 variadic: true,
27 }
28 }
29}
30
31pub struct BuiltinFn {
33 pub name: String,
35 pub args: Vec<FnArg>,
37 pub return_type: Type,
38 pub func: std::rc::Rc<dyn Fn(Vec<Value>) -> Value>,
40}
41
42impl BuiltinFn {
43 pub fn arity(&self) -> u8 {
44 let len = self.args.len() as u8;
45
46 if self.is_variadic() { len - 1 } else { len }
47 }
48
49 pub fn is_variadic(&self) -> bool {
50 self.args.last().map(|arg| arg.variadic).unwrap_or(false)
51 }
52
53 pub fn arity_matches(&self, arity: u8) -> bool {
54 if self.is_variadic() {
55 self.arity() <= arity
56 } else {
57 self.arity() == arity
58 }
59 }
60}
61
62impl PartialEq for BuiltinFn {
63 fn eq(&self, other: &Self) -> bool {
64 self.name == other.name
65 }
66}
67
68impl Display for BuiltinFn {
69 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70 let name = &self.name;
71 let args: Vec<String> = self
72 .args
73 .iter()
74 .map(|arg| {
75 let prefix: &str = if arg.variadic { "..." } else { "" };
76
77 format!("{prefix}{}: {}", arg.name.clone(), arg.ty.name())
78 })
79 .collect();
80
81 let args: String = args.join(", ");
82
83 let return_type: String = self.return_type.name().to_string();
84
85 write!(f, "{name}({args}) -> {return_type}")
86 }
87}
88
89impl fmt::Debug for BuiltinFn {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 let name = &self.name;
92 let args: Vec<String> = self
93 .args
94 .iter()
95 .map(|arg| {
96 let prefix: &str = if arg.variadic { "..." } else { "" };
97
98 format!("{prefix}{}: {}", arg.name.clone(), arg.ty.name())
99 })
100 .collect();
101
102 let args: String = args.join(", ");
103
104 let return_type: String = self.return_type.name().to_string();
105
106 write!(f, "{name}({args}) -> {return_type}")
107 }
108}
109
110#[derive(Debug, PartialEq)]
111pub enum FnArity {
112 N(u8),
113 Variadic { n: u8 },
114}
115
116pub struct BuiltinFns;
117
118impl BuiltinFns {
119 pub fn id(args: Vec<Value>) -> Value {
120 let arg = args.first().unwrap();
121
122 arg.clone()
123 }
124
125 pub fn noop(_: Vec<Value>) -> Value {
126 Value::String(String::from("noop"))
127 }
128
129 pub fn is_empty(args: Vec<Value>) -> Value {
130 let string_arg = args
131 .first()
132 .expect("should have string expression passed")
133 .get_string();
134
135 Value::Bool(string_arg.is_empty())
136 }
137
138 pub fn and(args: Vec<Value>) -> Value {
139 let a_arg = args
140 .first()
141 .expect("should have first expression passed")
142 .get_bool();
143 let b_arg = args
144 .get(1)
145 .expect("should have second expression passed")
146 .get_bool();
147
148 Value::Bool(a_arg && b_arg)
149 }
150
151 pub fn or(args: Vec<Value>) -> Value {
152 let a_arg = args
153 .first()
154 .expect("should have first expression passed")
155 .get_bool();
156 let b_arg = args
157 .get(1)
158 .expect("should have second expression passed")
159 .get_bool();
160
161 Value::Bool(a_arg || b_arg)
162 }
163
164 pub fn cond(args: Vec<Value>) -> Value {
165 let cond_arg = args
166 .first()
167 .expect("should have cond expression passed")
168 .get_bool();
169 let then_arg = args
170 .get(1)
171 .cloned()
172 .expect("should have then expression passed");
173 let else_arg = args
174 .get(2)
175 .cloned()
176 .expect("should have else expression passed");
177
178 if cond_arg { then_arg } else { else_arg }
179 }
180
181 pub fn to_str(args: Vec<Value>) -> Value {
182 let value_arg = args.first().expect("should have string expression passed");
183
184 match value_arg {
185 Value::String(_) => value_arg.clone(),
186 _ => Value::String(value_arg.to_string()),
187 }
188 }
189
190 pub fn concat(args: Vec<Value>) -> Value {
191 let mut result = String::new();
192
193 for arg in args {
194 let value = match arg {
195 Value::String(string) => string,
196 _ => arg.to_string(),
197 };
198
199 result.push_str(value.as_str());
200 }
201
202 Value::String(result)
203 }
204
205 pub fn contains(args: Vec<Value>) -> Value {
206 let needle_arg = args
207 .first()
208 .expect("should have first expression passed")
209 .get_string();
210 let haystack_arg = args
211 .get(1)
212 .expect("should have second expression passed")
213 .get_string();
214
215 Value::Bool(haystack_arg.contains(needle_arg))
216 }
217
218 pub fn trim(args: Vec<Value>) -> Value {
219 let string_arg = args
220 .first()
221 .expect("should have string expression passed")
222 .get_string();
223
224 Value::String(string_arg.trim().to_string())
225 }
226
227 pub fn trim_start(args: Vec<Value>) -> Value {
228 let string_arg = args
229 .first()
230 .expect("should have string expression passed")
231 .get_string();
232
233 Value::String(string_arg.trim_start().to_string())
234 }
235
236 pub fn trim_end(args: Vec<Value>) -> Value {
237 let string_arg = args
238 .first()
239 .expect("should have string expression passed")
240 .get_string();
241
242 Value::String(string_arg.trim_end().to_string())
243 }
244
245 pub fn lowercase(args: Vec<Value>) -> Value {
246 let string_arg = args
247 .first()
248 .expect("should have string expression passed")
249 .get_string();
250
251 Value::String(string_arg.to_lowercase().to_string())
252 }
253
254 pub fn uppercase(args: Vec<Value>) -> Value {
255 let string_arg = args
256 .first()
257 .expect("should have string expression passed")
258 .get_string();
259
260 Value::String(string_arg.to_uppercase().to_string())
261 }
262}
263
264#[cfg(test)]
265mod value_tests {
266 use std::rc::Rc;
267
268 use super::*;
269
270 #[test]
271 fn test_builtins_display_var_arity() {
272 assert_eq!(
273 "test_builtin(...rest: String) -> String",
274 format!(
275 "{}",
276 BuiltinFn {
277 name: "test_builtin".to_string(),
278 args: vec![FnArg::new_varadic("rest", Type::String)],
279 return_type: Type::String,
280 func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
281 }
282 )
283 )
284 }
285
286 #[test]
287 fn test_builtins_display_0_arity() {
288 assert_eq!(
289 "test_builtin() -> String",
290 format!(
291 "{}",
292 BuiltinFn {
293 name: "test_builtin".to_string(),
294 args: vec![],
295 return_type: Type::String,
296 func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
297 }
298 )
299 )
300 }
301
302 #[test]
303 fn test_builtins_debug_0_arity() {
304 assert_eq!(
305 "test_builtin() -> String",
306 format!(
307 "{:#?}",
308 BuiltinFn {
309 name: "test_builtin".to_string(),
310 args: vec![],
311 return_type: Type::String,
312 func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
313 }
314 )
315 )
316 }
317
318 #[test]
319 fn test_builtins_display_1_arity() {
320 assert_eq!(
321 "test_builtin(value: String) -> String",
322 format!(
323 "{}",
324 BuiltinFn {
325 name: "test_builtin".to_string(),
326 args: vec![FnArg::new("value", Type::String)],
327 return_type: Type::String,
328 func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
329 }
330 )
331 )
332 }
333
334 #[test]
335 fn test_builtins_debug_1_arity() {
336 assert_eq!(
337 "test_builtin(value: String) -> String",
338 format!(
339 "{:#?}",
340 BuiltinFn {
341 name: "test_builtin".to_string(),
342 args: vec![FnArg::new("value", Type::String)],
343 return_type: Type::String,
344 func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
345 }
346 )
347 )
348 }
349
350 #[test]
351 fn test_builtins_display_2_arity() {
352 assert_eq!(
353 "test_builtin(a: String, b: String) -> String",
354 format!(
355 "{}",
356 BuiltinFn {
357 name: "test_builtin".to_string(),
358 args: vec![FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
359 return_type: Type::String,
360 func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
361 }
362 )
363 )
364 }
365
366 #[test]
367 fn test_builtins_debug_2_arity() {
368 assert_eq!(
369 "test_builtin(a: String, b: String) -> String",
370 format!(
371 "{:#?}",
372 BuiltinFn {
373 name: "test_builtin".to_string(),
374 args: vec![FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
375 return_type: Type::String,
376 func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
377 }
378 )
379 )
380 }
381}