aboutsummaryrefslogtreecommitdiff
path: root/src/sexp/case.rs
blob: 40310e46728adc4c79c76e861256f8d06ee35948 (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

use crate::sexp::{SExp, SExp::*, SLeaf::*};

impl SExp {
    /**
     * Matches this expression against the given pattern,
     * that may contain RestPats and variables.
     * upon successful match, variable bindings are returned.
     *
     * Two equal values match:
     * ```rust
     * use myslip::sexp::{SExp::*, SLeaf::*, util::*};
     * assert_eq!(scons(1, Nil).matches_pat(&scons(1, Nil)), Some(vec![]));
     * ```
     * A variable (wildcard) pattern matches the whole expression:
     * ```rust
     * use myslip::sexp::{SExp::*, SLeaf::*, util::*};
     * 
     * assert_eq!(
     *     scons(1, scons(scons(2, scons(3, Nil)), Nil))
     *         .matches_pat(&var("x")),
     *     Some(vec![(
     *         "x".to_string(),
     *         scons(1, scons(scons(2, scons(3, Nil)), Nil))
     *     )])
     * );
     * ```
     * If a variable has a similar place in structure, it matches
     * the corresponding part:
     * ```rust
     * use myslip::sexp::{SExp::*, SLeaf::*, util::*};
     * 
     * assert_eq!(
     *     scons(1, scons(scons(2, scons(3, Nil)), Nil))
     *         .matches_pat(&scons(var("x"), scons(var("y"), Nil))),
     *     Some(vec![
     *         ("x".to_string(), Atom(Int(1))),
     *         ("y".to_string(), scons(2, scons(3, Nil)))
     *     ])
     * );
     * ```
     * Rest patterns:
     * ```rust
     * use myslip::sexp::{SExp::*, SLeaf::*, util::*};
     * 
     * assert_eq!(
     *     scons(1, scons(2, scons(3, Nil)))
     *         .matches_pat(&scons(var("x"), scons(RestPat("y".to_string()), Nil))),
     *     Some(vec![
     *         ("x".to_string(), Atom(Int(1))),
     *         ("y".to_string(), scons(2, scons(3, Nil)))
     *     ])
     * );
     * ```
     */
    pub fn matches_pat(&self, pat: &SExp) -> Option<Vec<(String, SExp)>> {
	match (self, pat) {
	    
	    (a, b) if a == b => Some(vec![]),

	    (a, Atom(Var(name))) => Some(vec![(name.clone(), a.clone())]),

	    (SCons(a1, a2), SCons(b1, b2)) => {
		let lefthand = a1.matches_pat(b1);
		let checkres = (*b2).check_res_pat();
		let righthand = match checkres {
		    Some(name) => Some(vec![(name, *a2.clone())]),
		    None => a2.matches_pat(b2),
		};
		lefthand.zip(righthand)
		    .map(|vs| vs.0.into_iter().chain(vs.1).collect())
	    },

	    _ => None,

	}
    }

    fn check_res_pat(&self) -> Option<String> {
	match self {
	    SCons(a, b) => match (*a.clone(), *b.clone()) {
		(Atom(RestPat(s)), Atom(Nil)) => Some(s),
		_ => None
	    },
	    _ => None
	}
    }
}