selfish 0.1.0

Lisp language for game scripting
Documentation
#[macro_use]
extern crate nom;

use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;

mod core;
mod reader;
mod eval;

pub use reader::Reader;
pub use core::read_eval;

pub use core::standart_environment;

#[derive(Clone, Debug, PartialEq)]
pub struct Value(Rc<Type>);

pub type List = Vec<Value>;

#[derive(Debug, PartialEq, Clone)]
pub enum Type {
    Atom(RefCell<Value>),
    Int(isize),
    Symbol(String),
    Str(String),
    Keyword(String),
    Boolean(bool),
    Float(f64),
    List(List),
    Vector(List),
    Map(HashMap<String, Value>),
    Func(Function),
    Closure(Closure),
    Nothing,
}

impl std::ops::Deref for Value {
    type Target = Rc<Type>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

macro_rules! value_constructor {
    ($name:ident ($t:ident) = $repr:expr) => (
        pub fn $name(v: $t) -> Self {
            Value(Rc::new($repr(v)))
        }
    )
}

type Function = fn(List) -> LispResult;

impl Value {

    pub fn new(v: Type) -> Self {
        Value(Rc::new(v))
    }

    value_constructor!(int (isize) = Type::Int);
    value_constructor!(float (f64) = Type::Float);
    value_constructor!(boolean (bool) = Type::Boolean);
    value_constructor!(symbol (String) = Type::Symbol);
    value_constructor!(string (String) = Type::Str);
    value_constructor!(keyword (String) = Type::Keyword);
    value_constructor!(list (List) = Type::List);
    value_constructor!(vector (List) = Type::Vector);
    value_constructor!(func (Function) = Type::Func);

    pub fn atom(v: Value) -> Self {
        Value(Rc::new(Type::Atom(RefCell::new(v))))
    }

    pub fn map(v: HashMap<String, Value>) -> Self {
        Value(Rc::new(Type::Map(v)))
    }

    pub fn nothing() -> Self {
        Value(Rc::new(Type::Nothing))
    }

    pub fn closure(binds: Vec<String>, body: Value, env: Environment) -> Self {
        Value(Rc::new(
            Type::Closure(
                Closure {
                    binds,
                    body,
                    env,
                    is_macro: false,
                }
            )
        ))
    }

    pub fn macros(closure: Closure) -> Self {
        Value(Rc::new(
            Type::Closure(
                Closure {
                    is_macro: true,
                    .. closure
                }
            )
        ))
    }
}

pub type LispResult = Result<Value, Error>;

#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    InvalidArg(&'static str, &'static str),
    InvalidArity(&'static str, &'static str),
    ParseError(String),
    Incomplete,
    Custom(String),
    Value(Value),
    BindError(BindError),
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            Error::Custom(ref c) => write!(f, "{}", c),
            Error::Incomplete => write!(f, "Incomplete (may be unmatched parentheses)"),
            Error::InvalidArg(func, valid) => write!(f, "Invalid arguments for {}. Valid arguments should be {}", func, valid),
            Error::InvalidArity(func, valid) => write!(f, "Invalid arity of {}. Valid arity is {}", func, valid),
            Error::ParseError(ref why) => write!(f, "Parse Error: {}", why),
            Error::Value(ref v) => write!(f, "{}", Writer::print(v.clone(), true)),
            Error::BindError(ref e) => match *e {
                BindError::NotEnoughArgs => write!(f, "Too many values supplied"),
                BindError::NotEnoughVals => write!(f, "Not enough values supplied"),
            }
        }
    }
}

#[derive(Clone)]
pub struct Closure {
    pub binds: Vec<String>,
    pub body: Value,
    pub env: Environment,
    pub is_macro: bool,
}

impl PartialEq for Closure {
    fn eq(&self, _: &Closure) -> bool {
        false
    }
}

impl std::fmt::Debug for Closure {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "Closure")
    }
}

fn print_str(ast: &Value, pretty: bool) -> String {
    match ***ast {
        Type::Boolean(ref v) => v.to_string(),
        Type::Float(ref v) => v.to_string(),
        Type::Int(ref v) => v.to_string(),
        Type::List(ref v) => {
            let mut result = String::new();
            for (i, e) in v.iter().enumerate() {
                result.push_str(&print_str(e, pretty));
                if i != v.len() - 1 {
                    result.push_str(" ");
                }
            }
            format!("({})", &result)
        },
        Type::Vector(ref v) => {
            let mut result = String::new();
            for (i, e) in v.iter().enumerate() {
                result.push_str(&print_str(e, pretty));
                if i != v.len() - 1 {
                    result.push_str(" ");
                }
            }
            format!("[{}]", &result)
        },
        Type::Map(ref v) => {
            let mut result = String::new();
            for (i, (key, value)) in v.iter().enumerate() {
                result.push_str(&format!(":{} {}", &key,
                                &print_str(value, pretty)));
                if i != v.len() - 1 {
                    result.push_str(" ");
                }
            }
            format!("{{{}}}", &result)
        },
        Type::Str(ref v) => if pretty {
            v.clone()
        } else {
            format!("\"{}\"", v)
        },
        Type::Symbol(ref v) => v.clone(),
        Type::Keyword(ref v) => format!(":{}", v),
        Type::Func(_) => "#<function>".to_owned(),
        Type::Closure(_) => "#<closure>".to_owned(),
        Type::Nothing => "".to_owned(),
        Type::Atom(ref v) => format!("atom<{}>", &print_str(&v.borrow(), pretty)),
    }
}

pub struct Writer {
}

impl Writer {
    pub fn print(ast: Value, pretty: bool) -> String {
        print_str(&ast, pretty)
    }
}

pub type Environment = Rc<RefCell<EnvironmentStruct>>;

#[derive(Clone, Debug, PartialEq)]
pub enum BindError {
    NotEnoughArgs,
    NotEnoughVals,
}

#[derive(Clone, Debug, PartialEq)]
pub struct EnvironmentStruct {
    data: HashMap<String, Value>,
    outer: Option<Environment>,
}

impl EnvironmentStruct {
    pub fn new(outer: Option<Environment>) -> Environment {
        Rc::new(RefCell::new(EnvironmentStruct {
            data: HashMap::new(),
            outer: outer,
        }))
    }

    pub fn top(env: &Environment) -> Environment {
        match env.borrow().outer {
            Some(ref e) => EnvironmentStruct::top(e),
            None => env.clone(),
        }
    }

    pub fn with_bindings(outer: Option<Environment>,
                         binds: Vec<String>,
                         exprs: Vec<Value>) -> Result<Environment, BindError> {
        let mut result = EnvironmentStruct {
            data: HashMap::new(),
            outer: outer,
        };
        let mut variadic = false;
        for (b, e) in binds.iter().zip(exprs.iter()) {
            if b == "&" {
                variadic = true;
            }
            if !variadic {
                result.set(b.clone(), e.clone());
            }
        }
        if variadic {
            let var_len = binds.len() - 1;
            let value_vec = exprs[(var_len - 1)..].to_vec();
            result.set(binds[var_len].clone(), Value::list(value_vec));
        } else {
            if binds.len() < exprs.len() {
                return Err(BindError::NotEnoughArgs)
            } else if binds.len() > exprs.len() {
                return Err(BindError::NotEnoughVals)
            }
        }
        Ok(Rc::new(RefCell::new(result)))
    }

    pub fn set(&mut self, key: String, val: Value) {
        self.data.insert(key, val);
    }

    pub fn find(&self, key: String) -> Option<Environment> {
        if self.data.contains_key(&key) {
            Some(Rc::new(RefCell::new(self.clone())))
        } else {
            match self.outer {
                Some(ref outer) => {
                    let outer = outer.borrow();
                    outer.find(key)
                },
                None => None,
            }
        }
    }

    pub fn get(&self, key: String) -> LispResult {
        match self.data.get(&key) {
            Some(v) => Ok(v.clone()),
            None => {
                if let Some(env) = self.find(key.clone()) {
                    env.borrow().get(key)
                } else {
                    Err(Error::Custom(format!("No value with name '{}' in the current scope", &key)))
                }
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use ::*;
    #[test]
    fn atoms() {
        assert_eq!(atom(&b"true"[..]), IResult::Done(&b""[..], Type::Boolean(true)));
        assert_eq!(atom(&b"false"[..]), IResult::Done(&b""[..], Type::Boolean(false)));
        assert_eq!(atom(&b"function"[..]), IResult::Done(&b""[..], Type::Symbol("function".to_owned())));
        assert_eq!(atom(&b":keyword"[..]), IResult::Done(&b""[..], Type::Keyword("keyword".to_owned())));
    }

    #[test]
    fn numbers() {
        assert_eq!(atom(&b"32"[..]), IResult::Done(&b""[..], Type::Int(32)));
        assert_eq!(atom(&b"-32321"[..]), IResult::Done(&b""[..], Type::Int(-32321)));
    }

    #[test]
    fn floats() {
        assert_eq!(atom(&b"1.3"[..]), IResult::Done(&b""[..], Type::Float(1.3)));
    }

    #[test]
    fn strings() {
        assert_eq!(atom(&b"\"some string\""[..]), IResult::Done(&b""[..],
        Type::Str("some string".to_owned())));
        assert_eq!(atom(&b"\"some \\\"escaped\\\" string\""[..]), IResult::Done(&b""[..],
        Type::Str("some \"escaped\" string".to_owned())));
    }

    #[test]
    fn lists() {
        let list1 = Type::List(vec![
            Rc::new(Type::Symbol("somesymbol".to_owned())),
            Rc::new(Type::Int(11)),
            Rc::new(Type::Boolean(true)),
            Rc::new(Type::Float(1.3)),
            Rc::new(Type::Int(10231)),
            Rc::new(Type::Int(-30)),
            Rc::new(Type::Str("some string".to_owned())),
        ]);
        assert_eq!(list(&b"(somesymbol 11 true 1.3 10231 -30 \"some string\")"[..]),
        IResult::Done(&b""[..], list1.clone()));
        let list2 = Type::List(vec![
            Rc::new(list1),
            Rc::new(Type::Symbol("somesymbol".to_owned())),
        ]);
        assert_eq!(list(&b"((somesymbol 11 true 1.3 10231 -30 \"some string\") somesymbol)"[..]),
        IResult::Done(&b""[..], list2));
    }

    #[test]
    fn vectors() {
        let vector1 = Type::Vector(vec![
            Rc::new(Type::Symbol("somesymbol".to_owned())),
            Rc::new(Type::Int(11)),
            Rc::new(Type::Boolean(true)),
            Rc::new(Type::Float(1.3)),
            Rc::new(Type::Int(10231)),
            Rc::new(Type::Int(-30)),
            Rc::new(Type::Str("some string".to_owned())),
        ]);
        assert_eq!(vector(&b"[somesymbol 11 true 1.3 10231 -30 \"some string\"]"[..]),
        IResult::Done(&b""[..], vector1.clone()));
        let vector2 = Type::Vector(vec![
            Rc::new(vector1),
            Rc::new(Type::Symbol("somesymbol".to_owned())),
        ]);
        assert_eq!(vector(&b"[[somesymbol 11 true 1.3 10231 -30 \"some string\"] somesymbol]"[..]),
        IResult::Done(&b""[..], vector2));
    }
}