turtle/interpreter/values/
operator.rs

1use crate::{
2    exp, exp_assert, parse, resolve_resource, CallSnapshot, Environment, Exception,
3    ExceptionValue as EV, Expression, Value,
4};
5use regex::Regex;
6use std::fmt;
7
8use crate::Locker;
9
10#[derive(Debug, Clone, PartialEq, PartialOrd)]
11pub enum Operator {
12    Quote,
13    Atom,
14    Eq,
15    Car,
16    Cdr,
17    Cons,
18    Cond,
19    Export,
20    Let,
21    Sum,
22    Prod,
23    Exp,
24    Modulo,
25    Gt,
26    Ge,
27    Type,
28    Disp,
29    Import,
30    Eval,
31    While,
32    Lambda,
33    Macro,
34    List,
35    Catch,
36    Throw,
37    Format,
38    Parse,
39    Length,
40    Append,
41    Do,
42    Floor,
43    Rand,
44    Equiv,
45}
46
47impl fmt::Display for Operator {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        write!(f, "{}", format!("{:?}", self).to_lowercase().as_str())
50    }
51}
52
53impl Operator {
54    pub fn apply(
55        &self,
56        snapshot: Locker<CallSnapshot>,
57        arguments: Vec<&Expression>,
58        expr: &Expression,
59        env: Locker<Environment>,
60    ) -> Result<Expression, Exception> {
61        use crate::Operator::*;
62        use Value::*;
63
64        let snap = || snapshot.clone();
65
66        match self {
67            Quote => {
68                if arguments.len() != 1 {
69                    exp!(
70                        EV::ArgumentMismatch(arguments.len(), "1".to_string()),
71                        snapshot
72                    );
73                }
74                let arg = *arguments.get(0).unwrap();
75                Ok(arg.clone())
76            }
77            Atom => {
78                exp_assert!(
79                    arguments.len() == 1,
80                    EV::ArgumentMismatch(arguments.len(), "1".to_string()),
81                    snapshot
82                );
83                match arguments.get(0).unwrap().eval(snapshot, env)?.into_value() {
84                    Value::List(_) => Ok(Expression::new(Value::List(vec![]))),
85                    _ => Ok(Expression::new(Value::True)),
86                }
87            }
88            Eq => {
89                exp_assert!(
90                    arguments.len() > 1,
91                    EV::ArgumentMismatch(arguments.len(), "2+".to_string()),
92                    snap()
93                );
94
95                let mut prev: Option<Expression> = None;
96                for argument in arguments {
97                    let evaled = argument.eval(snap(), env.clone())?;
98                    match &prev {
99                        None => prev = Some(evaled),
100                        Some(val) => match (evaled.get_value(), val.get_value()) {
101                            (Value::List(l1), Value::List(l2)) => {
102                                if !(l1.is_empty() && l2.is_empty()) {
103                                    return Ok(Expression::nil());
104                                }
105                            }
106                            (v1, v2) => {
107                                if v1 != v2 {
108                                    return Ok(Expression::nil());
109                                }
110                            }
111                        },
112                    }
113                }
114                Ok(Expression::t())
115            }
116            Car => {
117                exp_assert!(
118                    arguments.len() == 1,
119                    EV::ArgumentMismatch(arguments.len(), "1".to_string()),
120                    snap()
121                );
122
123                let list = arguments.get(0).unwrap().eval(snap(), env)?;
124
125                match list.into_value() {
126                    Value::List(mut vals) => {
127                        exp_assert!(
128                            !vals.is_empty(),
129                            EV::InvalidArgument,
130                            snap(),
131                            "cannot `car` an empty list (nil)".to_string()
132                        );
133                        Ok(vals.remove(0))
134                    }
135                    val => exp!(
136                        EV::InvalidArgument,
137                        snap(),
138                        format!("`car` expects a list, got `{}`", val)
139                    ),
140                }
141            }
142            Cdr => {
143                exp_assert!(
144                    arguments.len() == 1,
145                    EV::ArgumentMismatch(arguments.len(), "1".to_string()),
146                    snap()
147                );
148                let list = arguments.get(0).unwrap().eval(snap(), env)?;
149                match list.into_value() {
150                    Value::List(mut vals) => {
151                        if !vals.is_empty() {
152                            vals.remove(0);
153                        }
154                        Ok(Expression::new(Value::List(vals)))
155                    }
156                    val => exp!(
157                        EV::InvalidArgument,
158                        snap(),
159                        format!("`cdr` expects a list, not `{}`", val)
160                    ),
161                }
162            }
163            Cons => {
164                exp_assert!(
165                    arguments.len() == 2,
166                    EV::ArgumentMismatch(arguments.len(), "2".to_string()),
167                    snap()
168                );
169                let first = arguments.get(0).unwrap().eval(snap(), env.clone())?;
170                let list = arguments.get(1).unwrap().eval(snap(), env)?;
171                match list.into_value() {
172                    Value::List(mut vals) => {
173                        vals.insert(0, first);
174                        Ok(Expression::new(Value::List(vals)))
175                    }
176                    val => exp!(
177                        EV::InvalidArgument,
178                        snap(),
179                        format!(
180                            "`cons` expects a list as its second argument, got `{}`",
181                            val
182                        )
183                    ),
184                }
185            }
186            Cond => {
187                for argument in &arguments {
188                    match argument.get_value() {
189                        Value::List(elems) => {
190                            exp_assert!(
191                                elems.len() == 2,
192                                EV::InvalidArgument,
193                                snap(),
194                                format!(
195                                    "each `cond` condition must be a list of length two (the given list has {} elements)",
196                                    elems.len()
197                                )
198                            );
199                            let cond = { elems.get(0).unwrap() };
200                            if cond.eval(snap(), env.clone())? != Expression::nil() {
201                                let val = { elems.get(1).unwrap() };
202                                return val.eval(snapshot, env);
203                            }
204                        }
205                        val => exp!(
206                            EV::InvalidArgument,
207                            snap(),
208                            format!("`cond` must be called on a list, got `{}`", val)
209                        ),
210                    }
211                }
212                Ok(Expression::nil())
213            }
214            Export | Let => {
215                exp_assert!(
216                    arguments.len() == 2,
217                    EV::ArgumentMismatch(arguments.len(), "2".to_string()),
218                    snap()
219                );
220                let sym_exp = arguments.get(0).unwrap().eval(snap(), env.clone())?;
221                let symbol = match sym_exp.into_value() {
222                    Symbol(sym) => sym,
223                    other => exp!(
224                        EV::InvalidArgument,
225                        snap(),
226                        format!(
227                            "first arg of label must evaluate to a symbol (received `{}`)",
228                            other
229                        )
230                    ),
231                };
232
233                let assigned_expr = arguments.get(1).unwrap().eval(snap(), env.clone())?;
234                env.write().unwrap().assign(
235                    symbol,
236                    assigned_expr.clone(),
237                    !matches!(self, Export),
238                    snap(),
239                )?;
240                Ok(assigned_expr)
241            }
242            Sum => {
243                let mut sum = 0.0;
244                for arg in arguments {
245                    match arg.eval(snap(), env.clone())?.into_value() {
246                        Number(val) => sum += val,
247                        val => exp!(
248                            EV::InvalidArgument,
249                            snap(),
250                            format!("`sum` expects numbers as its arguments (got `{}`)", val)
251                        ),
252                    }
253                }
254                Ok(Expression::new(Value::Number(sum)))
255            }
256            Prod => {
257                let mut prod = 1.0;
258                for arg in arguments {
259                    match arg.eval(snap(), env.clone())?.into_value() {
260                        Number(val) => prod *= val,
261                        val => exp!(
262                            EV::InvalidArgument,
263                            snap(),
264                            format!("`prod` expects numbers as its arguments (got `{}`)", val)
265                        ),
266                    }
267                }
268                Ok(Expression::new(Value::Number(prod)))
269            }
270            Exp => {
271                exp_assert!(
272                    arguments.len() == 2,
273                    EV::ArgumentMismatch(arguments.len(), "2".to_string()),
274                    snap()
275                );
276                let base = arguments
277                    .get(0)
278                    .unwrap()
279                    .eval(snap(), env.clone())?
280                    .into_value();
281                let exp = arguments.get(1).unwrap().eval(snap(), env)?.into_value();
282                match (base, exp) {
283                    (Number(base), Number(exp)) => {
284                        Ok(Expression::new(Value::Number(base.powf(exp))))
285                    }
286                    (base, exp) => exp!(
287                        EV::InvalidArgument,
288                        snap(),
289                        format!(
290                            "`exp` requires its arguments to be both numeric (got `{}` and `{}`)",
291                            base, exp
292                        )
293                    ),
294                }
295            }
296            Modulo => {
297                exp_assert!(
298                    arguments.len() == 2,
299                    EV::ArgumentMismatch(arguments.len(), "2".to_string()),
300                    snap()
301                );
302                let val = arguments
303                    .get(0)
304                    .unwrap()
305                    .eval(snap(), env.clone())?
306                    .into_value();
307                let modu = arguments.get(1).unwrap().eval(snap(), env)?.into_value();
308                match (val, modu) {
309                    (Number(first), Number(second)) => {
310                        Ok(Expression::new(Value::Number(first % second)))
311                    }
312                    (base, exp) => exp!(
313                        EV::InvalidArgument,
314                        snap(),
315                        format!(
316                        "`modulo` requires its arguments to be both numeric (got `{}` and `{}`)",
317                        base, exp)
318                    ),
319                }
320            }
321            Gt => {
322                let mut args_evaled = Vec::with_capacity(arguments.len());
323                for arg in arguments {
324                    args_evaled.push(arg.eval(snap(), env.clone())?);
325                }
326                match args_evaled
327                    .iter()
328                    .skip(1)
329                    .zip(args_evaled.iter())
330                    .all(|(g, l)| g > l)
331                {
332                    true => Ok(Expression::t()),
333                    false => Ok(Expression::nil()),
334                }
335            }
336            Ge => {
337                let mut args_evaled = Vec::with_capacity(arguments.len());
338                for arg in arguments {
339                    args_evaled.push(arg.eval(snap(), env.clone())?);
340                }
341                match args_evaled
342                    .iter()
343                    .skip(1)
344                    .zip(args_evaled.iter())
345                    .all(|(g, l)| g >= l)
346                {
347                    true => Ok(Expression::t()),
348                    false => Ok(Expression::nil()),
349                }
350            }
351            Type => {
352                exp_assert!(
353                    arguments.len() == 1,
354                    EV::ArgumentMismatch(arguments.len(), "1".to_string()),
355                    snap()
356                );
357                let arg_type = arguments
358                    .get(0)
359                    .unwrap()
360                    .eval(snap(), env)?
361                    .into_value()
362                    .as_type();
363                Ok(Expression::new(arg_type))
364            }
365            Disp => {
366                for arg in arguments {
367                    println!("{}", arg.eval(snap(), env.clone())?);
368                }
369                Ok(Expression::nil())
370            }
371            Import => {
372                if !(arguments.len() == 1 || arguments.len() == 2) {
373                    exp!(
374                        EV::ArgumentMismatch(arguments.len(), "1 or 2".to_string()),
375                        snapshot
376                    );
377                }
378                let path = match arguments.get(0).unwrap().eval(snap(), env.clone())?.into_value() {
379                    Text(val) => val,
380                    val => exp!(
381                        EV::InvalidArgument,
382                        snapshot,
383                        format!(
384                            "`import` requires the path (:text) as its first argument (got `{}` instead)",
385                            val
386                        )
387                    ),
388                };
389
390                let namespace = match arguments.get(1) {
391                    Some(val) => match val.eval(snap(), env.clone())?.into_value() {
392                        Keyword(val) => Some(val.string_value().clone()),
393                        val => exp!(
394                            EV::InvalidArgument,
395                            snapshot,
396                            format!(
397                                "`import` requires the namespace (:keyword) as its second argument (got `{}` instead)",
398                                val
399                            )
400                        ),
401                    },
402                    None => None
403                };
404
405                let imported_env = Locker::new(Environment::root());
406                let exp = resolve_resource(&path, snapshot, expr, imported_env.clone())?;
407                env.write().unwrap().add_parent(imported_env, namespace);
408                Ok(exp)
409            }
410            Eval => {
411                if arguments.len() != 1 {
412                    exp!(
413                        EV::ArgumentMismatch(arguments.len(), "1".to_string()),
414                        snapshot
415                    );
416                }
417                arguments
418                    .get(0)
419                    .unwrap()
420                    .eval(snap(), env.clone())?
421                    .eval(snap(), env)
422            }
423            While => {
424                exp_assert!(
425                    arguments.len() >= 2,
426                    EV::ArgumentMismatch(arguments.len(), "2+".to_string()),
427                    snapshot
428                );
429                let condition = arguments.get(0).unwrap();
430                let mut result = Expression::nil();
431                while condition.eval(snap(), env.clone())? != Expression::nil() {
432                    for action in arguments.iter().skip(1) {
433                        result = action.eval(snap(), env.clone())?
434                    }
435                }
436                Ok(result)
437            }
438            crate::Operator::Lambda | crate::Operator::Macro => {
439                exp_assert!(
440                    arguments.len() >= 2,
441                    EV::ArgumentMismatch(arguments.len(), "2+".to_string()),
442                    snapshot
443                );
444
445                let mut collapse_input = true;
446                let func_args = match arguments
447                    .get(0)
448                    .unwrap()
449                    .eval(snap(), env.clone())?
450                    .into_value()
451                {
452                    Value::List(vals) => {
453                        collapse_input = false;
454                        let mut symbols = Vec::new();
455                        for val in vals {
456                            match val.into_value() {
457                                Value::Symbol(sym) => symbols.push(sym),
458                                other => exp!(EV::InvalidArgument, snapshot, format!("each item in the first argument (a list) must be a symbol (got `{}`)", other)),
459                            }
460                        }
461                        symbols
462                    }
463                    Value::Symbol(sym) => vec![sym],
464                    val => exp!(
465                        EV::InvalidArgument,
466                        snapshot,
467                        format!(
468                            "the first argument must only evaluate to symbol(s) (got `{}`)",
469                            val
470                        )
471                    ),
472                };
473                let mut func_expressions = Vec::new();
474                for arg_expr in arguments.iter().skip(1) {
475                    func_expressions.push(arg_expr.eval(snap(), env.clone())?);
476                }
477
478                (match self {
479                    crate::Operator::Lambda => {
480                        Expression::new(Value::Lambda(crate::Function::new(
481                            func_args,
482                            func_expressions,
483                            collapse_input,
484                            env.clone(),
485                        )))
486                    }
487                    crate::Operator::Macro => Expression::new(Value::Macro(crate::Function::new(
488                        func_args,
489                        func_expressions,
490                        collapse_input,
491                        env.clone(),
492                    ))),
493                    _ => unreachable!(),
494                })
495                .eval(snap(), env)
496            }
497            crate::Operator::List => {
498                let mut args_evaled = Vec::new();
499                for argument in arguments {
500                    args_evaled.push(argument.eval(snap(), env.clone())?);
501                }
502                Ok(Expression::new(Value::List(args_evaled)))
503            }
504            Catch => {
505                exp_assert!(
506                    arguments.len() == 2,
507                    EV::ArgumentMismatch(arguments.len(), "2".to_string()),
508                    snapshot
509                );
510                let action = arguments.get(0).unwrap().eval(snap(), env.clone());
511                let catch_func = arguments.get(1).unwrap().eval(snap(), env.clone())?;
512                match action {
513                    Ok(exp) => Ok(exp),
514                    Err(err) => {
515                        // TODO: remove extra clone
516                        match catch_func.get_value() {
517                            Value::Lambda{..} => Expression::new(Value::List(vec![catch_func.clone(), err.into_value().into_expression()])).eval(snapshot, Locker::new(Environment::root().with_parent(env, None))),
518                            _ => exp!(
519                                EV::InvalidArgument,
520                                snapshot,
521                                format!("the second argument of `catch` must be a lambda expression (got `{}`)", catch_func)
522                            )
523                        }
524                    }
525                }
526            }
527            Throw => {
528                exp_assert!(
529                    arguments.len() == 1,
530                    EV::ArgumentMismatch(arguments.len(), "1".to_string()),
531                    snapshot
532                );
533                Err(Exception::new(
534                    EV::Other(arguments.get(0).unwrap().eval(snap(), env)?),
535                    Some(snap()),
536                    None,
537                ))
538            }
539            Format => {
540                exp_assert!(
541                    !arguments.is_empty(),
542                    EV::ArgumentMismatch(arguments.len(), "1+".to_string()),
543                    snapshot
544                );
545                let mut literal = match arguments
546                    .get(0)
547                    .unwrap()
548                    .eval(snap(), env.clone())?
549                    .into_value()
550                {
551                    Text(value) => value,
552                    other => return Ok(Expression::new(Value::Text(format!("{}", other)))),
553                };
554                let placeholder = Regex::new(r"\{\}").unwrap(); // todo: make lazy_static?
555                let interpolations: Vec<regex::Match> = placeholder.find_iter(&literal).collect();
556                exp_assert!(
557                    arguments.len() == interpolations.len() + 1,
558                    EV::ArgumentMismatch(arguments.len(), format!("{}", interpolations.len() + 1)),
559                    snapshot,
560                    format!("`{}` has {} placeholders, so {} total arguments are necessary (including the first string literal)", literal, interpolations.len(), interpolations.len() + 1)
561                );
562                for i in 1..arguments.len() {
563                    let replace_with =
564                        format!("{}", arguments.get(i).unwrap().eval(snap(), env.clone())?);
565                    literal = String::from(placeholder.replace(&literal, replace_with.as_str()));
566                }
567                Ok(Expression::new(Value::Text(literal)))
568            }
569            Parse => {
570                exp_assert!(
571                    arguments.len() == 1,
572                    EV::ArgumentMismatch(arguments.len(), "1".to_string()),
573                    snapshot
574                );
575                let value_str = match arguments.get(0).unwrap().eval(snap(), env)?.into_value() {
576                    Text(value) => value,
577                    other => exp!(
578                        EV::InvalidArgument,
579                        snapshot,
580                        format!("the argument of `parse` must be text (got `{}`)", other)
581                    ),
582                };
583                let mut values = parse(&value_str, "<parse>")?;
584                exp_assert!(
585                    values.len() == 1,
586                    EV::InvalidArgument,
587                    snapshot,
588                    format!(
589                        "`parse` can only handle a single value, not {}",
590                        values.len()
591                    )
592                );
593                Ok(values.remove(0))
594            }
595            Length => {
596                exp_assert!(
597                    arguments.len() == 1,
598                    EV::ArgumentMismatch(arguments.len(), "1".to_string()),
599                    snapshot
600                );
601                match arguments.get(0).unwrap().eval(snap(), env)?.into_value() {
602                    Value::List(vals) => Ok(Expression::new(Value::Number(vals.len() as f64))),
603                    other => exp!(
604                        EV::InvalidArgument,
605                        snapshot,
606                        format!(
607                            "length expects a list as its first argument (got `{}`)",
608                            other
609                        )
610                    ),
611                }
612            }
613            Append => {
614                exp_assert!(
615                    !arguments.is_empty(),
616                    EV::ArgumentMismatch(arguments.len(), "1+".to_string()),
617                    snapshot
618                );
619                let mut new_list: Vec<Expression> = Vec::with_capacity(arguments.len());
620                for argument in arguments {
621                    match argument.eval(snap(), env.clone())?.into_value() {
622                        Value::List(values) => new_list.extend(values),
623                        other => exp!(
624                            EV::InvalidArgument,
625                            snapshot,
626                            format!(
627                                "append requires all its arguments to be a list (got `{}`)",
628                                other
629                            )
630                        ),
631                    }
632                }
633                Ok(Expression::new(Value::List(new_list)))
634            }
635            Do => {
636                exp_assert!(
637                    !arguments.is_empty(),
638                    EV::ArgumentMismatch(arguments.len(), "1+".to_string()),
639                    snapshot
640                );
641
642                let mut result = Expression::nil();
643                for argument in arguments {
644                    result = argument.eval(snap(), env.clone())?;
645                }
646                Ok(result)
647            }
648            Floor => {
649                exp_assert!(
650                    arguments.len() == 1,
651                    EV::ArgumentMismatch(arguments.len(), "1".to_string()),
652                    snapshot
653                );
654                match arguments
655                    .get(0)
656                    .unwrap()
657                    .eval(snap(), env.clone())?
658                    .into_value()
659                {
660                    Number(val) => Ok(Expression::new(Value::Number(val.floor()))),
661                    val => exp!(
662                        EV::InvalidArgument,
663                        snap(),
664                        format!("`floor` expects a number as its argument (got `{}`)", val)
665                    ),
666                }
667            }
668            Rand => {
669                exp_assert!(
670                    arguments.len() == 0,
671                    EV::ArgumentMismatch(arguments.len(), "0".to_string()),
672                    snapshot
673                );
674                Ok(Expression::new(Value::Number(rand::random())))
675            }
676            Equiv => {
677                exp_assert!(
678                    arguments.len() >= 2,
679                    EV::ArgumentMismatch(arguments.len(), "2+".to_string()),
680                    snapshot
681                );
682                let equals = arguments.get(0).unwrap().eval(snap(), env.clone())?;
683                for i in arguments.iter().skip(1) {
684                    if i.eval(snap(), env.clone())? != equals {
685                        return Ok(Expression::nil());
686                    }
687                }
688                return Ok(Expression::t());
689            }
690        }
691    }
692}