<commit_before>use std::{fmt, result, str, error as std_error};
use std::io::{Result, Read, Error, ErrorKind};

pub trait CharsExt {
    fn chars_iterator(self) -> Chars<Self> where Self: Sized {
        Chars { inner: self }
    }
}

impl<T: Read> CharsExt for T {

}

#[derive(Debug)]
pub struct Chars<R> {
    inner: R,
}

/// An enumeration of possible errors that can be generated from the `Chars`
/// adapter.
#[derive(Debug)]
pub enum CharsError {
    /// Variant representing that the underlying stream was read successfully
    /// but it did not contain valid utf8 data.
    NotUtf8,

    /// Variant representing that an I/O error occurred.
    Other(Error),
}

impl<R: Read> Iterator for Chars<R> {
    type Item = result::Result<char, CharsError>;

    fn next(&mut self) -> Option<result::Result<char, CharsError>> {
        let first_byte = match read_one_byte(&mut self.inner)? {
            Ok(b) => b,
            Err(e) => return Some(Err(CharsError::Other(e))),
        };
        let width = utf8_char_width(first_byte);
        if width == 1 { return Some(Ok(first_byte as char)) }
        if width == 0 { return Some(Err(CharsError::NotUtf8)) }
        let mut buf = [first_byte, 0, 0, 0];
        {
            let mut start = 1;
            while start < width {
                match self.inner.read(&mut buf[start..width]) {
                    Ok(0) => return Some(Err(CharsError::NotUtf8)),
                    Ok(n) => start += n,
                    Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                    Err(e) => return Some(Err(CharsError::Other(e))),
                }
            }
        }
        Some(match str::from_utf8(&buf[..width]).ok() {
            Some(s) => Ok(s.chars().next().unwrap()),
            None => Err(CharsError::NotUtf8),
        })
    }
}

impl std_error::Error for CharsError {
    fn description(&self) -> &str {
        match *self {
            CharsError::NotUtf8 => "invalid utf8 encoding",
            CharsError::Other(ref e) => std_error::Error::description(e),
        }
    }
    fn cause(&self) -> Option<&std_error::Error> {
        match *self {
            CharsError::NotUtf8 => None,
            CharsError::Other(ref e) => e.cause(),
        }
    }
}

impl fmt::Display for CharsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CharsError::NotUtf8 => {
                "byte stream did not contain valid utf8".fmt(f)
            }
            CharsError::Other(ref e) => e.fmt(f),
        }
    }
}

fn read_one_byte(reader: &mut Read) -> Option<Result<u8>> {
    let mut buf = [0];
    loop {
        return match reader.read(&mut buf) {
            Ok(0) => None,
            Ok(..) => Some(Ok(buf[0])),
            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
            Err(e) => Some(Err(e)),
        };
    }
}

// https://tools.ietf.org/html/rfc3629
static UTF8_CHAR_WIDTH: [u8; 256] = [
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
];

/// Given a first byte, determines how many bytes are in this UTF-8 character.
#[inline]
pub fn utf8_char_width(b: u8) -> usize {
    return UTF8_CHAR_WIDTH[b as usize] as usize;
}

#[cfg(test)]
mod tests {
    use CharsExt;

    use std::io::Cursor;

    #[test]
    fn chars_iterator_works() {
        let mut s = "hello, I'm cool".to_owned();

        let c = Cursor::new(s..into_bytes());

        let v: Vec<char> = c.chars_iterator().flat_map(Result::ok).collect();
        let v2: Vec<char> = s.chars().collect();
        assert_eq!(v, v2);
    }
}
<commit_msg>Fix tests and re-factor `utf8_char_width`<commit_after>use std::{fmt, result, str, error as std_error};
use std::io::{Result, Read, Error, ErrorKind};

pub trait CharsExt {
    fn chars_iterator(self) -> Chars<Self> where Self: Sized {
        Chars { inner: self }
    }
}

impl<T: Read> CharsExt for T {

}

#[derive(Debug)]
pub struct Chars<R> {
    inner: R,
}

/// An enumeration of possible errors that can be generated from the `Chars`
/// adapter.
#[derive(Debug)]
pub enum CharsError {
    /// Variant representing that the underlying stream was read successfully
    /// but it did not contain valid utf8 data.
    NotUtf8,

    /// Variant representing that an I/O error occurred.
    Other(Error),
}

impl<R: Read> Iterator for Chars<R> {
    type Item = result::Result<char, CharsError>;

    fn next(&mut self) -> Option<result::Result<char, CharsError>> {
        let first_byte = match read_one_byte(&mut self.inner)? {
            Ok(b) => b,
            Err(e) => return Some(Err(CharsError::Other(e))),
        };
        let width = utf8_char_width(first_byte);
        if width == 1 { return Some(Ok(first_byte as char)) }
        if width == 0 { return Some(Err(CharsError::NotUtf8)) }
        let mut buf = [first_byte, 0, 0, 0];
        {
            let mut start = 1;
            while start < width {
                match self.inner.read(&mut buf[start..width]) {
                    Ok(0) => return Some(Err(CharsError::NotUtf8)),
                    Ok(n) => start += n,
                    Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                    Err(e) => return Some(Err(CharsError::Other(e))),
                }
            }
        }
        Some(match str::from_utf8(&buf[..width]).ok() {
            Some(s) => Ok(s.chars().next().unwrap()),
            None => Err(CharsError::NotUtf8),
        })
    }
}

impl std_error::Error for CharsError {
    fn description(&self) -> &str {
        match *self {
            CharsError::NotUtf8 => "invalid utf8 encoding",
            CharsError::Other(ref e) => std_error::Error::description(e),
        }
    }
    fn cause(&self) -> Option<&std_error::Error> {
        match *self {
            CharsError::NotUtf8 => None,
            CharsError::Other(ref e) => e.cause(),
        }
    }
}

impl fmt::Display for CharsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CharsError::NotUtf8 => {
                "byte stream did not contain valid utf8".fmt(f)
            }
            CharsError::Other(ref e) => e.fmt(f),
        }
    }
}

fn read_one_byte(reader: &mut Read) -> Option<Result<u8>> {
    let mut buf = [0];
    loop {
        return match reader.read(&mut buf) {
            Ok(0) => None,
            Ok(..) => Some(Ok(buf[0])),
            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
            Err(e) => Some(Err(e)),
        };
    }
}

/// Given a first byte, determines how many bytes are in this UTF-8 character.
#[inline]
pub fn utf8_char_width(b: u8) -> usize {
    match b {
        0...0x7f => 1,
        0xc2...0xdf => 2,
        0xe0...0xef => 3,
        0xf0...0xf4 => 4,
        0x80...0xc1 | 0xf5...0xff => 0,
        _ => unreachable!(),
    }
}

#[cfg(test)]
mod tests {
    use super::CharsExt;

    use std::io::Cursor;

    // https://tools.ietf.org/html/rfc3629
    static UTF8_CHAR_WIDTH: [u8; 256] = [
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
        0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
        2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
        3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
        4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
    ];

    #[test]
    pub fn char_width() {
        for i in 0..256 {
            assert_eq!(super::utf8_char_width(i as u8) as u8, UTF8_CHAR_WIDTH[i]);
        }
    }

    #[test]
    fn chars_iterator_works() {
        let s = "hello, I'm cool".to_owned();

        let c = Cursor::new(s.clone().into_bytes());

        let v: Vec<char> = c.chars_iterator().flat_map(Result::ok).collect();
        let v2: Vec<char> = s.chars().collect();
        assert_eq!(v, v2);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt;
use syntax::{Expr, ExprF, Literal};
use typed_arena::Arena;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Error<'str, 'ty> {
  Unify(&'ty Ty<'ty>, &'ty Ty<'ty>),
  Var(&'str str),
}

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ID(usize);

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Ty<'ty> {
  Deferred(ID),
  Func(&'ty Ty<'ty>, &'ty Ty<'ty>),
  Bool,
  Int,
  Str,
  Bytes,
  Tuple(Vec<&'ty Ty<'ty>>),
}

pub static TY_BOOL:  Ty<'static> = Ty::Bool;
pub static TY_INT:   Ty<'static> = Ty::Int;
pub static TY_STR:   Ty<'static> = Ty::Str;
pub static TY_BYTES: Ty<'static> = Ty::Bytes;

pub static BUILTIN_STDOUT_TY:  Ty<'static> = Ty::Int;
pub static BUILTIN_TO_UTF8_TY: Ty<'static> = Ty::Func(&TY_STR, &TY_BYTES);
pub static BUILTIN_WRITE_TY:   Ty<'static> =
  Ty::Func(&TY_INT, &Ty::Func(&TY_BYTES, &TY_INT)); // FIXME: Return io int.

impl<'ty> Ty<'ty> {
  pub fn pretty<Purge, W>(&'ty self, purge: &Purge, into: &mut W) -> fmt::Result
    where
      Purge: Fn(&'ty Ty<'ty>) -> &'ty Ty<'ty>,
      W: fmt::Write {
    match *purge(self) {
      Ty::Deferred(ID(id)) =>
        write!(into, "t{}", id),
      Ty::Func(a, b) => {
        write!(into, "(")?;
        a.pretty(purge, into)?;
        write!(into, " -> ")?;
        b.pretty(purge, into)?;
        write!(into, ")")?;
        Ok(())
      },
      Ty::Bool =>
        write!(into, "bool"),
      Ty::Int =>
        write!(into, "int"),
      Ty::Str =>
        write!(into, "str"),
      Ty::Bytes =>
        write!(into, "bytes"),
      Ty::Tuple(ref elem_tys) => {
        write!(into, "{{")?;
        for elem_ty in elem_tys {
          elem_ty.pretty(purge, into)?;
          write!(into, ",")?;
        }
        write!(into, "}}")?;
        Ok(())
      },
    }
  }

  pub fn pretty_string<Purge>(&'ty self, purge: &Purge) -> String
    where Purge: Fn(&'ty Ty<'ty>) -> &'ty Ty<'ty> {
    let mut pretty = String::new();
    self.pretty(purge, &mut pretty).unwrap();
    pretty
  }
}

pub struct Check<'ty> {
  arena: &'ty Arena<Ty<'ty>>,
  next_id: usize,
  solved: HashMap<ID, &'ty Ty<'ty>>,
}

impl<'ty> Check<'ty> {
  pub fn new(arena: &'ty Arena<Ty<'ty>>) -> Self {
    Check{arena: arena, next_id: 0, solved: HashMap::new()}
  }

  pub fn fresh(&mut self) -> &'ty Ty<'ty> {
    let ty = self.arena.alloc(Ty::Deferred(ID(self.next_id)));
    self.next_id += 1;
    ty
  }

  fn solve(&mut self, id: ID, ty: &'ty Ty<'ty>) {
    self.solved.insert(id, ty);
  }

  pub fn purge(&self, ty: &'ty Ty<'ty>) -> &'ty Ty<'ty> {
    if let &Ty::Deferred(id) = ty {
      self.solved.get(&id).map(|uy| self.purge(uy)).unwrap_or(ty)
    } else {
      ty
    }
  }

  pub fn unify<'str>(&mut self, ty: &'ty Ty<'ty>, uy: &'ty Ty<'ty>)
    -> Result<(), Error<'str, 'ty>> {
    match (self.purge(ty), self.purge(uy)) {
      (&Ty::Deferred(ty_id), &Ty::Deferred(uy_id))
        if ty_id == uy_id =>
        Ok(()),
      (&Ty::Deferred(ty_id), uy_p) => {
        self.solve(ty_id, uy_p);
        Ok(())
      },
      (ty_p, &Ty::Deferred(uy_id)) => {
        self.solve(uy_id, ty_p);
        Ok(())
      },
      (&Ty::Func(ty_a, ty_b), &Ty::Func(uy_a, uy_b)) => {
        self.unify(ty_a, uy_a)?;
        self.unify(ty_b, uy_b)?;
        Ok(())
      },
      (&Ty::Bool,  &Ty::Bool)  => Ok(()),
      (&Ty::Int,   &Ty::Int)   => Ok(()),
      (&Ty::Str,   &Ty::Str)   => Ok(()),
      (&Ty::Bytes, &Ty::Bytes) => Ok(()),
      (&Ty::Tuple(ref elem_tys), &Ty::Tuple(ref elem_uys)) => {
        if elem_tys.len() != elem_uys.len() {
          Err(Error::Unify(&TY_BOOL, &TY_INT)) // FIXME: Tuple types.
        } else {
          for (ty, uy) in elem_tys.iter().zip(elem_uys.iter()) {
            self.unify(ty, uy)?;
          }
          Ok(())
        }
      },
      (a, b) => Err(Error::Unify(a, b))
    }
  }

  pub fn infer<'str, 'expr, T>(
    &mut self,
    env: &HashMap<&str, &'ty Ty<'ty>>,
    expr: &Expr<'str, 'expr, T>,
  ) -> Result<&'ty Ty<'ty>, Error<'str, 'ty>> {
    match expr.1 {
      ExprF::Lit(Literal::Bool(_)) => Ok(&TY_BOOL),
      ExprF::Lit(Literal::Int(_))  => Ok(&TY_INT),
      ExprF::Lit(Literal::Str(_))  => Ok(&TY_STR),
      ExprF::Var("stdout#")  => Ok(&BUILTIN_STDOUT_TY),
      ExprF::Var("to_utf8#") => Ok(&BUILTIN_TO_UTF8_TY),
      ExprF::Var("write#")   => Ok(&BUILTIN_WRITE_TY),
      ExprF::Var(name) => env.get(&name).map(|&ty| ty).ok_or(Error::Var(name)),
      ExprF::Abs(param, body) => {
        let param_ty = self.fresh();

        let mut body_env = env.clone();
        body_env.insert(param, param_ty);
        let result_ty = self.infer(&body_env, body)?;

        let func_ty = self.arena.alloc(Ty::Func(param_ty, result_ty));
        Ok(func_ty)
      },
      ExprF::App(callee, argument) => {
        let callee_ty = self.infer(env, callee)?;
        let argument_ty = self.infer(env, argument)?;

        let result_ty = self.fresh();
        let func_ty = self.arena.alloc(Ty::Func(argument_ty, result_ty));
        self.unify(callee_ty, func_ty)?;

        Ok(result_ty)
      },
      ExprF::Tup(ref elems) => {
        let mut elem_tys = Vec::with_capacity(elems.len());
        for elem in elems {
          let elem_ty = self.infer(env, elem)?;
          elem_tys.push(elem_ty);
        }
        let tuple_ty = self.arena.alloc(Ty::Tuple(elem_tys));
        Ok(tuple_ty)
      },
    }
  }
}

#[cfg(test)]
mod test {
  use super::*;

  #[test]
  fn test_unify() {
    let arena = Arena::new();
    let mut check = Check::new(&arena);

    let ty1 = &TY_BOOL;
    let ty2 = arena.alloc(Ty::Func(ty1, ty1));
    let ty3 = check.fresh();
    let ty4 = check.fresh();
    let ty5 = check.fresh();
    let ty6 = check.fresh();

    assert_eq!(check.unify(ty1, ty1), Ok(()));

    assert_eq!(check.unify(ty2, ty2), Ok(()));

    assert_eq!(check.unify(ty1, ty2), Err(Error::Unify(ty1, ty2)));

    assert_eq!(check.unify(ty3, ty3), Ok(()));
    assert_eq!(check.unify(ty3, ty4), Ok(()));
    assert_eq!(check.unify(ty4, ty1), Ok(()));
    assert_eq!(check.purge(ty3),      ty1);
    assert_eq!(check.unify(ty5, ty1), Ok(()));
    assert_eq!(check.unify(ty5, ty3), Ok(()));
    assert_eq!(check.unify(ty6, ty2), Ok(()));
    assert_eq!(check.unify(ty5, ty6), Err(Error::Unify(ty1, ty2)));
  }

  #[test]
  fn test_infer() {
    let expr_arena = Arena::new();
    let ty_arena = Arena::new();

    let mut check = Check::new(&ty_arena);

    let mut env = HashMap::new();
    env.insert("vari", &TY_INT);
    env.insert("even", ty_arena.alloc(Ty::Func(&TY_INT, &TY_BOOL)));

    let expr = Expr(
      (),
      ExprF::App(
        expr_arena.alloc(Expr((), ExprF::Var("even"))),
        expr_arena.alloc(Expr((), ExprF::Var("vari"))),
      ),
    );
    let ty = check.infer(&env, &expr);
    assert_eq!(ty.map(|t| check.purge(t)), Ok(&TY_BOOL));
  }
}
<commit_msg>Clean up stuff<commit_after>use std::collections::HashMap;
use std::fmt;
use syntax::{Expr, ExprF, Literal};
use typed_arena::Arena;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Error<'str, 'ty> {
  Unify(&'ty Ty<'ty>, &'ty Ty<'ty>),
  Var(&'str str),
}

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ID(usize);

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Ty<'ty> {
  Deferred(ID),
  Func(&'ty Ty<'ty>, &'ty Ty<'ty>),
  Bool,
  Int,
  Str,
  Bytes,
  Tuple(Vec<&'ty Ty<'ty>>),
}

pub static TY_BOOL:  Ty<'static> = Ty::Bool;
pub static TY_INT:   Ty<'static> = Ty::Int;
pub static TY_STR:   Ty<'static> = Ty::Str;
pub static TY_BYTES: Ty<'static> = Ty::Bytes;

pub static BUILTIN_STDOUT_TY:  Ty<'static> = Ty::Int;
pub static BUILTIN_TO_UTF8_TY: Ty<'static> = Ty::Func(&TY_STR, &TY_BYTES);
pub static BUILTIN_WRITE_TY:   Ty<'static> =
  Ty::Func(&TY_INT, &Ty::Func(&TY_BYTES, &TY_INT)); // FIXME: Return io int.

impl<'ty> Ty<'ty> {
  pub fn pretty<Purge, W>(&'ty self, purge: &Purge, into: &mut W) -> fmt::Result
    where
      Purge: Fn(&'ty Ty<'ty>) -> &'ty Ty<'ty>,
      W: fmt::Write {
    match *purge(self) {
      Ty::Deferred(ID(id)) =>
        write!(into, "t{}", id),
      Ty::Func(a, b) => {
        write!(into, "(")?;
        a.pretty(purge, into)?;
        write!(into, " -> ")?;
        b.pretty(purge, into)?;
        write!(into, ")")?;
        Ok(())
      },
      Ty::Bool =>  write!(into, "bool"),
      Ty::Int =>   write!(into, "int"),
      Ty::Str =>   write!(into, "str"),
      Ty::Bytes => write!(into, "bytes"),
      Ty::Tuple(ref elem_tys) => {
        write!(into, "{{")?;
        for elem_ty in elem_tys {
          elem_ty.pretty(purge, into)?;
          write!(into, ",")?;
        }
        write!(into, "}}")?;
        Ok(())
      },
    }
  }

  pub fn pretty_string<Purge>(&'ty self, purge: &Purge) -> String
    where Purge: Fn(&'ty Ty<'ty>) -> &'ty Ty<'ty> {
    let mut pretty = String::new();
    self.pretty(purge, &mut pretty).unwrap();
    pretty
  }
}

pub struct Check<'ty> {
  arena: &'ty Arena<Ty<'ty>>,
  next_id: usize,
  solved: HashMap<ID, &'ty Ty<'ty>>,
}

impl<'ty> Check<'ty> {
  pub fn new(arena: &'ty Arena<Ty<'ty>>) -> Self {
    Check{arena: arena, next_id: 0, solved: HashMap::new()}
  }

  pub fn fresh(&mut self) -> &'ty Ty<'ty> {
    let ty = self.arena.alloc(Ty::Deferred(ID(self.next_id)));
    self.next_id += 1;
    ty
  }

  fn solve(&mut self, id: ID, ty: &'ty Ty<'ty>) {
    self.solved.insert(id, ty);
  }

  pub fn purge(&self, ty: &'ty Ty<'ty>) -> &'ty Ty<'ty> {
    if let &Ty::Deferred(id) = ty {
      self.solved.get(&id).map(|uy| self.purge(uy)).unwrap_or(ty)
    } else {
      ty
    }
  }

  pub fn unify<'str>(&mut self, ty: &'ty Ty<'ty>, uy: &'ty Ty<'ty>)
    -> Result<(), Error<'str, 'ty>> {
    match (self.purge(ty), self.purge(uy)) {
      (&Ty::Deferred(ty_id), &Ty::Deferred(uy_id))
        if ty_id == uy_id =>
        Ok(()),
      (&Ty::Deferred(ty_id), uy_p) => {
        self.solve(ty_id, uy_p);
        Ok(())
      },
      (ty_p, &Ty::Deferred(uy_id)) => {
        self.solve(uy_id, ty_p);
        Ok(())
      },
      (&Ty::Func(ty_a, ty_b), &Ty::Func(uy_a, uy_b)) => {
        self.unify(ty_a, uy_a)?;
        self.unify(ty_b, uy_b)?;
        Ok(())
      },
      (&Ty::Bool,  &Ty::Bool)  => Ok(()),
      (&Ty::Int,   &Ty::Int)   => Ok(()),
      (&Ty::Str,   &Ty::Str)   => Ok(()),
      (&Ty::Bytes, &Ty::Bytes) => Ok(()),
      (&Ty::Tuple(ref elem_tys), &Ty::Tuple(ref elem_uys)) => {
        if elem_tys.len() != elem_uys.len() {
          Err(Error::Unify(&TY_BOOL, &TY_INT)) // FIXME: Tuple types.
        } else {
          for (ty, uy) in elem_tys.iter().zip(elem_uys.iter()) {
            self.unify(ty, uy)?;
          }
          Ok(())
        }
      },
      (a, b) => Err(Error::Unify(a, b))
    }
  }

  pub fn infer<'str, 'expr, T>(
    &mut self,
    env: &HashMap<&str, &'ty Ty<'ty>>,
    expr: &Expr<'str, 'expr, T>,
  ) -> Result<&'ty Ty<'ty>, Error<'str, 'ty>> {
    match expr.1 {
      ExprF::Lit(Literal::Bool(_)) => Ok(&TY_BOOL),
      ExprF::Lit(Literal::Int(_))  => Ok(&TY_INT),
      ExprF::Lit(Literal::Str(_))  => Ok(&TY_STR),
      ExprF::Var("stdout#")  => Ok(&BUILTIN_STDOUT_TY),
      ExprF::Var("to_utf8#") => Ok(&BUILTIN_TO_UTF8_TY),
      ExprF::Var("write#")   => Ok(&BUILTIN_WRITE_TY),
      ExprF::Var(name) => env.get(&name).map(|&ty| ty).ok_or(Error::Var(name)),
      ExprF::Abs(param, body) => {
        let param_ty = self.fresh();

        let mut body_env = env.clone();
        body_env.insert(param, param_ty);
        let result_ty = self.infer(&body_env, body)?;

        let func_ty = self.arena.alloc(Ty::Func(param_ty, result_ty));
        Ok(func_ty)
      },
      ExprF::App(callee, argument) => {
        let callee_ty = self.infer(env, callee)?;
        let argument_ty = self.infer(env, argument)?;

        let result_ty = self.fresh();
        let func_ty = self.arena.alloc(Ty::Func(argument_ty, result_ty));
        self.unify(callee_ty, func_ty)?;

        Ok(result_ty)
      },
      ExprF::Tup(ref elems) => {
        let mut elem_tys = Vec::with_capacity(elems.len());
        for elem in elems {
          let elem_ty = self.infer(env, elem)?;
          elem_tys.push(elem_ty);
        }
        let tuple_ty = self.arena.alloc(Ty::Tuple(elem_tys));
        Ok(tuple_ty)
      },
    }
  }
}

#[cfg(test)]
mod test {
  use super::*;

  #[test]
  fn test_unify() {
    let arena = Arena::new();
    let mut check = Check::new(&arena);

    let ty1 = &TY_BOOL;
    let ty2 = arena.alloc(Ty::Func(ty1, ty1));
    let ty3 = check.fresh();
    let ty4 = check.fresh();
    let ty5 = check.fresh();
    let ty6 = check.fresh();

    assert_eq!(check.unify(ty1, ty1), Ok(()));

    assert_eq!(check.unify(ty2, ty2), Ok(()));

    assert_eq!(check.unify(ty1, ty2), Err(Error::Unify(ty1, ty2)));

    assert_eq!(check.unify(ty3, ty3), Ok(()));
    assert_eq!(check.unify(ty3, ty4), Ok(()));
    assert_eq!(check.unify(ty4, ty1), Ok(()));
    assert_eq!(check.purge(ty3),      ty1);
    assert_eq!(check.unify(ty5, ty1), Ok(()));
    assert_eq!(check.unify(ty5, ty3), Ok(()));
    assert_eq!(check.unify(ty6, ty2), Ok(()));
    assert_eq!(check.unify(ty5, ty6), Err(Error::Unify(ty1, ty2)));
  }

  #[test]
  fn test_infer() {
    let expr_arena = Arena::new();
    let ty_arena = Arena::new();

    let mut check = Check::new(&ty_arena);

    let mut env = HashMap::new();
    env.insert("vari", &TY_INT);
    env.insert("even", ty_arena.alloc(Ty::Func(&TY_INT, &TY_BOOL)));

    let expr = Expr(
      (),
      ExprF::App(
        expr_arena.alloc(Expr((), ExprF::Var("even"))),
        expr_arena.alloc(Expr((), ExprF::Var("vari"))),
      ),
    );
    let ty = check.infer(&env, &expr);
    assert_eq!(ty.map(|t| check.purge(t)), Ok(&TY_BOOL));
  }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::fmt::{Debug, Formatter, Result as FmtResult};

use sdl2::Sdl;
use sdl2::rect::Rect;
use ep::FromPrimitive;

use screen::Screen;
use opcodes::Opcodes;

// TODO: move this elsewhere
const FONT_SET: [u8; 80] = [
    0xf0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xf0, 0x10, 0xf0, 0x80, 0xf0, // 2
    0xf0, 0x10, 0xf0, 0x10, 0xf0, // 3
    0x90, 0x90, 0xf0, 0x10, 0x10, // 4
    0xf0, 0x80, 0xf0, 0x10, 0xf0, // 5
    0xf0, 0x80, 0xf0, 0x90, 0xf0, // 6
    0xf0, 0x10, 0x20, 0x40, 0x40, // 7
    0xf0, 0x90, 0xf0, 0x90, 0xf0, // 8
    0xf0, 0x90, 0xf0, 0x10, 0xf0, // 9
    0xf0, 0x90, 0xf0, 0x90, 0x90, // A
    0xe0, 0x90, 0xe0, 0x90, 0xe0, // B
    0xf0, 0x80, 0x80, 0x80, 0x80, // C
    0xe0, 0x90, 0x90, 0x90, 0xe0, // D
    0xf0, 0x80, 0xf0, 0x80, 0xf0, // E
    0xf0, 0x80, 0xf0, 0x80, 0x80, // F
];

// TODO: move this elsewhere
#[derive(Default)]
struct Instruction {
    opcode: u16,
    nnn:    u16,
    kk:     u8,
    x:      usize,
    y:      usize,
    n:      u8,
}

impl Instruction {
    fn new() -> Instruction {
        Instruction::default()
    }

    fn decode(&mut self, data: (u8,u8)) {
        let fstb = data.0;
        let sndb = data.1;

        let op = fstb as u16 >> 4;

        match op {
            0x00                                => {
                self.opcode = op << 12 | sndb as u16;
            },
            0x01 | 0x02 | 0x0a | 0x0b           => {
                self.opcode = op << 12;
                self.nnn    = (fstb as u16 & 0x0F) << 8 | sndb as u16;
            },
            0x03 | 0x04 | 0x06 | 0x07 | 0x0c    => {
                self.opcode = op << 12;
                self.x      = (fstb & 0x0F) as usize;
                self.kk     = sndb;
            },
            0x05 | 0x08 | 0x09                  => {
                self.opcode = op << 12 | sndb as u16 & 0x0F;
                self.x      = (fstb & 0x0F) as usize;
                self.y      = ((sndb & 0xF0) >> 4) as usize;
            },
            0x0e | 0x0f                         => {
                self.opcode = op << 12 | sndb as u16;
                self.x      = (fstb & 0x0F) as usize;
            },
            0x0d                                => {
                self.opcode = op << 12;
                self.x      = (fstb & 0x0F) as usize;
                self.y      = ((sndb & 0xF0) >> 4) as usize;
                self.n      = sndb & 0x0F;
            },
            _                                   => {
                self.opcode = 0xFFFF;
            },
        }
    }

    fn get_opcode(&self) -> u16 {
        self.opcode
    }

    fn set_opcode(&mut self, val: u16) {
        self.opcode = val;
    }
}

pub struct Chip8<'a> {
    regs:   [u8; 16],
    i:      u16,
    dt:     u8,
    st:     u8,
    pc:     usize,

    inst:   Instruction,
    jmp:    bool,

    mem:    [u8; 4096],
    stack:  Vec<u16>,
    screen: Screen<'a>,
}

impl<'a> Debug for Chip8<'a> {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
         self.regs[..].fmt(f)
    }
}

impl<'a> Chip8<'a> {
    pub fn new(sdl: &Sdl) -> Chip8<'a> {
        Chip8 {
            regs:   [0; 16],
            i:      0,
            dt:     0,
            st:     0,
            pc:     0x200,
            inst:   Instruction::new(),
            jmp:    false,
            mem:    [0; 4096],
            stack:  vec![],
            screen: Screen::new(sdl),
        }
    }

    pub fn load_rom(&mut self, path: &str) {
        let mut rom: Vec<u8> = Vec::new();
        let mut file = File::open(Path::new(path)).unwrap();
        file.read_to_end(&mut rom).unwrap();
        for i in 0usize..rom.len() {
            self.mem[0x200 + i] = rom[i];
        }
        // TODO: move this
        for i in 0usize..FONT_SET.len() {
            self.mem[0x00 + i] = FONT_SET[i];
        }
    }

    pub fn fetch(&mut self) {
        let raw_data = (self.mem[self.pc], self.mem[self.pc +1]);
        self.inst.decode(raw_data);

        self.jmp = false;

        println!("{:#x}: {:#x}", self.pc, self.inst.get_opcode());

        match Opcodes::from_u16(self.inst.get_opcode()).unwrap() {
            Opcodes::CLS    => self.cls(),
            Opcodes::RET    => self.ret(),
            Opcodes::JMP    => self.jmp(),
            Opcodes::JMP_VA => self.jmp_va(),
            Opcodes::CALL   => self.call(),
            Opcodes::SE_VB  => self.se_vb(),
            Opcodes::SE_VV  => self.se_vv(),
            Opcodes::SNE_VB => self.sne_vb(),
            Opcodes::SNE_VV => self.sne_vv(),
            Opcodes::OR     => self.or(),
            Opcodes::ADD_VB => self.add_vb(),
            Opcodes::ADD_VV => self.add_vv(),
            Opcodes::ADD_IV => self.add_iv(),
            Opcodes::XOR    => self.xor(),
            Opcodes::AND    => self.and(),
            Opcodes::LD_VB  => self.ld_vb(),
            Opcodes::LD_BV  => self.ld_bv(),
            Opcodes::LD_VV  => self.ld_vv(),
            Opcodes::LD_VI  => self.ld_vi(),
            Opcodes::LD_IV  => self.ld_iv(),
            Opcodes::LD_FV  => self.ld_fv(),
            Opcodes::LD_IA  => self.ld_ia(),
            Opcodes::LD_VDT => self.ld_vdt(),
            Opcodes::LD_DTV => self.ld_dtv(),
            Opcodes::LD_STV => self.ld_stv(),
            Opcodes::SHR    => self.shr(),
            Opcodes::DRW    => self.drw(),
            _               => panic!("Unrecognized opcode: {:?}", self.inst.get_opcode()),
        }

        println!("i: {}", self.i);

        if !self.jmp { self.inc_pc(); }
        // TODO: refactor this to self.screen.present();
        self.screen.renderer.present();
    }

    fn set_pc(&mut self, addr: u16) {
        self.pc = addr as usize;
    }

    fn inc_pc(&mut self) {
        self.pc += 2;
    }

    fn cls(&mut self) {
        self.screen.clear();
    }

    fn ret(&mut self) {
        let addr = self.stack.pop().unwrap();
        self.set_pc(addr);
    }

    fn jmp(&mut self) {
        let addr: u16 = self.inst.nnn;
        self.set_pc(addr);
        self.jmp = true;
    }

    fn jmp_va(&mut self) {
        let offset = self.regs[0] as u16;
        let addr = self.inst.nnn + offset;
        self.set_pc(addr);
        self.jmp = true;
    }

    fn call(&mut self) {
        let old_addr: u16 = self.pc as u16;
        let cur_addr = self.inst.nnn;
        self.stack.push(old_addr);
        self.set_pc(cur_addr);
        self.jmp = true;
    }

    fn se_vb(&mut self) {
        let idx = self.inst.x;
        if self.regs[idx] == self.inst.kk {
            self.inc_pc();
        }
    }

    fn se_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        if self.regs[idx_x] == self.regs[idx_y] {
            self.inc_pc();
        }
    }

    fn sne_vb(&mut self) {
        let idx = self.inst.x;
        if self.regs[idx] != self.inst.kk {
            self.inc_pc();
        }
    }

    fn sne_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        if self.regs[idx_x] != self.regs[idx_y] {
            self.inc_pc();
        }
    }

    fn or(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] |= self.regs[idx_y]
    }

    fn and(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] &= self.regs[idx_y];
    }

    fn add_vb(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] += self.regs[idx_y];
    }

    fn add_vv(&self) {
        panic!("Not implemented add_vv");
    }

    fn add_iv(&mut self) {
        let idx = self.inst.x;
        self.i += self.regs[idx] as u16;
    }

    fn xor(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] ^= self.regs[idx_y];
    }

    fn shr(&mut self) {
        let idx_x = self.inst.x;
        let vx    = self.regs[idx_x];
        if vx & 0x01 == 0 {
            self.regs[15] = 1;
        }
        self.regs[idx_x] = vx >> 1;
    }

    fn ld_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] = self.regs[idx_y];
    }

    fn ld_vb(&mut self) {
        let idx = self.inst.x;
        self.regs[idx] = self.inst.kk;
    }

    fn ld_bv(&mut self) {
        let idx = self.inst.x;
        let mut vx  = self.regs[idx];
        let mut hundred = 100;

        for i in 0usize..3 {
            let bcd = vx / hundred;
            self.mem[self.i as usize + i] = bcd;
            vx -= bcd * hundred;
            hundred /= 10;
        }
    }

    fn ld_fv(&mut self) {
        let x = self.regs[self.inst.x];
        self.i = (x * 5) as u16;
    }

    fn ld_vi(&mut self) {
        let x = self.regs[self.inst.x] as usize;

        for i in 0usize..x {
           self.regs[i] = self.mem[self.i as usize + i];
        }
    }

    fn ld_iv(&mut self) {
        let x = self.regs[self.inst.x] as usize;

        for i in 0usize..x {
           self.mem[self.i as usize + i] = self.regs[i];
        }
    }

    fn ld_ia(&mut self) {
        self.i = self.inst.nnn;
    }

    fn ld_vdt(&mut self) {
        let idx = self.inst.x;
        self.regs[idx] = self.dt;
    }

    fn ld_dtv(&mut self) {
        let idx: usize = self.inst.x;
        self.dt = self.regs[idx];
    }

    fn ld_stv(&mut self) {
        let idx = self.inst.x;
        self.st = self.regs[idx];
    }

    fn drw(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        let (x, y) = (self.regs[idx_x] as usize, self.regs[idx_y] as usize);
        let n = self.inst.n as usize;

        let start = self.i;

        self.regs[15] = 0;

        for i in 0..n {
            let px = self.mem[(start + i as u16) as usize];
            for j in 0..8 {
                if px & (0x80 >> j) != 0 {
                    // println!("buffer idx: {}", ((x+j) + ((y+i) * 64)));
                    if self.screen.buffer[(x+j + (y+i) * 64) as usize] == 1 {
                        self.regs[15] = 1;
                    }
                    self.screen.buffer[(x+j + (y+i) * 64) as usize] ^= 1;
                }
            }
        }

        for i in 0usize..320 {
            for j in 0usize..640 {
                if self.screen.buffer[(j/10)+(i/10)*64] == 1 {
                    self.screen.renderer.fill_rect(
                        Rect::new(i as i32, j as i32, 1, 1)
                    );
                }
            }
        }
    }

    fn skp(&self) {
        panic!("Not implemented: SKP");
    }

    fn sknp(&self) {
        panic!("Not implemented: SKNP");
    }
}
<commit_msg>fixed rectangle coordinates order<commit_after>#![allow(dead_code)]

use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::fmt::{Debug, Formatter, Result as FmtResult};

use sdl2::Sdl;
use sdl2::rect::Rect;
use ep::FromPrimitive;

use screen::Screen;
use opcodes::Opcodes;

// TODO: move this elsewhere
const FONT_SET: [u8; 80] = [
    0xf0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xf0, 0x10, 0xf0, 0x80, 0xf0, // 2
    0xf0, 0x10, 0xf0, 0x10, 0xf0, // 3
    0x90, 0x90, 0xf0, 0x10, 0x10, // 4
    0xf0, 0x80, 0xf0, 0x10, 0xf0, // 5
    0xf0, 0x80, 0xf0, 0x90, 0xf0, // 6
    0xf0, 0x10, 0x20, 0x40, 0x40, // 7
    0xf0, 0x90, 0xf0, 0x90, 0xf0, // 8
    0xf0, 0x90, 0xf0, 0x10, 0xf0, // 9
    0xf0, 0x90, 0xf0, 0x90, 0x90, // A
    0xe0, 0x90, 0xe0, 0x90, 0xe0, // B
    0xf0, 0x80, 0x80, 0x80, 0x80, // C
    0xe0, 0x90, 0x90, 0x90, 0xe0, // D
    0xf0, 0x80, 0xf0, 0x80, 0xf0, // E
    0xf0, 0x80, 0xf0, 0x80, 0x80, // F
];

// TODO: move this elsewhere
#[derive(Default)]
struct Instruction {
    opcode: u16,
    nnn:    u16,
    kk:     u8,
    x:      usize,
    y:      usize,
    n:      u8,
}

impl Instruction {
    fn new() -> Instruction {
        Instruction::default()
    }

    fn decode(&mut self, data: (u8,u8)) {
        let fstb = data.0;
        let sndb = data.1;

        let op = fstb as u16 >> 4;

        match op {
            0x00                                => {
                self.opcode = op << 12 | sndb as u16;
            },
            0x01 | 0x02 | 0x0a | 0x0b           => {
                self.opcode = op << 12;
                self.nnn    = (fstb as u16 & 0x0F) << 8 | sndb as u16;
            },
            0x03 | 0x04 | 0x06 | 0x07 | 0x0c    => {
                self.opcode = op << 12;
                self.x      = (fstb & 0x0F) as usize;
                self.kk     = sndb;
            },
            0x05 | 0x08 | 0x09                  => {
                self.opcode = op << 12 | sndb as u16 & 0x0F;
                self.x      = (fstb & 0x0F) as usize;
                self.y      = ((sndb & 0xF0) >> 4) as usize;
            },
            0x0e | 0x0f                         => {
                self.opcode = op << 12 | sndb as u16;
                self.x      = (fstb & 0x0F) as usize;
            },
            0x0d                                => {
                self.opcode = op << 12;
                self.x      = (fstb & 0x0F) as usize;
                self.y      = ((sndb & 0xF0) >> 4) as usize;
                self.n      = sndb & 0x0F;
            },
            _                                   => {
                self.opcode = 0xFFFF;
            },
        }
    }

    fn get_opcode(&self) -> u16 {
        self.opcode
    }

    fn set_opcode(&mut self, val: u16) {
        self.opcode = val;
    }
}

pub struct Chip8<'a> {
    regs:   [u8; 16],
    i:      u16,
    dt:     u8,
    st:     u8,
    pc:     usize,

    inst:   Instruction,
    jmp:    bool,

    mem:    [u8; 4096],
    stack:  Vec<u16>,
    screen: Screen<'a>,
}

impl<'a> Debug for Chip8<'a> {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
         self.regs[..].fmt(f)
    }
}

impl<'a> Chip8<'a> {
    pub fn new(sdl: &Sdl) -> Chip8<'a> {
        Chip8 {
            regs:   [0; 16],
            i:      0,
            dt:     0,
            st:     0,
            pc:     0x200,
            inst:   Instruction::new(),
            jmp:    false,
            mem:    [0; 4096],
            stack:  vec![],
            screen: Screen::new(sdl),
        }
    }

    pub fn load_rom(&mut self, path: &str) {
        let mut rom: Vec<u8> = Vec::new();
        let mut file = File::open(Path::new(path)).unwrap();
        file.read_to_end(&mut rom).unwrap();
        for i in 0usize..rom.len() {
            self.mem[0x200 + i] = rom[i];
        }
        // TODO: move this
        for i in 0usize..FONT_SET.len() {
            self.mem[0x00 + i] = FONT_SET[i];
        }
    }

    pub fn fetch(&mut self) {
        let raw_data = (self.mem[self.pc], self.mem[self.pc +1]);
        self.inst.decode(raw_data);

        self.jmp = false;

        println!("{:#x}: {:#x}", self.pc, self.inst.get_opcode());

        match Opcodes::from_u16(self.inst.get_opcode()).unwrap() {
            Opcodes::CLS    => self.cls(),
            Opcodes::RET    => self.ret(),
            Opcodes::JMP    => self.jmp(),
            Opcodes::JMP_VA => self.jmp_va(),
            Opcodes::CALL   => self.call(),
            Opcodes::SE_VB  => self.se_vb(),
            Opcodes::SE_VV  => self.se_vv(),
            Opcodes::SNE_VB => self.sne_vb(),
            Opcodes::SNE_VV => self.sne_vv(),
            Opcodes::OR     => self.or(),
            Opcodes::ADD_VB => self.add_vb(),
            Opcodes::ADD_VV => self.add_vv(),
            Opcodes::ADD_IV => self.add_iv(),
            Opcodes::XOR    => self.xor(),
            Opcodes::AND    => self.and(),
            Opcodes::LD_VB  => self.ld_vb(),
            Opcodes::LD_BV  => self.ld_bv(),
            Opcodes::LD_VV  => self.ld_vv(),
            Opcodes::LD_VI  => self.ld_vi(),
            Opcodes::LD_IV  => self.ld_iv(),
            Opcodes::LD_FV  => self.ld_fv(),
            Opcodes::LD_IA  => self.ld_ia(),
            Opcodes::LD_VDT => self.ld_vdt(),
            Opcodes::LD_DTV => self.ld_dtv(),
            Opcodes::LD_STV => self.ld_stv(),
            Opcodes::SHR    => self.shr(),
            Opcodes::DRW    => self.drw(),
            _               => panic!("Unrecognized opcode: {:?}", self.inst.get_opcode()),
        }

        println!("i: {}", self.i);

        if !self.jmp { self.inc_pc(); }
        // TODO: refactor this to self.screen.present();
        self.screen.renderer.present();
    }

    fn set_pc(&mut self, addr: u16) {
        self.pc = addr as usize;
    }

    fn inc_pc(&mut self) {
        self.pc += 2;
    }

    fn cls(&mut self) {
        self.screen.clear();
    }

    fn ret(&mut self) {
        let addr = self.stack.pop().unwrap();
        self.set_pc(addr);
    }

    fn jmp(&mut self) {
        let addr: u16 = self.inst.nnn;
        self.set_pc(addr);
        self.jmp = true;
    }

    fn jmp_va(&mut self) {
        let offset = self.regs[0] as u16;
        let addr = self.inst.nnn + offset;
        self.set_pc(addr);
        self.jmp = true;
    }

    fn call(&mut self) {
        let old_addr: u16 = self.pc as u16;
        let cur_addr = self.inst.nnn;
        self.stack.push(old_addr);
        self.set_pc(cur_addr);
        self.jmp = true;
    }

    fn se_vb(&mut self) {
        let idx = self.inst.x;
        if self.regs[idx] == self.inst.kk {
            self.inc_pc();
        }
    }

    fn se_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        if self.regs[idx_x] == self.regs[idx_y] {
            self.inc_pc();
        }
    }

    fn sne_vb(&mut self) {
        let idx = self.inst.x;
        if self.regs[idx] != self.inst.kk {
            self.inc_pc();
        }
    }

    fn sne_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        if self.regs[idx_x] != self.regs[idx_y] {
            self.inc_pc();
        }
    }

    fn or(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] |= self.regs[idx_y]
    }

    fn and(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] &= self.regs[idx_y];
    }

    fn add_vb(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] += self.regs[idx_y];
    }

    fn add_vv(&self) {
        panic!("Not implemented add_vv");
    }

    fn add_iv(&mut self) {
        let idx = self.inst.x;
        self.i += self.regs[idx] as u16;
    }

    fn xor(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] ^= self.regs[idx_y];
    }

    fn shr(&mut self) {
        let idx_x = self.inst.x;
        let vx    = self.regs[idx_x];
        if vx & 0x01 == 0 {
            self.regs[15] = 1;
        }
        self.regs[idx_x] = vx >> 1;
    }

    fn ld_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] = self.regs[idx_y];
    }

    fn ld_vb(&mut self) {
        let idx = self.inst.x;
        self.regs[idx] = self.inst.kk;
    }

    fn ld_bv(&mut self) {
        let idx = self.inst.x;
        let mut vx  = self.regs[idx];
        let mut hundred = 100;

        for i in 0usize..3 {
            let bcd = vx / hundred;
            self.mem[self.i as usize + i] = bcd;
            vx -= bcd * hundred;
            hundred /= 10;
        }
    }

    fn ld_fv(&mut self) {
        let x = self.regs[self.inst.x];
        self.i = (x * 5) as u16;
    }

    fn ld_vi(&mut self) {
        let x = self.regs[self.inst.x] as usize;

        for i in 0usize..x {
           self.regs[i] = self.mem[self.i as usize + i];
        }
    }

    fn ld_iv(&mut self) {
        let x = self.regs[self.inst.x] as usize;

        for i in 0usize..x {
           self.mem[self.i as usize + i] = self.regs[i];
        }
    }

    fn ld_ia(&mut self) {
        self.i = self.inst.nnn;
    }

    fn ld_vdt(&mut self) {
        let idx = self.inst.x;
        self.regs[idx] = self.dt;
    }

    fn ld_dtv(&mut self) {
        let idx: usize = self.inst.x;
        self.dt = self.regs[idx];
    }

    fn ld_stv(&mut self) {
        let idx = self.inst.x;
        self.st = self.regs[idx];
    }

    fn drw(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        let (x, y) = (self.regs[idx_x] as usize, self.regs[idx_y] as usize);
        let n = self.inst.n as usize;

        let start = self.i;

        self.regs[15] = 0;

        for i in 0..n {
            let px = self.mem[(start + i as u16) as usize];
            for j in 0..8 {
                if px & (0x80 >> j) != 0 {
                    // println!("buffer idx: {}", ((x+j) + ((y+i) * 64)));
                    if self.screen.buffer[(x+j + (y+i) * 64) as usize] == 1 {
                        self.regs[15] = 1;
                    }
                    self.screen.buffer[(x+j + (y+i) * 64) as usize] ^= 1;
                }
            }
        }

        for i in 0usize..320 {
            for j in 0usize..640 {
                if self.screen.buffer[(j/10)+(i/10)*64] == 1 {
                    self.screen.renderer.fill_rect(
                        Rect::new(j as i32, i as i32, 1, 1)
                    );
                }
            }
        }
    }

    fn skp(&self) {
        panic!("Not implemented: SKP");
    }

    fn sknp(&self) {
        panic!("Not implemented: SKNP");
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
// inspired by http://primesieve.org/segmented_sieve.html

use std::collections::{Bitv};
use std::{cmp};
use std::num::Float;

use Primes;

pub struct Sieve {
    small: Primes,
    sieve: Bitv,
    primes: Vec<uint>,
    next: Vec<uint>,

    low: uint,
    current: uint,
    limit: uint,
}

const CACHE: uint = 32 << 10;
// 8 for the bit vector, 2 for storing odd numbers only
const SEG_SIZE: uint = 16 * CACHE;

impl Sieve {
    pub fn new(limit: uint) -> Sieve {
        let small = Primes::sieve((limit as f64).sqrt() as uint + 1);
        let current = 2;
        let low = 0;

        Sieve {
            small: small,
            sieve: Bitv::with_capacity(SEG_SIZE, false),
            primes: vec![],
            next: vec![],

            low: low,
            current: current,
            limit: limit
        }
    }

    pub fn next(&mut self) -> Option<(uint, &Bitv)> {
        if self.low >= self.limit {
            return None
        }

        let low = self.low;
        self.low += SEG_SIZE;
        let high = cmp::min(low + SEG_SIZE - 1, self.limit);
        self.sieve.set_all();

        while self.current * self.current <= high {
            if self.small.is_prime(self.current) {
                self.primes.push(self.current);
                self.next.push(self.current * self.current - low);
            }
            self.current += 1
        }
        for i in range(1, self.primes.len()) {
            let mut j = self.next[i] / 2;
            let k = self.primes[i];
            while j < SEG_SIZE / 2 {
                self.sieve.set(j, false);
                j += k;
            }

            self.next[i] = (2 * j + 1) - SEG_SIZE;
        }
        if low == 0 {
            // 1 is not prime.
            self.sieve.set(0, false);
        }

        Some((low, &self.sieve))
    }
}

#[cfg(test)]
mod tests {
    use test::Bencher;
    use super::Sieve;
    use std::iter::range_step;

    #[test]
    fn test() {
        let mut sieve = Sieve::new(2000);
        let primes = ::Primes::sieve(2000);

        loop {
            let (low, next) = match sieve.next() {
                None => break,
                Some(x) => x,
            };
            println!("tick {}", next.len());

            for i in range_step(low + 1, low + next.len(), 2) {
                if i > 2000 { break }
                assert!(primes.is_prime(i) == next[(i - low) / 2],
                        "failed for {} (is prime = {})", i, primes.is_prime(i));
            }
        }
    }

    fn run(b: &mut Bencher, n: uint) {
        b.iter(|| {
            let mut sieve = Sieve::new(n);
            while sieve.next().is_some() {}
        })
    }

    #[bench]
    fn sieve_small(b: &mut Bencher) {
        run(b, 100)
    }
    #[bench]
    fn sieve_medium(b: &mut Bencher) {
        run(b, 10_000)
    }
    #[bench]
    fn sieve_large(b: &mut Bencher) {
        run(b, 100_000)
    }
    #[bench]
    fn sieve_huge(b: &mut Bencher) {
        run(b, 10_000_000)
    }
}
<commit_msg>Fiddle with naming of StreamingSieve.<commit_after>#![allow(dead_code)]

use std::collections::{Bitv};
use std::{cmp};
use std::num::Float;

use Primes;

/// A segmented sieve that yields only a small run of primes at a
/// time.
///
/// This is heavily inspired by this [segmented
/// sieve](http://primesieve.org/segmented_sieve.html) code.
pub struct StreamingSieve {
    small: Primes,
    sieve: Bitv,
    primes: Vec<uint>,
    next: Vec<uint>,

    low: uint,
    current: uint,
    limit: uint,
}

const CACHE: uint = 32 << 10;
// 8 for the bit vector, 2 for storing odd numbers only
const SEG_SIZE: uint = 16 * CACHE;

impl StreamingSieve {
    /// Create a new instance of the streaming sieve that will
    /// correctly progressively filter primes up to `limit`.
    pub fn new(limit: uint) -> StreamingSieve {
        let small = Primes::sieve((limit as f64).sqrt() as uint + 1);
        let current = 2;
        let low = 0;

        StreamingSieve {
            small: small,
            sieve: Bitv::with_capacity(SEG_SIZE, false),
            primes: vec![],
            next: vec![],

            low: low,
            current: current,
            limit: limit
        }
    }

    /// Extract the next chunk of filtered primes, the return value is
    /// `Some((low, v))` or `None` if the sieve has reached the limit.
    ///
    /// The vector stores bits for each odd number starting at `low`.
    /// Bit `n` of `v` is set if and only if `low + 2 * n + 1` is
    /// prime.
    ///
    /// NB. the prime 2 is not included in any of these sieves and so
    /// needs special handling.
    pub fn next(&mut self) -> Option<(uint, &Bitv)> {
        if self.low >= self.limit {
            return None
        }

        let low = self.low;
        self.low += SEG_SIZE;
        let high = cmp::min(low + SEG_SIZE - 1, self.limit);
        self.sieve.set_all();

        while self.current * self.current <= high {
            if self.small.is_prime(self.current) {
                self.primes.push(self.current);
                self.next.push(self.current * self.current - low);
            }
            self.current += 1
        }
        for i in range(1, self.primes.len()) {
            let mut j = self.next[i] / 2;
            let k = self.primes[i];
            while j < SEG_SIZE / 2 {
                self.sieve.set(j, false);
                j += k;
            }

            self.next[i] = (2 * j + 1) - SEG_SIZE;
        }
        if low == 0 {
            // 1 is not prime.
            self.sieve.set(0, false);
        }

        Some((low, &self.sieve))
    }
}

#[cfg(test)]
mod tests {
    use test::Bencher;
    use super::StreamingSieve;
    use std::iter::range_step;

    #[test]
    fn test() {
        let mut sieve = StreamingSieve::new(2000);
        let primes = ::Primes::sieve(2000);

        loop {
            let (low, next) = match sieve.next() {
                None => break,
                Some(x) => x,
            };
            println!("tick {}", next.len());

            for i in range_step(low + 1, low + next.len(), 2) {
                if i > 2000 { break }
                assert!(primes.is_prime(i) == next[(i - low) / 2],
                        "failed for {} (is prime = {})", i, primes.is_prime(i));
            }
        }
    }

    fn run(b: &mut Bencher, n: uint) {
        b.iter(|| {
            let mut sieve = StreamingSieve::new(n);
            while sieve.next().is_some() {}
        })
    }

    #[bench]
    fn sieve_small(b: &mut Bencher) {
        run(b, 100)
    }
    #[bench]
    fn sieve_medium(b: &mut Bencher) {
        run(b, 10_000)
    }
    #[bench]
    fn sieve_large(b: &mut Bencher) {
        run(b, 100_000)
    }
    #[bench]
    fn sieve_huge(b: &mut Bencher) {
        run(b, 10_000_000)
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Ilkka Rauta
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate linuxdvb;

use linuxdvb::{Frontend,ReadWriteMode,BlockMode};

fn main() {
    let frontend = Frontend::open("/dev/dvb/adapter0/frontend0", ReadWriteMode::ReadOnly, BlockMode::NonBlocking).unwrap();
    println!("{:?}", frontend.get_info().unwrap());
}<commit_msg>Improve the frontend-info program.<commit_after>// Copyright 2015 Ilkka Rauta
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate linuxdvb;

use std::fs;
use std::error::Error;
use std::path::Path;

use linuxdvb::{Frontend,ReadWriteMode,BlockMode,properties};
use linuxdvb::properties::GetProperty as GP;
use linuxdvb::properties::GetPropertyValue as GPV;

type SimpleResult = Result<(), Box<Error>>;

fn show_frontend_info(path: &Path) -> SimpleResult {
    let frontend = Frontend::open("/dev/dvb/adapter0/frontend0", ReadWriteMode::ReadOnly, BlockMode::NonBlocking).unwrap();
    println!("Device: {}", path.to_string_lossy());
    let info = try!(frontend.get_info());
    println!("Name: {}", info.name);

    let properties = try!(frontend.get_properties(&[
        GP::DtvEnumDelsys
        ]));
    if let GPV::DtvEnumDelsys(delsys) = properties[0] {
        println!("Supported delivery systems: {:?}", delsys.delivery_systems());
    }

    println!("Minimal frequency: {}", info.frequency_min);
    println!("Maximal frequency: {}", info.frequency_max);
    println!("Frequency step: {}", info.frequency_stepsize);
    println!("Frequency tolerance: {}", info.frequency_tolerance);
    println!("Minimal symbol rate: {}", info.symbol_rate_min);
    println!("Maximal symbol rate: {}", info.symbol_rate_max);
    println!("Maximal symbol rate tolerance: {}", info.symbol_rate_tolerance);

    Ok(())
}

fn iterate_devices(adapter_dir: &Path) -> SimpleResult {
    for device in try!(fs::read_dir(adapter_dir)) {
        let device = try!(device);
        let is_frontend = device.path().file_name()
            .map(|f| f.to_str())
            .unwrap_or(None)
            .map_or(false, |f| f.starts_with("frontend"));
        if is_frontend {
            try!(show_frontend_info(&device.path()));
        }
    }
    Ok(())
}

fn iterate_adapters() -> SimpleResult {
    for adapter_dir in try!(fs::read_dir("/dev/dvb")) {
        let adapter_dir = try!(adapter_dir);
        try!(iterate_devices(&adapter_dir.path()));
    }
    Ok(())
}

fn main() {
    match iterate_adapters() {
        Ok(_) => (),
        Err(error) => println!("Error: {}", error.description())
    }
}<|endoftext|>


<commit_before>use std::fmt;
use std::cell::Cell;

/// The Memory Management Unit.
/// Provides access to all mapped memory in the system, including I/O and graphics.
pub struct MMU {
    pub loading_bios: Cell<bool>,
    data: [u8; 65536],
}

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

impl MMU {
    pub fn new() -> MMU {
        MMU {
            loading_bios: Cell::new(true),
            data: [1; 65536],
        }
    }

    /// reads a byte at the memory address specified
    pub fn read_b(&self, addr: u16) -> u8 {
        // when PC first reaches 0x100, the BIOS data is not addressable anymore.
        // using Cell sucks, but this is one of the few cases mentioned by official docs where
        // it is an option. Read is logically an immutable operation, but we need to make the
        // bios unaddressable after its first read.
        if self.loading_bios.get() && addr == 0x0100 {
            self.loading_bios.set(false);
        }

        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => 0,
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0000...0x3FFF if !self.loading_bios.get() => 0,
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => 0,
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => 0,
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize]
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => self.data[addr as usize],
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => self.data[(addr - 0x2000) as usize],
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => 0,
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => 0,
            // I/O ports
            0xFF00...0xFF4B => 0,
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => 0,
            // Interrupt Enable register
            0xFFFF => 0,

            _ => panic!("tried to write at unkown address: {:4X}", addr)
        }
    }

    pub fn write_b(&mut self, addr: u16, data: u8) {
        self.data[addr as usize] = data;
    }
}
<commit_msg>Added module comment<commit_after>/// The memory module.
/// This module declares a single struct, the Memory Management Unit (MMU).
/// The MMU holds all components that are addressable via memory, instead of having
/// a single Memory object holding all data and shared among components.
/// This approach simplifies design and avoids having to resort to `Rc` pointers or
/// adding lifetime specifications to every struct.
///
/// Each component (GPU, I/O registers, Cartridges) keeps its own data, the MMU simply
/// accesses it according to the memory mapping.

use std::fmt;
use std::cell::Cell;

/// The Memory Management Unit.
/// Provides access to all mapped memory in the system, including I/O and graphics.
pub struct MMU {
    pub loading_bios: Cell<bool>,
    data: [u8; 65536],
}

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

impl MMU {
    pub fn new() -> MMU {
        MMU {
            loading_bios: Cell::new(true),
            data: [1; 65536],
        }
    }

    /// reads a byte at the memory address specified
    pub fn read_b(&self, addr: u16) -> u8 {
        // when PC first reaches 0x100, the BIOS data is not addressable anymore.
        // using Cell sucks, but this is one of the few cases mentioned by official docs where
        // it is an option. Read is logically an immutable operation, but we need to make the
        // bios unaddressable after its first read.
        if self.loading_bios.get() && addr == 0x0100 {
            self.loading_bios.set(false);
        }

        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => 0,
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0000...0x3FFF if !self.loading_bios.get() => 0,
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => 0,
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => 0,
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize]
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => self.data[addr as usize],
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => self.data[(addr - 0x2000) as usize],
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => 0,
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => 0,
            // I/O ports
            0xFF00...0xFF4B => 0,
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => 0,
            // Interrupt Enable register
            0xFFFF => 0,

            _ => panic!("tried to write at unkown address: {:4X}", addr),
        }
    }

    pub fn write_b(&mut self, addr: u16, data: u8) {
        self.data[addr as usize] = data;
    }
}
<|endoftext|>


<commit_before>use std::io::process::{Command,ProcessOutput,ProcessExit,ExitStatus,ExitSignal};
use std::io::IoError;
use std::fmt;
use std::fmt::{Show, Formatter};

use TomlError = toml::Error;

pub trait CargoError {
    fn description(&self) -> String;
    fn detail(&self) -> Option<String> { None }
    fn cause<'a>(&'a self) -> Option<&'a CargoError> { None }
    fn is_human(&self) -> bool { false }

    fn to_error<E: FromError<Self>>(self) -> E {
        FromError::from_error(self)
    }

    fn box_error(self) -> Box<CargoError> {
        box self as Box<CargoError>
    }

    fn concrete(&self) -> ConcreteCargoError {
        ConcreteCargoError {
            description: self.description(),
            detail: self.detail(),
            cause: self.cause().map(|c| box c.concrete() as Box<CargoError>),
            is_human: self.is_human()
        }
    }

    fn with_cause<E: CargoError>(self, cause: E) -> Box<CargoError> {
        let mut concrete = self.concrete();
        concrete.cause = Some(cause.box_error());
        box concrete as Box<CargoError>
    }

    fn mark_human(self) -> Box<CargoError> {
        let mut concrete = self.concrete();
        concrete.is_human = true;
        box concrete as Box<CargoError>
    }
}

pub trait FromError<E> {
    fn from_error(error: E) -> Self;
}

impl<E: CargoError> FromError<E> for Box<CargoError> {
    fn from_error(error: E) -> Box<CargoError> {
        error.box_error()
    }
}

macro_rules! from_error (
    ($ty:ty) => {
        impl FromError<$ty> for $ty {
            fn from_error(error: $ty) -> $ty {
                error
            }
        }
    }
)

impl Show for Box<CargoError> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));
        Ok(())
    }
}

impl CargoError for Box<CargoError> {
    fn description(&self) -> String {
        (*self).description()
    }

    fn detail(&self) -> Option<String> {
        (*self).detail()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        (*self).cause()
    }

    fn is_human(&self) -> bool {
        (*self).is_human()
    }

    fn box_error(self) -> Box<CargoError> {
        self
    }
}

pub type CargoResult<T> = Result<T, Box<CargoError>>;

pub trait BoxError<T> {
    fn box_error(self) -> CargoResult<T>;
}

pub trait ChainError<T> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T> ;
}

impl<T, E: CargoError> BoxError<T> for Result<T, E> {
    fn box_error(self) -> CargoResult<T> {
        self.map_err(|err| err.box_error())
    }
}

impl<T, E: CargoError> ChainError<T> for Result<T, E> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T>  {
        self.map_err(|err| {
            callback().with_cause(err)
        })
    }
}

impl CargoError for IoError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(IoError)

impl CargoError for TomlError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(TomlError)

pub struct ProcessError {
    pub msg: String,
    pub command: String,
    pub exit: Option<ProcessExit>,
    pub output: Option<ProcessOutput>,
    pub detail: Option<String>,
    pub cause: Option<Box<CargoError>>
}

from_error!(ProcessError)

impl Show for ProcessError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        write!(f, "{} (status={})", self.msg, exit)
    }
}

impl CargoError for ProcessError {
    fn description(&self) -> String {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        format!("{} (status={})", self.msg, exit)
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn with_cause<E: CargoError>(mut self, err: E) -> Box<CargoError> {
        self.cause = Some(err.box_error());
        box self as Box<CargoError>
    }
}

pub struct ConcreteCargoError {
    description: String,
    detail: Option<String>,
    cause: Option<Box<CargoError>>,
    is_human: bool
}

impl Show for ConcreteCargoError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.description)
    }
}

impl CargoError for ConcreteCargoError {
    fn description(&self) -> String {
        self.description.clone()
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn with_cause<E: CargoError>(mut self, err: E) -> Box<CargoError> {
        self.cause = Some(err.box_error());
        box self as Box<CargoError>
    }

    fn mark_human(mut self) -> Box<CargoError> {
        self.is_human = true;
        box self as Box<CargoError>
    }

    fn is_human(&self) -> bool {
        self.is_human
    }
}

pub type CliResult<T> = Result<T, CliError>;

#[deriving(Show)]
pub struct CliError {
    pub error: Box<CargoError>,
    pub exit_code: uint
}

impl CliError {
    pub fn new<S: Str>(error: S, code: uint) -> CliError {
        let error = human(error.as_slice().to_str());
        CliError::from_boxed(error, code)
    }

    pub fn from_error<E: CargoError + 'static>(error: E, code: uint) -> CliError {
        let error = box error as Box<CargoError>;
        CliError::from_boxed(error, code)
    }

    pub fn from_boxed(error: Box<CargoError>, code: uint) -> CliError {
        let error = if error.is_human() {
            error
        } else {
            human("An unknown error occurred").with_cause(error)
        };

        CliError { error: error, exit_code: code }
    }
}

pub fn process_error<S: Str>(msg: S, command: &Command, status: Option<&ProcessExit>, output: Option<&ProcessOutput>) -> ProcessError {
    ProcessError {
        msg: msg.as_slice().to_str(),
        command: command.to_str(),
        exit: status.map(|o| o.clone()),
        output: output.map(|o| o.clone()),
        detail: None,
        cause: None
    }
}

pub fn internal_error<S1: Str, S2: Str>(error: S1, detail: S2) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: Some(detail.as_slice().to_str()),
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn internal<S1: Str>(error: S1) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn human<S: Str>(error: S) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: true
    } as Box<CargoError>
}
<commit_msg>Provide more context on process errors<commit_after>use std::io::process::{Command,ProcessOutput,ProcessExit,ExitStatus,ExitSignal};
use std::io::IoError;
use std::fmt;
use std::fmt::{Show, Formatter};

use TomlError = toml::Error;

pub trait CargoError {
    fn description(&self) -> String;
    fn detail(&self) -> Option<String> { None }
    fn cause<'a>(&'a self) -> Option<&'a CargoError> { None }
    fn is_human(&self) -> bool { false }

    fn to_error<E: FromError<Self>>(self) -> E {
        FromError::from_error(self)
    }

    fn box_error(self) -> Box<CargoError> {
        box self as Box<CargoError>
    }

    fn concrete(&self) -> ConcreteCargoError {
        ConcreteCargoError {
            description: self.description(),
            detail: self.detail(),
            cause: self.cause().map(|c| box c.concrete() as Box<CargoError>),
            is_human: self.is_human()
        }
    }

    fn with_cause<E: CargoError>(self, cause: E) -> Box<CargoError> {
        let mut concrete = self.concrete();
        concrete.cause = Some(cause.box_error());
        box concrete as Box<CargoError>
    }

    fn mark_human(self) -> Box<CargoError> {
        let mut concrete = self.concrete();
        concrete.is_human = true;
        box concrete as Box<CargoError>
    }
}

pub trait FromError<E> {
    fn from_error(error: E) -> Self;
}

impl<E: CargoError> FromError<E> for Box<CargoError> {
    fn from_error(error: E) -> Box<CargoError> {
        error.box_error()
    }
}

macro_rules! from_error (
    ($ty:ty) => {
        impl FromError<$ty> for $ty {
            fn from_error(error: $ty) -> $ty {
                error
            }
        }
    }
)

impl Show for Box<CargoError> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));
        Ok(())
    }
}

impl CargoError for Box<CargoError> {
    fn description(&self) -> String {
        (*self).description()
    }

    fn detail(&self) -> Option<String> {
        (*self).detail()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        (*self).cause()
    }

    fn is_human(&self) -> bool {
        (*self).is_human()
    }

    fn box_error(self) -> Box<CargoError> {
        self
    }
}

pub type CargoResult<T> = Result<T, Box<CargoError>>;

pub trait BoxError<T> {
    fn box_error(self) -> CargoResult<T>;
}

pub trait ChainError<T> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T> ;
}

impl<T, E: CargoError> BoxError<T> for Result<T, E> {
    fn box_error(self) -> CargoResult<T> {
        self.map_err(|err| err.box_error())
    }
}

impl<T, E: CargoError> ChainError<T> for Result<T, E> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T>  {
        self.map_err(|err| {
            callback().with_cause(err)
        })
    }
}

impl CargoError for IoError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(IoError)

impl CargoError for TomlError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(TomlError)

pub struct ProcessError {
    pub msg: String,
    pub command: String,
    pub exit: Option<ProcessExit>,
    pub output: Option<ProcessOutput>,
    pub detail: Option<String>,
    pub cause: Option<Box<CargoError>>
}

from_error!(ProcessError)

impl Show for ProcessError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        try!(write!(f, "{} (status={})", self.msg, exit));
        match self.output {
            Some(ref out) => {
                match str::from_utf8(out.output.as_slice()) {
                    Some(s) if s.trim().len() > 0 => {
                        try!(write!(f, "\n--- stdout\n{}", s));
                    }
                    Some(..) | None => {}
                }
                match str::from_utf8(out.error.as_slice()) {
                    Some(s) if s.trim().len() > 0 => {
                        try!(write!(f, "\n--- stderr\n{}", s));
                    }
                    Some(..) | None => {}
                }
            }
            None => {}
        }
        Ok(())
    }
}

impl CargoError for ProcessError {
    fn description(&self) -> String { self.to_str() }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn with_cause<E: CargoError>(mut self, err: E) -> Box<CargoError> {
        self.cause = Some(err.box_error());
        box self as Box<CargoError>
    }
}

pub struct ConcreteCargoError {
    description: String,
    detail: Option<String>,
    cause: Option<Box<CargoError>>,
    is_human: bool
}

impl Show for ConcreteCargoError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.description)
    }
}

impl CargoError for ConcreteCargoError {
    fn description(&self) -> String {
        self.description.clone()
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn with_cause<E: CargoError>(mut self, err: E) -> Box<CargoError> {
        self.cause = Some(err.box_error());
        box self as Box<CargoError>
    }

    fn mark_human(mut self) -> Box<CargoError> {
        self.is_human = true;
        box self as Box<CargoError>
    }

    fn is_human(&self) -> bool {
        self.is_human
    }
}

pub type CliResult<T> = Result<T, CliError>;

#[deriving(Show)]
pub struct CliError {
    pub error: Box<CargoError>,
    pub exit_code: uint
}

impl CliError {
    pub fn new<S: Str>(error: S, code: uint) -> CliError {
        let error = human(error.as_slice().to_str());
        CliError::from_boxed(error, code)
    }

    pub fn from_error<E: CargoError + 'static>(error: E, code: uint) -> CliError {
        let error = box error as Box<CargoError>;
        CliError::from_boxed(error, code)
    }

    pub fn from_boxed(error: Box<CargoError>, code: uint) -> CliError {
        let error = if error.is_human() {
            error
        } else {
            human("An unknown error occurred").with_cause(error)
        };

        CliError { error: error, exit_code: code }
    }
}

pub fn process_error<S: Str>(msg: S, command: &Command, status: Option<&ProcessExit>, output: Option<&ProcessOutput>) -> ProcessError {
    ProcessError {
        msg: msg.as_slice().to_str(),
        command: command.to_str(),
        exit: status.map(|o| o.clone()),
        output: output.map(|o| o.clone()),
        detail: None,
        cause: None
    }
}

pub fn internal_error<S1: Str, S2: Str>(error: S1, detail: S2) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: Some(detail.as_slice().to_str()),
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn internal<S1: Str>(error: S1) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn human<S: Str>(error: S) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: true
    } as Box<CargoError>
}
<|endoftext|>