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
 97
 98
 99
100
101
102
103
104
105
106
use std::cell::{Ref, RefCell, RefMut};
use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::rc::Rc;

use crate::eval::Env;
use crate::objects::{Datum, Eval, Foolang, Object, Vtable};
use crate::unwind::Unwind;

pub struct Dictionary {
    data: RefCell<HashMap<Object, Object>>,
}

impl Dictionary {
    pub fn borrow(&self) -> Ref<HashMap<Object, Object>> {
        self.data.borrow()
    }
    pub fn borrow_mut(&self) -> RefMut<HashMap<Object, Object>> {
        self.data.borrow_mut()
    }
}

impl PartialEq for Dictionary {
    fn eq(&self, other: &Self) -> bool {
        std::ptr::eq(self, other)
    }
}

impl Eq for Dictionary {}

impl Hash for Dictionary {
    fn hash<H: Hasher>(&self, state: &mut H) {
        std::ptr::hash(self, state);
    }
}

impl fmt::Debug for Dictionary {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{{")?;
        let mut first = true;
        for (k, v) in self.borrow().iter() {
            if first {
                first = false;
            } else {
                write!(f, ", ")?;
            }
            write!(f, "{:?} -> {:?}", k, v)?;
        }
        write!(f, "}}")
    }
}

pub fn class_vtable() -> Vtable {
    let vt = Vtable::new("class Dictionary");
    vt.add_primitive_method_or_panic("new", class_dictionary_new);
    vt
}

pub fn instance_vtable() -> Vtable {
    let vt = Vtable::new("Dictionary");
    vt.add_primitive_method_or_panic("at:", dictionary_at);
    vt.add_primitive_method_or_panic("put:at:", dictionary_put_at);
    vt
}

pub fn into_dictionary(foolang: &Foolang, data: HashMap<Object, Object>) -> Object {
    Object {
        vtable: Rc::clone(&foolang.dictionary_vtable),
        datum: Datum::Dictionary(Rc::new(Dictionary {
            data: RefCell::new(data),
        })),
    }
}

pub fn as_dictionary<'a>(obj: &'a Object, ctx: &str) -> Result<&'a Dictionary, Unwind> {
    match &obj.datum {
        Datum::Dictionary(ref dict) => Ok(dict),
        _ => Unwind::error(&format!("{:?} is not a Dictionary in {}", obj, ctx)),
    }
}

fn class_dictionary_new(_receiver: &Object, _args: &[Object], env: &Env) -> Eval {
    let data = RefCell::new(HashMap::new());
    Ok(Object {
        vtable: env.foo.dictionary_vtable.clone(),
        datum: Datum::Dictionary(Rc::new(Dictionary {
            data,
        })),
    })
}

fn dictionary_at(receiver: &Object, args: &[Object], env: &Env) -> Eval {
    match receiver.as_dictionary("in Dictionary#at:")?.borrow().get(&args[0]) {
        Some(obj) => Ok(obj.clone()),
        None => Ok(env.foo.make_boolean(false)),
    }
}

fn dictionary_put_at(receiver: &Object, args: &[Object], _env: &Env) -> Eval {
    receiver
        .as_dictionary("in Dictionary#put:at:")?
        .borrow_mut()
        .insert(args[1].clone(), args[0].clone());
    Ok(receiver.clone())
}