aboutsummaryrefslogtreecommitdiff
path: root/src/sexp/util.rs
blob: 507455f17159e38c40e3ea5477ac988783496dd1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

use std::iter;

use crate::sexp::SExp;
use crate::sexp::SLeaf;
use crate::sexp::SExp::*;
use crate::sexp::SLeaf::*;

impl From<i32> for Box<SExp> {
    fn from(int: i32) -> Self {
        Box::new(Atom(Int(int)))
    }
}

impl From<bool> for SExp {
    fn from(bl: bool) -> Self {
        Atom( if bl { True } else { False } )
    }
}

impl From<SLeaf> for Box<SExp> {
    fn from(leaf: SLeaf) -> Self {
        Box::new(Atom(leaf))
    }
}

impl From<i32> for SLeaf {
    fn from(int: i32) -> Self {
        Int(int)
    }
}

pub fn scons(x: impl Into<Box<SExp>>, y: impl Into<Box<SExp>>) -> SExp {
    SCons(x.into(), y.into())
}

pub fn var(name: &str) -> SExp {
    Atom(Var(name.to_string()))
}

impl SExp {
    /// Transforms this SExp into a Vec<SLeaf>.
    ///
    /// Errors if the left-hand side of SCons has an SCons,
    /// or if the passed SExp is an Atom, or if the
    /// lists aren't Nil-terminated.
    ///
    /// ```rust
    /// use myslip::sexp::{SExp::*, SLeaf::*, util::*};
    /// assert_eq!(
    ///     scons(1, scons(2, scons(3, Nil))).into_vec(),
    ///     Ok(vec![Int(1), Int(2), Int(3)])
    /// );
    /// assert!(scons(scons(1, Nil), Nil).into_vec().is_err());
    /// assert!(scons(1, 2).into_vec().is_err());
    /// ```
    pub fn into_vec(self) -> Result<Vec<SLeaf>, String> {
        match self {
            SCons(a, b) => match (*a, *b) {
                (Atom(a), Atom(Nil)) => Ok(iter::once(a).collect::<Vec<SLeaf>>()),
                (Atom(a), b) => Ok(iter::once(a).chain(b.into_vec()?).collect::<Vec<SLeaf>>()),
                (a, b) => Err(format!("invalid list structure: {:?}", scons(a, b)))
            },
            _ => Err("expected list, found atom".to_string()),
        }
    }

    pub fn parts(self) -> Vec<SExp> {
        match self {
            Atom(x) => vec![Atom(x)],
            SCons(a, b) if *b == Atom(Nil) => vec![*a],
            SCons(a, b) => {
                let mut res = vec![*a];
                res.extend_from_slice(&(*b).parts());
                res
            },
        }
    }

    pub fn check_let(self) -> Option<(String, SExp)> {
        match &(self.parts())[..] {
            [l, n, v] if *l == Atom(Let) => match (n.clone(), v.clone()) {
                (Atom(Var(name)), val) => Some((name, val)),
                _ => None
            },
            _ => None
        }
    }

    pub fn is_print(&self) -> bool {
        match self {
            SCons(op, _) if **op == Atom(Print) => true,
            _ => false
        }
    }
}