selfish 0.1.0

Lisp language for game scripting
Documentation
use nom::{IResult, digit, double, not_line_ending, line_ending};
use std::str;
use std::str::FromStr;
use std::collections::HashMap;

use {Type, LispResult, Value};

named!(string<&[u8], String>,
    delimited!(
        tag!("\""),
        fold_many0!(
            alt!(
                is_not!("\\\"") |
                map!(
                    complete!(tag!("\\\"")),
                    |_| &b"\""[..]
                )
            ),
            String::new(),
            |mut acc: String, bytes: &[u8]| {
                acc.push_str(str::from_utf8(bytes).expect("Wrong utf8"));
                acc
            }
        ),
        tag!("\"")
    )
);

named!(integer_number<&[u8], isize>,
    map!(do_parse!(
        sign: opt!(alt!(char!('-') | char!('+'))) >>
        number: map_res!(map_res!(digit, str::from_utf8), isize::from_str) >>
        (number, sign)
    ),
    | (number, sign) | {
        match sign {
            Some(_) => -number,
            None => number,
        }
    })
);

named!(number<Type>,
    alt_complete!(double => { |v| Type::Float(v as f64) } |
                  integer_number => { |v| Type::Int(v as isize) }
                  )
);

named!(symbol<String>,
    map!(many1!(none_of!("'`~@[](){};\"\t\n\r ")),
    |v| {
        v.into_iter().collect()
    })
);

named!(keyword<String>,
    preceded!(
        tag!(":"),
        symbol
    )
);

named!(atom<&[u8], Type>,
    alt_complete!(number |
                  keyword => { |v| Type::Keyword(v) } |
                  string => { |v| Type::Str(v) } |
                  symbol => { |v: String| match v.as_ref() {
                      "true" => Type::Boolean(true),
                      "false" => Type::Boolean(false),
                      _ => Type::Symbol(v),
                      }
                  }
));

named!(list<&[u8], Type>,
    map!(delimited!(
        char!('('),
        many0!(ws!(expression)),
        char!(')')
    ),
    |v| {
        let mut result = Vec::new();
        for e in v.into_iter().filter(|e| e != &Type::Nothing) {
            result.push(Value::new(e));
        }
        Type::List(result)
    })
);

named!(vector<&[u8], Type>,
    map!(delimited!(
        char!('['),
        many0!(ws!(expression)),
        char!(']')
    ),
    |v| {
        let mut result = Vec::new();
        for e in v.into_iter().filter(|e| e != &Type::Nothing) {
            result.push(Value::new(e));
        }
        Type::Vector(result)
    })
);

named!(hash_map<Type>,
    map!(delimited!(
        char!('{'),
        many0!(ws!(pair!(keyword, expression))),
        char!('}')
    ),
    |v| {
        let mut result = HashMap::new();
        for e in v.into_iter().filter(|e| e.1 != Type::Nothing) {
            result.insert(e.0, Value::new(e.1));
        }
        Type::Map(result)
    })
);

named!(expression<&[u8], Type>,
    map!(pair!(
        many0!(alt_complete!(
            tag!("'") => { |_| Type::Symbol("quote".into()) } |
            tag!("@") => { |_| Type::Symbol("deref".into()) } |
            tag!("`") => { |_| Type::Symbol("quasiquote".into()) } |
            tag!("~@") => { |_| Type::Symbol("splice-unquote".into()) } |
            tag!("~") => { |_| Type::Symbol("unquote".into()) }
        )),
        alt!(atom | list | vector | hash_map | comment)
    ),
    |(mut mac, expr)| {
        if !mac.is_empty() {
            let mut result = expr;
            mac.reverse();
            for e in mac {
                result = Type::List(vec![Value::new(e), Value::new(result)]);
            }
            result
        } else {
            expr
        }
    })
);

named!(comment<&[u8], Type>,
    do_parse!(
        tag!(";") >>
        not_line_ending >>
        line_ending >>
        (Type::Nothing)
    )
);

named!(program<&[u8], Value>,
    map!(many1!(ws!(expression)),
    |exprs| {
        if exprs.len() > 1 {
            let mut values: Vec<Value> = exprs
                                                .into_iter()
                                                .filter(|e| e != &Type::Nothing)
                                                .map(Value::new)
                                                .collect();
            values.insert(0, Value::symbol("do".into()));
            Value::list(values)
        } else {
            Value::new(exprs[0].clone())
        }
    })
);

pub struct Reader {
}

impl Reader {
    pub fn read(slice: &[u8]) -> LispResult {
        use Error;
        match program(slice) {
            IResult::Done(_, result) => Ok(result),
            IResult::Error(e) => Err(Error::ParseError(format!("{}", e))),
            IResult::Incomplete(_) => Err(Error::Incomplete),
        }
    }
}