<commit_before>extern mod std;

use libc::c_char;
use libc::c_int;
use libc::size_t;
use libc::c_ulong;
use libc::c_void;
use ptr::null;
use ptr::addr_of;
use ptr::mut_addr_of;
use ptr::to_mut_unsafe_ptr;
use str::as_c_str;

struct mpz_struct {
  _mp_alloc: c_int,
  _mp_size: c_int,
  _mp_d: *c_void
}

type mpz_srcptr = *const mpz_struct;
type mpz_ptr = *mut mpz_struct;

extern mod gmp {
  fn __gmpz_init(x: mpz_ptr);
  fn __gmpz_init_set(rop: mpz_ptr, op: mpz_srcptr);
  fn __gmpz_init_set_str(rop: mpz_ptr, str: *c_char, base: c_int) -> c_int;
  fn __gmpz_clear(x: mpz_ptr);
  fn __gmpz_set_str(rop: mpz_ptr, str: *c_char, base: c_int) -> c_int;
  fn __gmpz_get_str(str: *c_char, base: c_int, op: mpz_srcptr) -> *c_char;
  pure fn __gmpz_sizeinbase(op: mpz_srcptr, base: c_int) -> size_t;
  pure fn __gmpz_cmp(op: mpz_srcptr, op2: mpz_srcptr) -> c_int;
  pure fn __gmpz_cmp_ui(op1: mpz_srcptr, op2: c_ulong) -> c_int;
  fn __gmpz_add(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_sub(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_mul(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_neg(rop: mpz_ptr, op: mpz_srcptr);
  fn __gmpz_tdiv_q(r: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
  fn __gmpz_mod(r: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
}

use gmp::*;

pub struct Mpz {
  priv mpz: mpz_struct,

  drop {
    __gmpz_clear(mut_addr_of(&self.mpz));
  }
}

impl Mpz {
  fn set_str(&mut self, s: &str, base: int) -> bool {
    let mpz = to_mut_unsafe_ptr(&mut self.mpz);
    let r = as_c_str(s, { |s| __gmpz_set_str(mpz, s, base as c_int) });
    r == 0
  }

  pure fn to_str_radix(&self, base: int) -> ~str unsafe {
    let length = self.size_in_base(10) + 2;
    let dst = vec::to_mut(vec::from_elem(length, '0'));
    let pdst = vec::raw::to_ptr(dst);

    str::raw::from_c_str(__gmpz_get_str(pdst as *c_char, base as c_int,
                         addr_of(&self.mpz)))
  }

  pure fn size_in_base(&self, base: int) -> uint {
    __gmpz_sizeinbase(addr_of(&self.mpz), base as c_int) as uint
  }

  // TODO: implement the clone::Clone trait when 0.5 is released
  pure fn clone() -> Mpz unsafe {
    let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
    __gmpz_init_set(mut_addr_of(&mpz), addr_of(&self.mpz));
    Mpz { mpz: mpz }
  }
}

impl Mpz: cmp::Eq {
  pure fn eq(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) == 0
  }
  pure fn ne(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) != 0
  }
}

impl Mpz: cmp::Ord {
  pure fn lt(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) < 0
  }
  pure fn le(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) <= 0
  }
  pure fn gt(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) > 0
  }
  pure fn ge(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) >= 0
  }
}

impl Mpz: num::Num {
  pure fn add(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_add(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn sub(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_sub(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn mul(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_mul(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn div(other: &Mpz) -> Mpz unsafe {
    if __gmpz_cmp_ui(addr_of(&self.mpz), 0) == 0 {
      fail ~"divide by zero";
    }

    let res = init();
    __gmpz_tdiv_q(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn modulo(other: &Mpz) -> Mpz unsafe {
    if __gmpz_cmp_ui(addr_of(&self.mpz), 0) == 0 {
      fail ~"divide by zero";
    }

    let res = init();
    __gmpz_mod(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn neg() -> Mpz unsafe {
    let res = init();
    __gmpz_neg(mut_addr_of(&res.mpz), addr_of(&self.mpz));
    res
  }
  pure fn to_int() -> int {
    fail ~"not implemented";
  }
  static pure fn from_int(other: int) -> Mpz {
    // the gmp functions dealing with longs aren't usable here - long is only
    // guaranteed to be at least 32-bit
    option::unwrap(from_str(other.to_str()))
  }
}

pub pure fn from_str_radix(s: &str, base: int) -> Option<Mpz> unsafe {
  let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
  let mpz_ptr = mut_addr_of(&mpz);
  let r = as_c_str(s, { |s| __gmpz_init_set_str(mpz_ptr, s, base as c_int) });
  if r == 0 {
    Some(Mpz { mpz: mpz })
  } else {
    __gmpz_clear(mpz_ptr);
    None
  }
}

pub pure fn from_str(s: &str) -> Option<Mpz> {
  from_str_radix(s, 10)
}

impl Mpz : from_str::FromStr {
  static fn from_str(s: &str) -> Option<Mpz> {
    from_str(s)
  }
}

impl Mpz : to_str::ToStr {
  pure fn to_str() -> ~str {
    self.to_str_radix(10)
  }
}

pub pure fn init() -> Mpz unsafe {
  let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
  __gmpz_init(mut_addr_of(&mpz));
  Mpz { mpz: mpz }
}

#[cfg(test)]
mod tests {
  #[test]
  fn size_in_base() {
    let x = option::unwrap(from_str("150000"));
    assert(x.size_in_base(10) == 6);
  }

  #[test]
  fn eq() {
    let x = option::unwrap(from_str("4242142195"));
    let y = option::unwrap(from_str("4242142195"));
    let z = option::unwrap(from_str("4242142196"));

    assert(x == y);
    assert(x != z);
    assert(y != z);
  }

  #[test]
  fn ord() {
    let x = option::unwrap(from_str("40000000000000000000000"));
    let y = option::unwrap(from_str("45000000000000000000000"));
    let z = option::unwrap(from_str("50000000000000000000000"));

    assert(x < y && x < z && y < z);
    assert(x <= x && x <= y && x <= z && y <= z);
    assert(z > y && z > x && y > x);
    assert(z >= z && z >= y && z >= x && y >= x);
  }

  #[test]
  #[should_fail]
  fn div_zero() {
    let x = init();
    x / x;
  }

  #[test]
  #[should_fail]
  fn modulo_zero() {
    let x = init();
    x % x;
  }

  #[test]
  fn test_div_round() {
    let mut x = init();
    let mut y = init();
    let mut z: Mpz;

    x.set_str("2", 10);
    y.set_str("3", 10);
    z = x / y;
    assert(__gmpz_cmp_ui(addr_of(&z.mpz), 2 / 3) == 0);

    x.set_str("2", 10);
    y.set_str("-3", 10);
    z = x / y;
    assert(__gmpz_cmp_ui(addr_of(&z.mpz), 2 / -3) == 0);
  }

  #[test]
  fn to_str_radix() {
    let x = option::unwrap(from_str("255"));
    assert(x.to_str_radix(16) == ~"ff");
  }

  #[test]
  fn to_str() {
    let x = option::unwrap(from_str("1234567890"));
    assert(x.to_str() == ~"1234567890");
  }

  #[test]
  fn invalid_str() {
    assert(from_str("foobar").is_none());
  }

  #[test]
  fn test_clone() {
    let a = option::unwrap(from_str("100"));
    let b = a.clone();
    assert(b == a);
    assert(a + b == option::unwrap(from_str("200")));
  }

  #[test]
  fn from_int() {
    let x: Mpz = num::from_int(150);
    assert(x.to_str() == ~"150");
    assert(x == option::unwrap(from_str("150")));
  }
}
<commit_msg>improve size_in_base test<commit_after>extern mod std;

use libc::c_char;
use libc::c_int;
use libc::size_t;
use libc::c_ulong;
use libc::c_void;
use ptr::null;
use ptr::addr_of;
use ptr::mut_addr_of;
use ptr::to_mut_unsafe_ptr;
use str::as_c_str;

struct mpz_struct {
  _mp_alloc: c_int,
  _mp_size: c_int,
  _mp_d: *c_void
}

type mpz_srcptr = *const mpz_struct;
type mpz_ptr = *mut mpz_struct;

extern mod gmp {
  fn __gmpz_init(x: mpz_ptr);
  fn __gmpz_init_set(rop: mpz_ptr, op: mpz_srcptr);
  fn __gmpz_init_set_str(rop: mpz_ptr, str: *c_char, base: c_int) -> c_int;
  fn __gmpz_clear(x: mpz_ptr);
  fn __gmpz_set_str(rop: mpz_ptr, str: *c_char, base: c_int) -> c_int;
  fn __gmpz_get_str(str: *c_char, base: c_int, op: mpz_srcptr) -> *c_char;
  pure fn __gmpz_sizeinbase(op: mpz_srcptr, base: c_int) -> size_t;
  pure fn __gmpz_cmp(op: mpz_srcptr, op2: mpz_srcptr) -> c_int;
  pure fn __gmpz_cmp_ui(op1: mpz_srcptr, op2: c_ulong) -> c_int;
  fn __gmpz_add(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_sub(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_mul(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_neg(rop: mpz_ptr, op: mpz_srcptr);
  fn __gmpz_tdiv_q(r: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
  fn __gmpz_mod(r: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
}

use gmp::*;

pub struct Mpz {
  priv mpz: mpz_struct,

  drop {
    __gmpz_clear(mut_addr_of(&self.mpz));
  }
}

impl Mpz {
  fn set_str(&mut self, s: &str, base: int) -> bool {
    let mpz = to_mut_unsafe_ptr(&mut self.mpz);
    let r = as_c_str(s, { |s| __gmpz_set_str(mpz, s, base as c_int) });
    r == 0
  }

  pure fn to_str_radix(&self, base: int) -> ~str unsafe {
    let length = self.size_in_base(10) + 2;
    let dst = vec::to_mut(vec::from_elem(length, '0'));
    let pdst = vec::raw::to_ptr(dst);

    str::raw::from_c_str(__gmpz_get_str(pdst as *c_char, base as c_int,
                         addr_of(&self.mpz)))
  }

  pure fn size_in_base(&self, base: int) -> uint {
    __gmpz_sizeinbase(addr_of(&self.mpz), base as c_int) as uint
  }

  // TODO: implement the clone::Clone trait when 0.5 is released
  pure fn clone() -> Mpz unsafe {
    let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
    __gmpz_init_set(mut_addr_of(&mpz), addr_of(&self.mpz));
    Mpz { mpz: mpz }
  }
}

impl Mpz: cmp::Eq {
  pure fn eq(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) == 0
  }
  pure fn ne(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) != 0
  }
}

impl Mpz: cmp::Ord {
  pure fn lt(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) < 0
  }
  pure fn le(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) <= 0
  }
  pure fn gt(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) > 0
  }
  pure fn ge(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) >= 0
  }
}

impl Mpz: num::Num {
  pure fn add(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_add(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn sub(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_sub(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn mul(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_mul(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn div(other: &Mpz) -> Mpz unsafe {
    if __gmpz_cmp_ui(addr_of(&self.mpz), 0) == 0 {
      fail ~"divide by zero";
    }

    let res = init();
    __gmpz_tdiv_q(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn modulo(other: &Mpz) -> Mpz unsafe {
    if __gmpz_cmp_ui(addr_of(&self.mpz), 0) == 0 {
      fail ~"divide by zero";
    }

    let res = init();
    __gmpz_mod(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn neg() -> Mpz unsafe {
    let res = init();
    __gmpz_neg(mut_addr_of(&res.mpz), addr_of(&self.mpz));
    res
  }
  pure fn to_int() -> int {
    fail ~"not implemented";
  }
  static pure fn from_int(other: int) -> Mpz {
    // the gmp functions dealing with longs aren't usable here - long is only
    // guaranteed to be at least 32-bit
    option::unwrap(from_str(other.to_str()))
  }
}

pub pure fn from_str_radix(s: &str, base: int) -> Option<Mpz> unsafe {
  let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
  let mpz_ptr = mut_addr_of(&mpz);
  let r = as_c_str(s, { |s| __gmpz_init_set_str(mpz_ptr, s, base as c_int) });
  if r == 0 {
    Some(Mpz { mpz: mpz })
  } else {
    __gmpz_clear(mpz_ptr);
    None
  }
}

pub pure fn from_str(s: &str) -> Option<Mpz> {
  from_str_radix(s, 10)
}

impl Mpz : from_str::FromStr {
  static fn from_str(s: &str) -> Option<Mpz> {
    from_str(s)
  }
}

impl Mpz : to_str::ToStr {
  pure fn to_str() -> ~str {
    self.to_str_radix(10)
  }
}

pub pure fn init() -> Mpz unsafe {
  let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
  __gmpz_init(mut_addr_of(&mpz));
  Mpz { mpz: mpz }
}

#[cfg(test)]
mod tests {
  #[test]
  fn size_in_base() {
    let x = option::unwrap(from_str("150000"));
    assert(x.size_in_base(10) == 6);
    assert(x == option::unwrap(from_str_radix("249f0", 16)));
    assert(x.size_in_base(16) == 5);
  }

  #[test]
  fn eq() {
    let x = option::unwrap(from_str("4242142195"));
    let y = option::unwrap(from_str("4242142195"));
    let z = option::unwrap(from_str("4242142196"));

    assert(x == y);
    assert(x != z);
    assert(y != z);
  }

  #[test]
  fn ord() {
    let x = option::unwrap(from_str("40000000000000000000000"));
    let y = option::unwrap(from_str("45000000000000000000000"));
    let z = option::unwrap(from_str("50000000000000000000000"));

    assert(x < y && x < z && y < z);
    assert(x <= x && x <= y && x <= z && y <= z);
    assert(z > y && z > x && y > x);
    assert(z >= z && z >= y && z >= x && y >= x);
  }

  #[test]
  #[should_fail]
  fn div_zero() {
    let x = init();
    x / x;
  }

  #[test]
  #[should_fail]
  fn modulo_zero() {
    let x = init();
    x % x;
  }

  #[test]
  fn test_div_round() {
    let mut x = init();
    let mut y = init();
    let mut z: Mpz;

    x.set_str("2", 10);
    y.set_str("3", 10);
    z = x / y;
    assert(__gmpz_cmp_ui(addr_of(&z.mpz), 2 / 3) == 0);

    x.set_str("2", 10);
    y.set_str("-3", 10);
    z = x / y;
    assert(__gmpz_cmp_ui(addr_of(&z.mpz), 2 / -3) == 0);
  }

  #[test]
  fn to_str_radix() {
    let x = option::unwrap(from_str("255"));
    assert(x.to_str_radix(16) == ~"ff");
  }

  #[test]
  fn to_str() {
    let x = option::unwrap(from_str("1234567890"));
    assert(x.to_str() == ~"1234567890");
  }

  #[test]
  fn invalid_str() {
    assert(from_str("foobar").is_none());
  }

  #[test]
  fn test_clone() {
    let a = option::unwrap(from_str("100"));
    let b = a.clone();
    assert(b == a);
    assert(a + b == option::unwrap(from_str("200")));
  }

  #[test]
  fn from_int() {
    let x: Mpz = num::from_int(150);
    assert(x.to_str() == ~"150");
    assert(x == option::unwrap(from_str("150")));
  }
}
<|endoftext|>


<commit_before>use std::num;
use std::vec;
use addressable::Addressable;

pub struct Ram<ADDR> {
	data: ~[u8],
}

impl<ADDR: Int> Ram<ADDR> {
	pub fn new (size: ADDR) -> ~Ram<ADDR> {
		let s: uint = num::cast(size);
		~Ram { data: vec::from_elem(s, 0) }
	}
}

impl<ADDR: Int> Addressable<ADDR, u8> for Ram<ADDR> {
	pub fn get (&self, addr: ADDR) -> u8 {
		let i: uint = num::cast(addr);
		if i > self.data.len()-1 { fail!("memory: read beyond memory bounds (%? > %?)", i, self.data.len()-1); }
		self.data[i]
	}

	pub fn set (&mut self, addr: ADDR, data: u8) {
		let i: uint = num::cast(addr);
		if i > self.data.len()-1 { fail!("memory: write beyond memory bounds (%? > %?)", i, self.data.len()-1); }
		self.data[i] = data;
	}
}

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

	#[test]
	fn test () {
		let mut memory = Ram::new(1024);
		memory.set(0x0123, 0x55);
		assert_eq!(memory.get(0x0123), 0x55);
	}
}
<commit_msg>Fixed RAM size problems<commit_after>use std::num;
use std::vec;
use addressable::Addressable;

pub struct Ram<ADDR> {
	data: ~[u8],
}

impl<ADDR: Int> Ram<ADDR> {
	pub fn new (size: uint) -> ~Ram<ADDR> {
		~Ram { data: vec::from_elem(size, 0) }
	}
}

impl<ADDR: Int> Addressable<ADDR, u8> for Ram<ADDR> {
	pub fn get (&self, addr: ADDR) -> u8 {
		let i: uint = num::cast(addr);
		if i >= self.data.len() { fail!("memory: read beyond memory bounds ($%x >= $%x)", i, self.data.len()); }
		self.data[i]
	}

	pub fn set (&mut self, addr: ADDR, data: u8) {
		let i: uint = num::cast(addr);
		if i >= self.data.len() { fail!("memory: write beyond memory bounds ($%x >= $%x)", i, self.data.len()); }
		self.data[i] = data;
	}
}

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

	#[test]
	fn test () {
		let mut memory = Ram::new(1024);
		memory.set(0x0123, 0x55);
		assert_eq!(memory.get(0x0123), 0x55);
	}
}
<|endoftext|>


<commit_before>use parser::Identifier;
use operations::{Operation};
use operations::item_type::{ItemType, FuncArgType};
use operations::scope::{ScopeStack, ScopeItem, TypeId};

pub fn define_boolean(scope: &mut ScopeStack) -> TypeId {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let bool_type = scope.declare_type(
        Identifier::from("bool"),
        ItemType::Primitive(1)
    );
    scope.register_primitive("bool", bool_type);

    scope.declare_constant(
        Identifier::from("true"),
        bool_type,
        vec![1u8]
    );

    scope.declare_constant(
        Identifier::from("false"),
        bool_type,
        vec![0u8]
    );

    let unit_type = scope.primitives().unit();

    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(bool_type)],
            return_type: unit_type,
        },
        move |scope, args, _| {
            match args[0] {
                ScopeItem::TypedBlock {memory, ..} => Ok(vec![
                    Operation::Increment {
                        target: memory.position(),
                        amount: b'0',
                    },
                    Operation::Write {
                        target: memory,
                    },
                    // Need to make sure we reverse the operation after so this function has no
                    // side effects
                    Operation::Decrement {
                        target: memory.position(),
                        amount: b'0',
                    },
                ]),
                ScopeItem::Constant {type_id, ref bytes} => {
                    let bool_type = scope.primitives().bool();
                    debug_assert_eq!(type_id, bool_type);

                    let u8_type = scope.primitives().u8();
                    let mem = scope.allocate(u8_type);

                    // This code assumes that this is 1
                    debug_assert_eq!(bytes.len(), 1);
                    let value = b'0' + bytes[0];
                    Ok(vec![Operation::TempAllocate {
                        temp: mem,
                        body: vec![
                            Operation::Increment {
                                target: mem.position(),
                                amount: value,
                            },
                            Operation::Write {
                                target: mem,
                            },
                            Operation::Decrement {
                                target: mem.position(),
                                amount: value,
                            },
                        ],
                        should_zero: false,
                    }])
                },
                _ => unreachable!(),
            }
        }
    );

    scope.declare_builtin_function(
        Identifier::from("std::ops::Not"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(bool_type)],
            return_type: bool_type,
        },
        move |scope, args, target| {
            let bool_type = scope.primitives().bool();
            match args[0] {
                ScopeItem::TypedBlock {memory, ..} => {
                    let temp0 = scope.allocate(bool_type);
                    Ok(vec![Operation::TempAllocate {
                        temp: temp0,
                        body: vec![
                            // Need to copy to the destination because the algorithm consumes
                            // the operand
                            // After this copy the operand becomes `target` (x in the algorithm)
                            Operation::Copy {
                                source: memory.position(),
                                target: target.position(),
                                size: target.size(),
                            },

                            // Algorithm from: https://esolangs.org/wiki/Brainfuck_algorithms#x_.3D_not_x_.28boolean.2C_logical.29
                            //
                            // temp0[-]+
                            // x[-temp0-]temp0[x+temp0-]
                            //
                            // Results in x = !x and temp0 = 0
                            Operation::Increment {
                                target: temp0.position(),
                                amount: 1,
                            },
                            Operation::Loop {
                                cond: target.position(),
                                body: vec![
                                    Operation::Decrement {
                                        target: target.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                ],
                            },
                            Operation::Loop {
                                cond: temp0.position(),
                                body: vec![
                                    Operation::Increment {
                                        target: target.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                ],
                            },
                        ],
                        should_zero: false,
                    }])
                },
                ScopeItem::Constant {type_id, ref bytes} => {
                    debug_assert_eq!(type_id, bool_type);
                    // This code assumes that this is 1
                    debug_assert_eq!(bytes.len(), 1);

                    Ok(match bytes[0] {
                        //TODO: Verify if this makes sense or if we should explicitly zero
                        1 => Vec::new(),
                        0 => vec![
                            Operation::Increment {
                                target: target.position(),
                                amount: 1,
                            },
                        ],
                        _ => unreachable!(),
                    })
                },
                _ => unreachable!(),
            }
        }
    );

    // boolean and operator (operator&&) and boolean or operator (operator||)
    // These operations have special names because they are not regular functions
    // that can be defined or overloaded
    // The reason these are not definable is because they have to support short
    // circuiting. This behaviour cannot be modelled by a trait, so these special
    // operators are not definable by the user.
    //TODO

    bool_type
}

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

    use parser::Identifier;
    use operations::scope::{ScopeType, ScopeItem};

    #[test]
    fn defines_primitive() {
        let mut scope = ScopeStack::new();
        define_boolean(&mut scope);

        let bool_type_id = match **scope.lookup_type(&Identifier::from("bool")).first().unwrap() {
            ScopeType::Type(id) => id,
        };
        assert_eq!(scope.primitives().bool(), bool_type_id);
    }

    #[test]
    fn constants() {
        let mut scope = ScopeStack::new();
        define_boolean(&mut scope);

        let bool_type_id = match **scope.lookup_type(&Identifier::from("bool")).first().unwrap() {
            ScopeType::Type(id) => id,
        };

        let true_bytes = match **scope.lookup(&Identifier::from("true")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![1]);
                bytes
            },
            _ => unreachable!(),
        };

        let false_bytes = match **scope.lookup(&Identifier::from("false")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![0]);
                bytes
            },
            _ => unreachable!(),
        };

        // No matter what the value of both, this property must always hold
        assert!(true_bytes != false_bytes);
    }
}
<commit_msg>Implementing operator|| for booleans<commit_after>use parser::Identifier;
use operations::{Operation};
use operations::item_type::{ItemType, FuncArgType};
use operations::scope::{ScopeStack, ScopeItem, TypeId};

pub fn define_boolean(scope: &mut ScopeStack) -> TypeId {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let bool_type = scope.declare_type(
        Identifier::from("bool"),
        ItemType::Primitive(1)
    );
    scope.register_primitive("bool", bool_type);

    scope.declare_constant(
        Identifier::from("true"),
        bool_type,
        vec![1u8]
    );

    scope.declare_constant(
        Identifier::from("false"),
        bool_type,
        vec![0u8]
    );

    let unit_type = scope.primitives().unit();

    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(bool_type)],
            return_type: unit_type,
        },
        move |scope, args, _| {
            match args[0] {
                ScopeItem::TypedBlock {memory, ..} => Ok(vec![
                    Operation::Increment {
                        target: memory.position(),
                        amount: b'0',
                    },
                    Operation::Write {
                        target: memory,
                    },
                    // Need to make sure we reverse the operation after so this function has no
                    // side effects
                    Operation::Decrement {
                        target: memory.position(),
                        amount: b'0',
                    },
                ]),
                ScopeItem::Constant {type_id, ref bytes} => {
                    let bool_type = scope.primitives().bool();
                    debug_assert_eq!(type_id, bool_type);

                    let u8_type = scope.primitives().u8();
                    let mem = scope.allocate(u8_type);

                    // This code assumes that this is 1
                    debug_assert_eq!(bytes.len(), 1);
                    let value = b'0' + bytes[0];
                    Ok(vec![Operation::TempAllocate {
                        temp: mem,
                        body: vec![
                            Operation::Increment {
                                target: mem.position(),
                                amount: value,
                            },
                            Operation::Write {
                                target: mem,
                            },
                            Operation::Decrement {
                                target: mem.position(),
                                amount: value,
                            },
                        ],
                        should_zero: false,
                    }])
                },
                _ => unreachable!(),
            }
        }
    );

    scope.declare_builtin_function(
        Identifier::from("std::ops::Not"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(bool_type)],
            return_type: bool_type,
        },
        move |scope, args, target| {
            let bool_type = scope.primitives().bool();
            match args[0] {
                ScopeItem::TypedBlock {memory, ..} => {
                    let temp0 = scope.allocate(bool_type);
                    Ok(vec![Operation::TempAllocate {
                        temp: temp0,
                        body: vec![
                            // Need to copy to the destination because the algorithm consumes
                            // the operand
                            // After this copy the operand becomes `target` (x in the algorithm)
                            Operation::Copy {
                                source: memory.position(),
                                target: target.position(),
                                size: target.size(),
                            },

                            // Algorithm from: https://esolangs.org/wiki/Brainfuck_algorithms#x_.3D_not_x_.28boolean.2C_logical.29
                            //
                            // temp0[-]+
                            // x[-temp0-]temp0[x+temp0-]
                            //
                            // Results in x = !x and temp0 = 0
                            Operation::Increment {
                                target: temp0.position(),
                                amount: 1,
                            },
                            Operation::Loop {
                                cond: target.position(),
                                body: vec![
                                    Operation::Decrement {
                                        target: target.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                ],
                            },
                            Operation::Loop {
                                cond: temp0.position(),
                                body: vec![
                                    Operation::Increment {
                                        target: target.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                ],
                            },
                        ],
                        should_zero: false,
                    }])
                },
                ScopeItem::Constant {type_id, ref bytes} => {
                    debug_assert_eq!(type_id, bool_type);
                    // This code assumes that this is 1
                    debug_assert_eq!(bytes.len(), 1);

                    Ok(match bytes[0] {
                        //TODO: Verify if this makes sense or if we should explicitly zero
                        1 => Vec::new(),
                        0 => vec![
                            Operation::Increment {
                                target: target.position(),
                                amount: 1,
                            },
                        ],
                        _ => unreachable!(),
                    })
                },
                _ => unreachable!(),
            }
        }
    );

    // boolean and operator (operator&&) and boolean or operator (operator||)
    // These operations have special names because they are not regular functions
    // that can be defined or overloaded
    // The reason these are not definable is because they have to support short
    // circuiting. This behaviour cannot be modelled by a trait, so these special
    // operators are not definable by the user.
    scope.declare_builtin_function(
        Identifier::from("operator||"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(bool_type), FuncArgType::Arg(bool_type)],
            return_type: bool_type,
        },
        move |scope, args, target| {
            let bool_type = scope.primitives().bool();

            Ok(match (&args[0], &args[1]) {
                (&ScopeItem::Constant {type_id: xt, bytes: ref xb}, &ScopeItem::Constant {type_id: yt, bytes: ref yb}) => {
                    debug_assert_eq!(xt, bool_type);
                    debug_assert_eq!(yt, bool_type);
                    // This code assumes that this is 1
                    debug_assert_eq!(xb.len(), 1);
                    debug_assert_eq!(yb.len(), 1);

                    // The `> 0` converts the byte-representation into a Rust boolean
                    let x = xb[0] > 0;
                    let y = yb[0] > 0;

                    match x || y {
                        false => Vec::new(),
                        true => vec![Operation::Increment {
                            target: target.position(),
                            amount: 1,
                        }],
                    }
                },
                /// Thanks to certain properties of booleans, we can evaluate certain things during
                /// compilation and avoid a lot of extra computation.
                (&ScopeItem::Constant {type_id: const_type, ref bytes}, &ScopeItem::TypedBlock {type_id: other_type, memory}) |
                (&ScopeItem::TypedBlock {type_id: other_type, memory}, &ScopeItem::Constant {type_id: const_type, ref bytes}) => {
                    debug_assert_eq!(const_type, bool_type);
                    debug_assert_eq!(other_type, bool_type);
                    // This code assumes that this is 1
                    debug_assert_eq!(bytes.len(), 1);

                    match bytes[0] {
                        // If either operand is true, the || operator always returns true
                        1 => vec![Operation::Increment {
                            target: target.position(),
                            amount: 1,
                        }],
                        // If either operand is false, the || operator always returns its other operand
                        0 => vec![Operation::Copy {
                            source: memory.position(),
                            target: target.position(),
                            size: memory.size(),
                        }],
                        _ => unreachable!(),
                    }
                },
                (&ScopeItem::TypedBlock {memory: x, ..}, &ScopeItem::TypedBlock {memory: y, ..}) => {
                    let temp0 = scope.allocate(bool_type);
                    vec![Operation::TempAllocate {
                        temp: temp0,
                        body: vec![
                            // Algorithm from: https://esolangs.org/wiki/Brainfuck_algorithms#z_.3D_x_or_y_.28boolean.2C_logical.29
                            //
                            // z[-]
                            // temp0[-]+
                            // x[z+temp0-x-]
                            // temp0[-
                            //  y[z+y-]
                            // ]
                            // y[-]
                            Operation::Increment {
                                target: temp0.position(),
                                amount: 1,
                            },
                            Operation::Loop {
                                cond: x.position(),
                                body: vec![
                                    Operation::Increment {
                                        target: target.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: x.position(),
                                        amount: 1,
                                    },
                                ],
                            },
                            Operation::Loop {
                                cond: temp0.position(),
                                body: vec![
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                    Operation::Loop {
                                        cond: y.position(),
                                        body: vec![
                                            Operation::Increment {
                                                target: target.position(),
                                                amount: 1,
                                            },
                                            Operation::Decrement {
                                                target: y.position(),
                                                amount: 1,
                                            },
                                        ],
                                    },
                                ],
                            },
                            Operation::Zero {
                                target: y,
                            }
                        ],
                        should_zero: false,
                    }]
                },
                _ => unreachable!(),
            })
        }
    );

    bool_type
}

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

    use parser::Identifier;
    use operations::scope::{ScopeType, ScopeItem};

    #[test]
    fn defines_primitive() {
        let mut scope = ScopeStack::new();
        define_boolean(&mut scope);

        let bool_type_id = match **scope.lookup_type(&Identifier::from("bool")).first().unwrap() {
            ScopeType::Type(id) => id,
        };
        assert_eq!(scope.primitives().bool(), bool_type_id);
    }

    #[test]
    fn constants() {
        let mut scope = ScopeStack::new();
        define_boolean(&mut scope);

        let bool_type_id = match **scope.lookup_type(&Identifier::from("bool")).first().unwrap() {
            ScopeType::Type(id) => id,
        };

        let true_bytes = match **scope.lookup(&Identifier::from("true")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![1]);
                bytes
            },
            _ => unreachable!(),
        };

        let false_bytes = match **scope.lookup(&Identifier::from("false")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![0]);
                bytes
            },
            _ => unreachable!(),
        };

        // No matter what the value of both, this property must always hold
        assert!(true_bytes != false_bytes);
    }
}
<|endoftext|>


<commit_before>use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::query::Providers;
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::symbol::Symbol;

// When adding features to the below lists
// check whether they're named already elsewhere in rust
// e.g. in stdarch and whether the given name matches LLVM's
// if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted

const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("aclass", Some(sym::arm_target_feature)),
    ("mclass", Some(sym::arm_target_feature)),
    ("rclass", Some(sym::arm_target_feature)),
    ("dsp", Some(sym::arm_target_feature)),
    ("neon", Some(sym::arm_target_feature)),
    ("crc", Some(sym::arm_target_feature)),
    ("crypto", Some(sym::arm_target_feature)),
    ("aes", Some(sym::arm_target_feature)),
    ("sha2", Some(sym::arm_target_feature)),
    ("v5te", Some(sym::arm_target_feature)),
    ("v6", Some(sym::arm_target_feature)),
    ("v6k", Some(sym::arm_target_feature)),
    ("v6t2", Some(sym::arm_target_feature)),
    ("v7", Some(sym::arm_target_feature)),
    ("v8", Some(sym::arm_target_feature)),
    ("vfp2", Some(sym::arm_target_feature)),
    ("vfp3", Some(sym::arm_target_feature)),
    ("vfp4", Some(sym::arm_target_feature)),
    ("fp-armv8", Some(sym::arm_target_feature)),
    // This is needed for inline assembly, but shouldn't be stabilized as-is
    // since it should be enabled per-function using #[instruction_set], not
    // #[target_feature].
    ("thumb-mode", Some(sym::arm_target_feature)),
];

// Commented features are not available in LLVM 10.0, or have since been renamed
const AARCH64_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    // FEAT_AdvSimd
    ("neon", Some(sym::aarch64_target_feature)),
    // FEAT_FP
    ("fp", Some(sym::aarch64_target_feature)),
    // FEAT_FP16
    ("fp16", Some(sym::aarch64_target_feature)),
    // FEAT_SVE
    ("sve", Some(sym::aarch64_target_feature)),
    // FEAT_CRC
    ("crc", Some(sym::aarch64_target_feature)),
    // FEAT_RAS
    ("ras", Some(sym::aarch64_target_feature)),
    // FEAT_LSE
    ("lse", Some(sym::aarch64_target_feature)),
    // FEAT_RDM
    ("rdm", Some(sym::aarch64_target_feature)),
    // FEAT_RCPC
    ("rcpc", Some(sym::aarch64_target_feature)),
    // FEAT_RCPC2
    ("rcpc2", Some(sym::aarch64_target_feature)),
    // FEAT_DotProd
    ("dotprod", Some(sym::aarch64_target_feature)),
    // FEAT_TME
    ("tme", Some(sym::aarch64_target_feature)),
    // FEAT_FHM
    ("fhm", Some(sym::aarch64_target_feature)),
    // FEAT_DIT
    ("dit", Some(sym::aarch64_target_feature)),
    // FEAT_FLAGM
    // ("flagm", Some(sym::aarch64_target_feature)),
    // FEAT_SSBS
    ("ssbs", Some(sym::aarch64_target_feature)),
    // FEAT_SB
    ("sb", Some(sym::aarch64_target_feature)),
    // FEAT_PAUTH
    // ("pauth", Some(sym::aarch64_target_feature)),
    // FEAT_DPB
    ("dpb", Some(sym::aarch64_target_feature)),
    // FEAT_DPB2
    ("dpb2", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2
    ("sve2", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_AES
    ("sve2-aes", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_SM4
    ("sve2-sm4", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_SHA3
    ("sve2-sha3", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_BitPerm
    ("sve2-bitperm", Some(sym::aarch64_target_feature)),
    // FEAT_FRINTTS
    ("frintts", Some(sym::aarch64_target_feature)),
    // FEAT_I8MM
    // ("i8mm", Some(sym::aarch64_target_feature)),
    // FEAT_F32MM
    // ("f32mm", Some(sym::aarch64_target_feature)),
    // FEAT_F64MM
    // ("f64mm", Some(sym::aarch64_target_feature)),
    // FEAT_BF16
    // ("bf16", Some(sym::aarch64_target_feature)),
    // FEAT_RAND
    ("rand", Some(sym::aarch64_target_feature)),
    // FEAT_BTI
    ("bti", Some(sym::aarch64_target_feature)),
    // FEAT_MTE
    ("mte", Some(sym::aarch64_target_feature)),
    // FEAT_JSCVT
    ("jsconv", Some(sym::aarch64_target_feature)),
    // FEAT_FCMA
    ("fcma", Some(sym::aarch64_target_feature)),
    // FEAT_AES
    ("aes", Some(sym::aarch64_target_feature)),
    // FEAT_SHA1 & FEAT_SHA256
    ("sha2", Some(sym::aarch64_target_feature)),
    // FEAT_SHA512 & FEAT_SHA3
    ("sha3", Some(sym::aarch64_target_feature)),
    // FEAT_SM3 & FEAT_SM4
    ("sm4", Some(sym::aarch64_target_feature)),
    ("v8.1a", Some(sym::aarch64_target_feature)),
    ("v8.2a", Some(sym::aarch64_target_feature)),
    ("v8.3a", Some(sym::aarch64_target_feature)),
    ("v8.4a", Some(sym::aarch64_target_feature)),
    ("v8.5a", Some(sym::aarch64_target_feature)),
    // ("v8.6a", Some(sym::aarch64_target_feature)),
    // ("v8.7a", Some(sym::aarch64_target_feature)),
];

const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("adx", Some(sym::adx_target_feature)),
    ("aes", None),
    ("avx", None),
    ("avx2", None),
    ("avx512bf16", Some(sym::avx512_target_feature)),
    ("avx512bitalg", Some(sym::avx512_target_feature)),
    ("avx512bw", Some(sym::avx512_target_feature)),
    ("avx512cd", Some(sym::avx512_target_feature)),
    ("avx512dq", Some(sym::avx512_target_feature)),
    ("avx512er", Some(sym::avx512_target_feature)),
    ("avx512f", Some(sym::avx512_target_feature)),
    ("avx512gfni", Some(sym::avx512_target_feature)),
    ("avx512ifma", Some(sym::avx512_target_feature)),
    ("avx512pf", Some(sym::avx512_target_feature)),
    ("avx512vaes", Some(sym::avx512_target_feature)),
    ("avx512vbmi", Some(sym::avx512_target_feature)),
    ("avx512vbmi2", Some(sym::avx512_target_feature)),
    ("avx512vl", Some(sym::avx512_target_feature)),
    ("avx512vnni", Some(sym::avx512_target_feature)),
    ("avx512vp2intersect", Some(sym::avx512_target_feature)),
    ("avx512vpclmulqdq", Some(sym::avx512_target_feature)),
    ("avx512vpopcntdq", Some(sym::avx512_target_feature)),
    ("bmi1", None),
    ("bmi2", None),
    ("cmpxchg16b", Some(sym::cmpxchg16b_target_feature)),
    ("ermsb", Some(sym::ermsb_target_feature)),
    ("f16c", Some(sym::f16c_target_feature)),
    ("fma", None),
    ("fxsr", None),
    ("lzcnt", None),
    ("movbe", Some(sym::movbe_target_feature)),
    ("pclmulqdq", None),
    ("popcnt", None),
    ("rdrand", None),
    ("rdseed", None),
    ("rtm", Some(sym::rtm_target_feature)),
    ("sha", None),
    ("sse", None),
    ("sse2", None),
    ("sse3", None),
    ("sse4.1", None),
    ("sse4.2", None),
    ("sse4a", Some(sym::sse4a_target_feature)),
    ("ssse3", None),
    ("tbm", Some(sym::tbm_target_feature)),
    ("xsave", None),
    ("xsavec", None),
    ("xsaveopt", None),
    ("xsaves", None),
];

const HEXAGON_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("hvx", Some(sym::hexagon_target_feature)),
    ("hvx-length128b", Some(sym::hexagon_target_feature)),
];

const POWERPC_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("altivec", Some(sym::powerpc_target_feature)),
    ("power8-altivec", Some(sym::powerpc_target_feature)),
    ("power9-altivec", Some(sym::powerpc_target_feature)),
    ("power8-vector", Some(sym::powerpc_target_feature)),
    ("power9-vector", Some(sym::powerpc_target_feature)),
    ("vsx", Some(sym::powerpc_target_feature)),
];

const MIPS_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] =
    &[("fp64", Some(sym::mips_target_feature)), ("msa", Some(sym::mips_target_feature))];

const RISCV_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("m", Some(sym::riscv_target_feature)),
    ("a", Some(sym::riscv_target_feature)),
    ("c", Some(sym::riscv_target_feature)),
    ("f", Some(sym::riscv_target_feature)),
    ("d", Some(sym::riscv_target_feature)),
    ("e", Some(sym::riscv_target_feature)),
];

const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("simd128", None),
    ("atomics", Some(sym::wasm_target_feature)),
    ("nontrapping-fptoint", Some(sym::wasm_target_feature)),
];

const BPF_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[("alu32", Some(sym::bpf_target_feature))];

/// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented.
///
/// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator!
pub fn all_known_features() -> impl Iterator<Item = (&'static str, Option<Symbol>)> {
    std::iter::empty()
        .chain(ARM_ALLOWED_FEATURES.iter())
        .chain(AARCH64_ALLOWED_FEATURES.iter())
        .chain(X86_ALLOWED_FEATURES.iter())
        .chain(HEXAGON_ALLOWED_FEATURES.iter())
        .chain(POWERPC_ALLOWED_FEATURES.iter())
        .chain(MIPS_ALLOWED_FEATURES.iter())
        .chain(RISCV_ALLOWED_FEATURES.iter())
        .chain(WASM_ALLOWED_FEATURES.iter())
        .chain(BPF_ALLOWED_FEATURES.iter())
        .cloned()
}

pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Option<Symbol>)] {
    match &*sess.target.arch {
        "arm" => ARM_ALLOWED_FEATURES,
        "aarch64" => AARCH64_ALLOWED_FEATURES,
        "x86" | "x86_64" => X86_ALLOWED_FEATURES,
        "hexagon" => HEXAGON_ALLOWED_FEATURES,
        "mips" | "mips64" => MIPS_ALLOWED_FEATURES,
        "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES,
        "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES,
        "wasm32" | "wasm64" => WASM_ALLOWED_FEATURES,
        "bpf" => BPF_ALLOWED_FEATURES,
        _ => &[],
    }
}

pub(crate) fn provide(providers: &mut Providers) {
    providers.supported_target_features = |tcx, cnum| {
        assert_eq!(cnum, LOCAL_CRATE);
        if tcx.sess.opts.actually_rustdoc {
            // rustdoc needs to be able to document functions that use all the features, so
            // whitelist them all
            all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
        } else {
            supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect()
        }
    };
}
<commit_msg>enable `i8mm` target feature on aarch64<commit_after>use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::query::Providers;
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::symbol::Symbol;

// When adding features to the below lists
// check whether they're named already elsewhere in rust
// e.g. in stdarch and whether the given name matches LLVM's
// if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted

const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("aclass", Some(sym::arm_target_feature)),
    ("mclass", Some(sym::arm_target_feature)),
    ("rclass", Some(sym::arm_target_feature)),
    ("dsp", Some(sym::arm_target_feature)),
    ("neon", Some(sym::arm_target_feature)),
    ("crc", Some(sym::arm_target_feature)),
    ("crypto", Some(sym::arm_target_feature)),
    ("aes", Some(sym::arm_target_feature)),
    ("sha2", Some(sym::arm_target_feature)),
    ("v5te", Some(sym::arm_target_feature)),
    ("v6", Some(sym::arm_target_feature)),
    ("v6k", Some(sym::arm_target_feature)),
    ("v6t2", Some(sym::arm_target_feature)),
    ("v7", Some(sym::arm_target_feature)),
    ("v8", Some(sym::arm_target_feature)),
    ("vfp2", Some(sym::arm_target_feature)),
    ("vfp3", Some(sym::arm_target_feature)),
    ("vfp4", Some(sym::arm_target_feature)),
    ("fp-armv8", Some(sym::arm_target_feature)),
    // This is needed for inline assembly, but shouldn't be stabilized as-is
    // since it should be enabled per-function using #[instruction_set], not
    // #[target_feature].
    ("thumb-mode", Some(sym::arm_target_feature)),
];

// Commented features are not available in LLVM 10.0, or have since been renamed
const AARCH64_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    // FEAT_AdvSimd
    ("neon", Some(sym::aarch64_target_feature)),
    // FEAT_FP
    ("fp", Some(sym::aarch64_target_feature)),
    // FEAT_FP16
    ("fp16", Some(sym::aarch64_target_feature)),
    // FEAT_SVE
    ("sve", Some(sym::aarch64_target_feature)),
    // FEAT_CRC
    ("crc", Some(sym::aarch64_target_feature)),
    // FEAT_RAS
    ("ras", Some(sym::aarch64_target_feature)),
    // FEAT_LSE
    ("lse", Some(sym::aarch64_target_feature)),
    // FEAT_RDM
    ("rdm", Some(sym::aarch64_target_feature)),
    // FEAT_RCPC
    ("rcpc", Some(sym::aarch64_target_feature)),
    // FEAT_RCPC2
    ("rcpc2", Some(sym::aarch64_target_feature)),
    // FEAT_DotProd
    ("dotprod", Some(sym::aarch64_target_feature)),
    // FEAT_TME
    ("tme", Some(sym::aarch64_target_feature)),
    // FEAT_FHM
    ("fhm", Some(sym::aarch64_target_feature)),
    // FEAT_DIT
    ("dit", Some(sym::aarch64_target_feature)),
    // FEAT_FLAGM
    // ("flagm", Some(sym::aarch64_target_feature)),
    // FEAT_SSBS
    ("ssbs", Some(sym::aarch64_target_feature)),
    // FEAT_SB
    ("sb", Some(sym::aarch64_target_feature)),
    // FEAT_PAUTH
    // ("pauth", Some(sym::aarch64_target_feature)),
    // FEAT_DPB
    ("dpb", Some(sym::aarch64_target_feature)),
    // FEAT_DPB2
    ("dpb2", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2
    ("sve2", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_AES
    ("sve2-aes", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_SM4
    ("sve2-sm4", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_SHA3
    ("sve2-sha3", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_BitPerm
    ("sve2-bitperm", Some(sym::aarch64_target_feature)),
    // FEAT_FRINTTS
    ("frintts", Some(sym::aarch64_target_feature)),
    // FEAT_I8MM
    ("i8mm", Some(sym::aarch64_target_feature)),
    // FEAT_F32MM
    // ("f32mm", Some(sym::aarch64_target_feature)),
    // FEAT_F64MM
    // ("f64mm", Some(sym::aarch64_target_feature)),
    // FEAT_BF16
    // ("bf16", Some(sym::aarch64_target_feature)),
    // FEAT_RAND
    ("rand", Some(sym::aarch64_target_feature)),
    // FEAT_BTI
    ("bti", Some(sym::aarch64_target_feature)),
    // FEAT_MTE
    ("mte", Some(sym::aarch64_target_feature)),
    // FEAT_JSCVT
    ("jsconv", Some(sym::aarch64_target_feature)),
    // FEAT_FCMA
    ("fcma", Some(sym::aarch64_target_feature)),
    // FEAT_AES
    ("aes", Some(sym::aarch64_target_feature)),
    // FEAT_SHA1 & FEAT_SHA256
    ("sha2", Some(sym::aarch64_target_feature)),
    // FEAT_SHA512 & FEAT_SHA3
    ("sha3", Some(sym::aarch64_target_feature)),
    // FEAT_SM3 & FEAT_SM4
    ("sm4", Some(sym::aarch64_target_feature)),
    ("v8.1a", Some(sym::aarch64_target_feature)),
    ("v8.2a", Some(sym::aarch64_target_feature)),
    ("v8.3a", Some(sym::aarch64_target_feature)),
    ("v8.4a", Some(sym::aarch64_target_feature)),
    ("v8.5a", Some(sym::aarch64_target_feature)),
    // ("v8.6a", Some(sym::aarch64_target_feature)),
    // ("v8.7a", Some(sym::aarch64_target_feature)),
];

const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("adx", Some(sym::adx_target_feature)),
    ("aes", None),
    ("avx", None),
    ("avx2", None),
    ("avx512bf16", Some(sym::avx512_target_feature)),
    ("avx512bitalg", Some(sym::avx512_target_feature)),
    ("avx512bw", Some(sym::avx512_target_feature)),
    ("avx512cd", Some(sym::avx512_target_feature)),
    ("avx512dq", Some(sym::avx512_target_feature)),
    ("avx512er", Some(sym::avx512_target_feature)),
    ("avx512f", Some(sym::avx512_target_feature)),
    ("avx512gfni", Some(sym::avx512_target_feature)),
    ("avx512ifma", Some(sym::avx512_target_feature)),
    ("avx512pf", Some(sym::avx512_target_feature)),
    ("avx512vaes", Some(sym::avx512_target_feature)),
    ("avx512vbmi", Some(sym::avx512_target_feature)),
    ("avx512vbmi2", Some(sym::avx512_target_feature)),
    ("avx512vl", Some(sym::avx512_target_feature)),
    ("avx512vnni", Some(sym::avx512_target_feature)),
    ("avx512vp2intersect", Some(sym::avx512_target_feature)),
    ("avx512vpclmulqdq", Some(sym::avx512_target_feature)),
    ("avx512vpopcntdq", Some(sym::avx512_target_feature)),
    ("bmi1", None),
    ("bmi2", None),
    ("cmpxchg16b", Some(sym::cmpxchg16b_target_feature)),
    ("ermsb", Some(sym::ermsb_target_feature)),
    ("f16c", Some(sym::f16c_target_feature)),
    ("fma", None),
    ("fxsr", None),
    ("lzcnt", None),
    ("movbe", Some(sym::movbe_target_feature)),
    ("pclmulqdq", None),
    ("popcnt", None),
    ("rdrand", None),
    ("rdseed", None),
    ("rtm", Some(sym::rtm_target_feature)),
    ("sha", None),
    ("sse", None),
    ("sse2", None),
    ("sse3", None),
    ("sse4.1", None),
    ("sse4.2", None),
    ("sse4a", Some(sym::sse4a_target_feature)),
    ("ssse3", None),
    ("tbm", Some(sym::tbm_target_feature)),
    ("xsave", None),
    ("xsavec", None),
    ("xsaveopt", None),
    ("xsaves", None),
];

const HEXAGON_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("hvx", Some(sym::hexagon_target_feature)),
    ("hvx-length128b", Some(sym::hexagon_target_feature)),
];

const POWERPC_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("altivec", Some(sym::powerpc_target_feature)),
    ("power8-altivec", Some(sym::powerpc_target_feature)),
    ("power9-altivec", Some(sym::powerpc_target_feature)),
    ("power8-vector", Some(sym::powerpc_target_feature)),
    ("power9-vector", Some(sym::powerpc_target_feature)),
    ("vsx", Some(sym::powerpc_target_feature)),
];

const MIPS_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] =
    &[("fp64", Some(sym::mips_target_feature)), ("msa", Some(sym::mips_target_feature))];

const RISCV_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("m", Some(sym::riscv_target_feature)),
    ("a", Some(sym::riscv_target_feature)),
    ("c", Some(sym::riscv_target_feature)),
    ("f", Some(sym::riscv_target_feature)),
    ("d", Some(sym::riscv_target_feature)),
    ("e", Some(sym::riscv_target_feature)),
];

const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("simd128", None),
    ("atomics", Some(sym::wasm_target_feature)),
    ("nontrapping-fptoint", Some(sym::wasm_target_feature)),
];

const BPF_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[("alu32", Some(sym::bpf_target_feature))];

/// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented.
///
/// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator!
pub fn all_known_features() -> impl Iterator<Item = (&'static str, Option<Symbol>)> {
    std::iter::empty()
        .chain(ARM_ALLOWED_FEATURES.iter())
        .chain(AARCH64_ALLOWED_FEATURES.iter())
        .chain(X86_ALLOWED_FEATURES.iter())
        .chain(HEXAGON_ALLOWED_FEATURES.iter())
        .chain(POWERPC_ALLOWED_FEATURES.iter())
        .chain(MIPS_ALLOWED_FEATURES.iter())
        .chain(RISCV_ALLOWED_FEATURES.iter())
        .chain(WASM_ALLOWED_FEATURES.iter())
        .chain(BPF_ALLOWED_FEATURES.iter())
        .cloned()
}

pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Option<Symbol>)] {
    match &*sess.target.arch {
        "arm" => ARM_ALLOWED_FEATURES,
        "aarch64" => AARCH64_ALLOWED_FEATURES,
        "x86" | "x86_64" => X86_ALLOWED_FEATURES,
        "hexagon" => HEXAGON_ALLOWED_FEATURES,
        "mips" | "mips64" => MIPS_ALLOWED_FEATURES,
        "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES,
        "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES,
        "wasm32" | "wasm64" => WASM_ALLOWED_FEATURES,
        "bpf" => BPF_ALLOWED_FEATURES,
        _ => &[],
    }
}

pub(crate) fn provide(providers: &mut Providers) {
    providers.supported_target_features = |tcx, cnum| {
        assert_eq!(cnum, LOCAL_CRATE);
        if tcx.sess.opts.actually_rustdoc {
            // rustdoc needs to be able to document functions that use all the features, so
            // whitelist them all
            all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
        } else {
            supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect()
        }
    };
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::path::Path;
use std::path::PathBuf;

use protobuf::descriptor::FileDescriptorSet;

use crate::protoc;
use crate::pure;
use crate::which_parser::WhichParser;
use crate::ParsedAndTypechecked;

/// Configure and invoke `.proto` parser.
#[derive(Default, Debug)]
pub struct Parser {
    which_parser: WhichParser,
    pub(crate) includes: Vec<PathBuf>,
    pub(crate) inputs: Vec<PathBuf>,
    pub(crate) protoc: Option<PathBuf>,
    pub(crate) protoc_extra_args: Vec<OsString>,
}

impl Parser {
    /// Create new default configured parser.
    pub fn new() -> Parser {
        Parser::default()
    }

    /// Use pure rust parser.
    pub fn pure(&mut self) -> &mut Self {
        self.which_parser = WhichParser::Pure;
        self
    }

    /// Use `protoc` for parsing.
    pub fn protoc(&mut self) -> &mut Self {
        self.which_parser = WhichParser::Protoc;
        self
    }

    /// Add an include directory.
    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
        self.includes.push(include.as_ref().to_owned());
        self
    }

    /// Add include directories.
    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for include in includes {
            self.include(include);
        }
        self
    }

    /// Append a `.proto` file path to compile
    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
        self.inputs.push(input.as_ref().to_owned());
        self
    }

    /// Append multiple `.proto` file paths to compile
    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for input in inputs {
            self.input(input);
        }
        self
    }

    /// Specify `protoc` path used for parsing.
    ///
    /// This is ignored if pure rust parser is used.
    pub fn protoc_path(&mut self, protoc: &Path) -> &mut Self {
        self.protoc = Some(protoc.to_owned());
        self
    }

    /// Extra arguments to pass to `protoc` command (like experimental options).
    ///
    /// This is ignored if pure rust parser is used.
    pub fn protoc_extra_args(
        &mut self,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> &mut Self {
        self.protoc_extra_args = args.into_iter().map(|s| s.as_ref().to_owned()).collect();
        self
    }

    /// Parse `.proto` files and typecheck them using pure Rust parser of `protoc` command.
    pub fn parse_and_typecheck(&self) -> anyhow::Result<ParsedAndTypechecked> {
        match &self.which_parser {
            WhichParser::Pure => pure::parse_and_typecheck::parse_and_typecheck(&self),
            WhichParser::Protoc => protoc::parse_and_typecheck::parse_and_typecheck(&self),
        }
    }

    /// Parse and convert result to `FileDescriptorSet`.
    pub fn file_descriptor_set(&self) -> anyhow::Result<FileDescriptorSet> {
        let mut generated = self.parse_and_typecheck()?;
        let relative_paths: HashSet<_> = generated
            .relative_paths
            .iter()
            .map(|path| path.to_string())
            .collect();
        generated
            .file_descriptors
            .retain(|fd| relative_paths.contains(fd.name()));
        let mut fds = FileDescriptorSet::new();
        fds.file = generated.file_descriptors;
        Ok(fds)
    }
}
<commit_msg>More error context<commit_after>use std::collections::HashSet;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::path::Path;
use std::path::PathBuf;
use anyhow::Context;

use protobuf::descriptor::FileDescriptorSet;

use crate::protoc;
use crate::pure;
use crate::which_parser::WhichParser;
use crate::ParsedAndTypechecked;

/// Configure and invoke `.proto` parser.
#[derive(Default, Debug)]
pub struct Parser {
    which_parser: WhichParser,
    pub(crate) includes: Vec<PathBuf>,
    pub(crate) inputs: Vec<PathBuf>,
    pub(crate) protoc: Option<PathBuf>,
    pub(crate) protoc_extra_args: Vec<OsString>,
}

impl Parser {
    /// Create new default configured parser.
    pub fn new() -> Parser {
        Parser::default()
    }

    /// Use pure rust parser.
    pub fn pure(&mut self) -> &mut Self {
        self.which_parser = WhichParser::Pure;
        self
    }

    /// Use `protoc` for parsing.
    pub fn protoc(&mut self) -> &mut Self {
        self.which_parser = WhichParser::Protoc;
        self
    }

    /// Add an include directory.
    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
        self.includes.push(include.as_ref().to_owned());
        self
    }

    /// Add include directories.
    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for include in includes {
            self.include(include);
        }
        self
    }

    /// Append a `.proto` file path to compile
    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
        self.inputs.push(input.as_ref().to_owned());
        self
    }

    /// Append multiple `.proto` file paths to compile
    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for input in inputs {
            self.input(input);
        }
        self
    }

    /// Specify `protoc` path used for parsing.
    ///
    /// This is ignored if pure rust parser is used.
    pub fn protoc_path(&mut self, protoc: &Path) -> &mut Self {
        self.protoc = Some(protoc.to_owned());
        self
    }

    /// Extra arguments to pass to `protoc` command (like experimental options).
    ///
    /// This is ignored if pure rust parser is used.
    pub fn protoc_extra_args(
        &mut self,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> &mut Self {
        self.protoc_extra_args = args.into_iter().map(|s| s.as_ref().to_owned()).collect();
        self
    }

    /// Parse `.proto` files and typecheck them using pure Rust parser of `protoc` command.
    pub fn parse_and_typecheck(&self) -> anyhow::Result<ParsedAndTypechecked> {
        match &self.which_parser {
            WhichParser::Pure => pure::parse_and_typecheck::parse_and_typecheck(&self)
                .context("using pure parser"),
            WhichParser::Protoc => protoc::parse_and_typecheck::parse_and_typecheck(&self)
                .context("using protoc parser"),
        }
    }

    /// Parse and convert result to `FileDescriptorSet`.
    pub fn file_descriptor_set(&self) -> anyhow::Result<FileDescriptorSet> {
        let mut generated = self.parse_and_typecheck()?;
        let relative_paths: HashSet<_> = generated
            .relative_paths
            .iter()
            .map(|path| path.to_string())
            .collect();
        generated
            .file_descriptors
            .retain(|fd| relative_paths.contains(fd.name()));
        let mut fds = FileDescriptorSet::new();
        fds.file = generated.file_descriptors;
        Ok(fds)
    }
}
<|endoftext|>


<commit_before>use log::{error, trace};

use std::io::{Error, ErrorKind, Read, Result, Write};

use crate::buffer::Buffer;
use crate::connection::{Connection, ConnectionState};

impl Connection {
  /// helper function to handle reading and writing repeatedly from the network until there's no more state to update
  pub fn run<T: Read + Write>(&mut self, stream: &mut T, send_buffer: &mut Buffer, receive_buffer: &mut Buffer) -> Result<ConnectionState> {
    let mut write_would_block = false;
    let mut read_would_block  = false;

    loop {
      let continue_writing = !write_would_block && self.can_write(send_buffer);
      let continue_reading = !read_would_block && self.can_read(receive_buffer);
      let continue_parsing = self.can_parse(receive_buffer);

      if !continue_writing && !continue_reading && !continue_parsing {
        return Ok(self.state.clone());
      }

      if continue_writing {
        match self.write_to_stream(stream, send_buffer) {
          Ok((_,_)) => {

          },
          Err(e) => {
            match e.kind() {
              ErrorKind::WouldBlock => {
                write_would_block = true;
              },
              k => {
                error!("error writing: {:?}", k);
                self.state = ConnectionState::Error;
                return Err(e);
              }
            }
          }
        }
      }

      if continue_reading {
        match self.read_from_stream(stream, receive_buffer) {
          Ok(_) => {},
          Err(e) => {
            match e.kind() {
              ErrorKind::WouldBlock => {
                read_would_block = true;
              },
              k => {
                error!("error reading: {:?}", k);
                self.state = ConnectionState::Error;
                return Err(e);
              }
            }
          }
        }
      }

      if continue_parsing {
        //FIXME: handle the Incomplete case. We need a WantRead and WantWrite signal
        if let Ok((sz, _)) = self.parse(receive_buffer.data()) {
          receive_buffer.consume(sz);
        }
      }
    }
  }

  /// tests whether we can write to the send buffer
  pub fn can_write(&self, send_buffer: &Buffer) -> bool {
    send_buffer.available_data() > 0 || !self.frame_receiver.is_empty()
  }

  /// tests whether we can read from the receive buffer
  pub fn can_read(&self, receive_buffer: &Buffer) -> bool {
    receive_buffer.available_space() > 0
  }

  /// tests whether we can parse data from the receive buffer
  pub fn can_parse(&self, receive_buffer: &Buffer) -> bool {
    receive_buffer.available_data() > 0
  }

  /// serializes frames to the send buffer then to the writer (if possible)
  pub fn write_to_stream(&mut self, writer: &mut dyn Write, send_buffer: &mut Buffer) -> Result<(usize, ConnectionState)> {
    match self.serialize(send_buffer.space()) {
      Ok((sz, _)) => {
        send_buffer.fill(sz);
      },
      Err(e) => {
        return Err(e);
      }
    }

    match writer.write(&mut send_buffer.data()) {
      Ok(sz) => {
        trace!("wrote {} bytes", sz);
        send_buffer.consume(sz);
        Ok((sz, self.state.clone()))
      },
      Err(e) => Err(e),
    }
  }

  /// read data from the network into the receive buffer
  pub fn read_from_stream(&mut self, reader: &mut dyn Read, receive_buffer: &mut Buffer) -> Result<(usize, ConnectionState)> {
    if self.state == ConnectionState::Initial || self.state == ConnectionState::Error {
      self.state = ConnectionState::Error;
      return Err(Error::new(ErrorKind::Other, "invalid state"))
    }

    match reader.read(&mut receive_buffer.space()) {
      Ok(sz) => {
        trace!("read {} bytes", sz);
        receive_buffer.fill(sz);
        Ok((sz, self.state.clone()))
      },
      Err(e) => Err(e),
    }
  }
}
<commit_msg>async: cleanup io<commit_after>use log::{error, trace};

use std::io::{Error, ErrorKind, Read, Result, Write};

use crate::buffer::Buffer;
use crate::connection::{Connection, ConnectionState};

impl Connection {
  /// helper function to handle reading and writing repeatedly from the network until there's no more state to update
  pub fn run<T: Read + Write>(&mut self, stream: &mut T, send_buffer: &mut Buffer, receive_buffer: &mut Buffer) -> Result<ConnectionState> {
    let mut write_would_block = false;
    let mut read_would_block  = false;

    loop {
      let continue_writing = !write_would_block && self.can_write(send_buffer);
      let continue_reading = !read_would_block && self.can_read(receive_buffer);
      let continue_parsing = self.can_parse(receive_buffer);

      if !continue_writing && !continue_reading && !continue_parsing {
        return Ok(self.state.clone());
      }

      if continue_writing {
        if let Err(e) = self.write_to_stream(stream, send_buffer) {
          match e.kind() {
            ErrorKind::WouldBlock => {
              write_would_block = true;
            },
            k => {
              error!("error writing: {:?}", k);
              self.state = ConnectionState::Error;
              return Err(e);
            }
          }
        }
      }

      if continue_reading {
        if let Err(e) = self.read_from_stream(stream, receive_buffer) {
          match e.kind() {
            ErrorKind::WouldBlock => {
              read_would_block = true;
            },
            k => {
              error!("error reading: {:?}", k);
              self.state = ConnectionState::Error;
              return Err(e);
            }
          }
        }
      }

      if continue_parsing {
        //FIXME: handle the Incomplete case. We need a WantRead and WantWrite signal
        if let Ok((sz, _)) = self.parse(receive_buffer.data()) {
          receive_buffer.consume(sz);
        }
      }
    }
  }

  /// tests whether we can write to the send buffer
  fn can_write(&self, send_buffer: &Buffer) -> bool {
    send_buffer.available_data() > 0 || !self.frame_receiver.is_empty()
  }

  /// tests whether we can read from the receive buffer
  fn can_read(&self, receive_buffer: &Buffer) -> bool {
    receive_buffer.available_space() > 0
  }

  /// tests whether we can parse data from the receive buffer
  fn can_parse(&self, receive_buffer: &Buffer) -> bool {
    receive_buffer.available_data() > 0
  }

  /// serializes frames to the send buffer then to the writer (if possible)
  fn write_to_stream(&mut self, writer: &mut dyn Write, send_buffer: &mut Buffer) -> Result<(usize, ConnectionState)> {
    let (sz, _) = self.serialize(send_buffer.space())?;
    send_buffer.fill(sz);

    writer.write(&mut send_buffer.data()).map(|sz| {
      trace!("wrote {} bytes", sz);
      send_buffer.consume(sz);
      (sz, self.state.clone())
    })
  }

  /// read data from the network into the receive buffer
  fn read_from_stream(&mut self, reader: &mut dyn Read, receive_buffer: &mut Buffer) -> Result<(usize, ConnectionState)> {
    if self.state == ConnectionState::Initial || self.state == ConnectionState::Error {
      self.state = ConnectionState::Error;
      return Err(Error::new(ErrorKind::Other, "invalid state"));
    }

    reader.read(&mut receive_buffer.space()).map(|sz| {
      trace!("read {} bytes", sz);
      receive_buffer.fill(sz);
      (sz, self.state.clone())
    })
  }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

// http://bitsquid.blogspot.ru/2012/01/sensible-error-handling-part-1.html

#![feature(macro_rules)]

use std::cast::transmute;
use std::cell;

#[macro_export]
macro_rules! set_error_context (
    ($name: expr, $data: expr) => (
        let _ec = error_context::ErrorContext::new($name, $data);
    )
)

// ErrorContext version without destructor for storing in global array
struct ErrorContextInternal {
    description: &'static str,
    data: &'static str,
}

local_data_key!(CONTEXTS: cell::RefCell<Vec<ErrorContextInternal>>)

pub struct ErrorContext<'a> {
    description: &'static str,
    data: &'a str,
}

impl<'a> ErrorContext<'a> {
    #[inline]
    pub fn new<'a>(description: &'static str, data: &'a str) -> ErrorContext<'a> {
        if !is_initialized() {
            init();
        }
        match CONTEXTS.get() {
            Some(contexts) => {
                contexts.borrow_mut().push(ErrorContextInternal {
                    description: description,
                    data: unsafe {
                        transmute::<&'a str, &'static str>(data)
                    },
                });
            }
            None => fail!("Contexts can not be None"),
        }
        ErrorContext {
            description: description,
            data: data,
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for ErrorContext<'a> {
    fn drop(&mut self) {
        if std::task::failing() {
            on_task_fail();
        } else {
            match CONTEXTS.get() {
                Some(contexts) => {
                    contexts.borrow_mut().pop();
                },
                None => {},
            }
        }
    }
}

fn init() {
    assert!(!is_initialized());
    CONTEXTS.replace(Some(cell::RefCell::new(Vec::new())));
}

#[inline]
fn is_initialized() -> bool {
    CONTEXTS.get().is_some()
}

fn print_contexts() {
    match CONTEXTS.get() {
        Some(contexts) => {
            for context in contexts.borrow().iter() {
                println!("When {}: {}", context.description, context.data);
            }
        },
        None => fail!("Contexts can not be None"),
    }
}

fn on_task_fail() {
    print_contexts();
    match CONTEXTS.get() {
        Some(contexts) => contexts.borrow_mut().clear(),
        None => fail!("Contexts can not be None"),
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>Updated to latest Rust: std::cast::transmute -> std::mem::transmute<commit_after>// See LICENSE file for copyright and license details.

// http://bitsquid.blogspot.ru/2012/01/sensible-error-handling-part-1.html

#![feature(macro_rules)]

use std::mem::transmute;
use std::cell;

#[macro_export]
macro_rules! set_error_context (
    ($name: expr, $data: expr) => (
        let _ec = error_context::ErrorContext::new($name, $data);
    )
)

// ErrorContext version without destructor for storing in global array
struct ErrorContextInternal {
    description: &'static str,
    data: &'static str,
}

local_data_key!(CONTEXTS: cell::RefCell<Vec<ErrorContextInternal>>)

pub struct ErrorContext<'a> {
    description: &'static str,
    data: &'a str,
}

impl<'a> ErrorContext<'a> {
    #[inline]
    pub fn new<'a>(description: &'static str, data: &'a str) -> ErrorContext<'a> {
        if !is_initialized() {
            init();
        }
        match CONTEXTS.get() {
            Some(contexts) => {
                contexts.borrow_mut().push(ErrorContextInternal {
                    description: description,
                    data: unsafe {
                        transmute::<&'a str, &'static str>(data)
                    },
                });
            }
            None => fail!("Contexts can not be None"),
        }
        ErrorContext {
            description: description,
            data: data,
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for ErrorContext<'a> {
    fn drop(&mut self) {
        if std::task::failing() {
            on_task_fail();
        } else {
            match CONTEXTS.get() {
                Some(contexts) => {
                    contexts.borrow_mut().pop();
                },
                None => {},
            }
        }
    }
}

fn init() {
    assert!(!is_initialized());
    CONTEXTS.replace(Some(cell::RefCell::new(Vec::new())));
}

#[inline]
fn is_initialized() -> bool {
    CONTEXTS.get().is_some()
}

fn print_contexts() {
    match CONTEXTS.get() {
        Some(contexts) => {
            for context in contexts.borrow().iter() {
                println!("When {}: {}", context.description, context.data);
            }
        },
        None => fail!("Contexts can not be None"),
    }
}

fn on_task_fail() {
    print_contexts();
    match CONTEXTS.get() {
        Some(contexts) => contexts.borrow_mut().clear(),
        None => fail!("Contexts can not be None"),
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>extern crate bf;
extern crate clap;

use std::io::{self, Read, Write};
use std::fs::File;
use std::process::exit;

use clap::{Arg, App};

use bf::ast;
use bf::state;
use bf::rle_ast;
use bf::flat;
use bf::interpreter::Interpretable;

#[derive(Debug, Clone)]
struct Options {
    program_text: Vec<u8>,
    memory_size:  Option<usize>,
}

fn main() {
    // Process command-line options:
    let options = get_options();

    // Parse the program to AST:
    let program = parse(&options);

    // Two compilation/optimization passes:
    let program = rle_ast::compiler::compile(&program);
    let program = flat::compiler::compile(&program);

    // Run it:
    interpret(&*program, &options);
}

fn parse(options: &Options) -> ast::Program {
    ast::parser::parse_program(&options.program_text)
        .unwrap_or_else(|e| error_exit(2, format!("Syntax error: {:?}.", e)))
}

fn interpret<P: Interpretable + ?Sized>(program: &P, options: &Options) {
    let state = options.memory_size.map(state::State::with_capacity);
    program.interpret_stdin(state)
        .unwrap_or_else(|e| error_exit(3, format!("Runtime error: {:?}.", e)))
}

fn get_options() -> Options {
    let mut result = Options {
        program_text: Vec::new(),
        memory_size:  None,
    };

    let matches = build_clap_app().get_matches();

    if let Some(size) = matches.value_of("size") {
        let size = size.parse()
            .unwrap_or_else(|e| error_exit(1, format!("Could not parse memory size: {}", e)));
        result.memory_size = Some(size);
    }

    if let Some(exprs) = matches.values_of("expr") {
        for e in exprs {
            result.program_text.extend(e.as_bytes());
        }
    } else if let Some(files) = matches.values_of("INPUT") {
        for f in files {
            let mut file = File::open(f)
                .unwrap_or_else(|e| error_exit(1, format!("{}: {}", e, f)));
            file.read_to_end(&mut result.program_text)
                .unwrap_or_else(|e| error_exit(1, format!("{}: {}", e, f)));
        }
    } else {
        error_exit(1, "No program given.".to_owned());
    }

    result
}

fn build_clap_app() -> App<'static, 'static> {
    App::new("bfi")
        .author("Jesse A. Tov <jesse.tov@gmail.com>")
        .about("A Brainfuck interpreter")
        .arg(Arg::with_name("expr")
            .short("e")
            .long("expr")
            .value_name("CODE")
            .help("BF code to execute")
            .multiple(true)
            .takes_value(true)
            .conflicts_with("INPUT"))
        .arg(Arg::with_name("INPUT")
            .help("The source file(s) to interpret")
            .multiple(true)
            .conflicts_with("expr")
            .index(1))
        .arg(Arg::with_name("size")
            .short("s")
            .long("size")
            .help("Memory size in bytes")
            .takes_value(true))
}

fn error_exit(code: i32, msg: String) -> ! {
    writeln!(io::stderr(), "{}", msg).unwrap();
    exit(code)
}

<commit_msg>Added compiler pass option for comparing different interpreters.<commit_after>extern crate bf;
extern crate clap;

use std::io::{self, Read, Write};
use std::fs::File;
use std::process::exit;

use clap::{Arg, App};

use bf::ast;
use bf::state;
use bf::rle_ast;
use bf::flat;
use bf::interpreter::Interpretable;

#[derive(Debug, Clone)]
struct Options {
    program_text:  Vec<u8>,
    memory_size:   Option<usize>,
    compiler_pass: Pass,
}

#[derive(Debug, Clone, Copy)]
enum Pass {
    Ast,
    Rle,
    Flat,
}

fn main() {
    // Process command-line options:
    let options = get_options();

    // Parse the program to AST:
    let program = parse(&options);

    match options.compiler_pass {
        Pass::Ast => {
            interpret(&*program, &options);
        }

        Pass::Rle => {
            let program = rle_ast::compiler::compile(&program);
            interpret(&*program, &options);
        }

        Pass::Flat => {
            let program = rle_ast::compiler::compile(&program);
            let program = flat::compiler::compile(&program);
            interpret(&*program, &options);
        }

    }
}

fn parse(options: &Options) -> ast::Program {
    ast::parser::parse_program(&options.program_text)
        .unwrap_or_else(|e| error_exit(2, format!("Syntax error: {:?}.", e)))
}

fn interpret<P: Interpretable + ?Sized>(program: &P, options: &Options) {
    let state = options.memory_size.map(state::State::with_capacity);
    program.interpret_stdin(state)
        .unwrap_or_else(|e| error_exit(3, format!("Runtime error: {:?}.", e)))
}

fn get_options() -> Options {
    let mut result = Options {
        program_text:  Vec::new(),
        memory_size:   None,
        compiler_pass: Pass::Flat,
    };

    let matches = build_clap_app().get_matches();

    if let Some(size) = matches.value_of("size") {
        let size = size.parse()
            .unwrap_or_else(|e| error_exit(1, format!("Could not parse memory size: {}", e)));
        result.memory_size = Some(size);
    }

    if matches.is_present("ast") {
        result.compiler_pass = Pass::Ast;
    } else if matches.is_present("rle") {
        result.compiler_pass = Pass::Rle;
    } else if matches.is_present("flat") {
        result.compiler_pass = Pass::Flat;
    }

    if let Some(exprs) = matches.values_of("expr") {
        for e in exprs {
            result.program_text.extend(e.as_bytes());
        }
    } else if let Some(files) = matches.values_of("INPUT") {
        for f in files {
            let mut file = File::open(f)
                .unwrap_or_else(|e| error_exit(1, format!("{}: {}", e, f)));
            file.read_to_end(&mut result.program_text)
                .unwrap_or_else(|e| error_exit(1, format!("{}: {}", e, f)));
        }
    } else {
        error_exit(1, "No program given.".to_owned());
    }

    result
}

fn build_clap_app() -> App<'static, 'static> {
    App::new("bfi")
        .author("Jesse A. Tov <jesse.tov@gmail.com>")
        .about("A Brainfuck interpreter")
        .arg(Arg::with_name("expr")
            .short("e")
            .long("expr")
            .value_name("CODE")
            .help("BF code to execute")
            .multiple(true)
            .takes_value(true)
            .conflicts_with("INPUT"))
        .arg(Arg::with_name("INPUT")
            .help("The source file(s) to interpret")
            .multiple(true)
            .conflicts_with("expr")
            .index(1))
        .arg(Arg::with_name("size")
            .short("s")
            .long("size")
            .help("Memory size in bytes")
            .takes_value(true))
        .arg(Arg::with_name("ast")
            .long("ast")
            .help("Interpret the AST directly")
            .conflicts_with_all(&["flat", "rle"]))
        .arg(Arg::with_name("rle")
            .long("rle")
            .help("Interpret the run-length encoded AST directly")
            .conflicts_with_all(&["ast", "flat"]))
        .arg(Arg::with_name("flat")
            .long("flat")
            .help("Interpret the flattened AST (default)")
            .conflicts_with_all(&["ast", "rle"]))
}

fn error_exit(code: i32, msg: String) -> ! {
    writeln!(io::stderr(), "{}", msg).unwrap();
    exit(code)
}

<|endoftext|>


<commit_before>use std::cmp::{max, min};

#[derive(Clone, Debug)]
struct Puzzle {
    board: Vec<Vec<i32>>,
    fixed: Vec<i32>,
    start: (usize, usize),
}

impl Puzzle {
    fn new(initial_board: Vec<Vec<i32>>) -> Self {
        let mut s: (usize, usize) = (0, 0);
        let mut f = initial_board
            .iter()
            .enumerate()
            .flat_map(|(y, r)| r.iter().enumerate().map(move |(x, c)| (y, x, *c)))
            .filter(|(_, _, c)| c > &0)
            .fold(Vec::new(), |mut fixed, (y, x, c)| {
                fixed.push(c);
                if c == 1 {
                    s = (y, x)
                };
                fixed
            });

        f.sort_unstable();

        Puzzle {
            board: initial_board,
            fixed: f,
            start: s,
        }
    }

    pub fn print_board(&self) {
        self.board.iter().for_each(|r| {
            println!("{}", {
                let mut row = String::default();

                r.iter().for_each(|c| match c {
                    c if c < &0 => row.push_str(format!("{:>2} ", " ").as_ref()),
                    c if c == &0 => row.push_str(format!("{:>2} ", ".").as_ref()),
                    _ => row.push_str(format!("{:>2} ", c).as_ref()),
                });
                row
            })
        });
    }

    fn solver(&mut self, current: &(usize, usize), n: &i32, mut next: i32) -> bool {
        if n > self.fixed.last().unwrap() {
            return true;
        }

        if self.board[current.0][current.1] != 0 && &self.board[current.0][current.1] != n {
            return false;
        }

        if self.board[current.0][current.1] == 0 && self.fixed[next as usize] == *n {
            return false;
        }

        let mut backup: i32 = 0;
        if self.board[current.0][current.1] == *n {
            backup = *n;
            next += 1;
        }

        self.board[current.0][current.1] = *n;

        for y in (max(current.0, 1) - 1)..=min(current.0 + 1, self.board.len() - 1) {
            for x in (max(current.1, 1) - 1)..=min(current.1 + 1, self.board[0].len() - 1) {
                if self.solver(&(y, x), &(n + 1), next) {
                    return true;
                }
            }
        }

        self.board[current.0][current.1] = backup;
        false
    }

    pub fn solve(&mut self) {
        let start = &self.start.clone();
        self.solver(start, &1, 0);
    }
}

fn main() {
    let input = vec![
        vec![0, 33, 35, 0, 0, -1, -1, -1],
        vec![0, 0, 24, 22, 0, -1, -1, -1],
        vec![0, 0, 0, 21, 0, 0, -1, -1],
        vec![0, 26, 0, 13, 40, 11, -1, -1],
        vec![27, 0, 0, 0, 9, 0, 1, -1],
        vec![-1, -1, 0, 0, 18, 0, 0, -1],
        vec![-1, -1, -1, -1, 0, 7, 0, 0],
        vec![-1, -1, -1, -1, -1, -1, 5, 0],
    ];

    let mut p = Puzzle::new(input);
    p.print_board();
    p.solve();
    println!("\nSolution:");
    p.print_board();
}
<commit_msg>Restructuring, adding comments and test<commit_after>use std::cmp::{max, min};
use std::fmt;
use std::ops;

#[derive(Debug, Clone, PartialEq)]
struct Board {
    cells: Vec<Vec<Option<u32>>>,
}

impl Board {
    fn new(initial_board: Vec<Vec<u32>>) -> Self {
        let b = initial_board
            .iter()
            .map(|r| {
                r.iter()
                    .map(|c| if *c == u32::MAX { None } else { Some(*c) })
                    .collect()
            })
            .collect();

        Board { cells: b }
    }

    fn height(&self) -> usize {
        self.cells.len()
    }

    fn width(&self) -> usize {
        self.cells[0].len()
    }
}
impl ops::Index<(usize, usize)> for Board {
    type Output = Option<u32>;

    fn index(&self, (y, x): (usize, usize)) -> &Self::Output {
        &self.cells[y][x]
    }
}
impl ops::IndexMut<(usize, usize)> for Board {
    /// Returns a mutable reference to an cell for a given 'x' 'y' coordinates
    fn index_mut(&mut self, (y, x): (usize, usize)) -> &mut Option<u32> {
        &mut self.cells[y][x]
    }
}

impl fmt::Display for Board {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let output: Vec<String> = self
            .cells
            .iter()
            .map(|r| {
                let mut row = String::default();

                r.iter().for_each(|c| match c {
                    None => row.push_str(format!("{:>2} ", " ").as_ref()),
                    Some(c) if c == &0 => row.push_str(format!("{:>2} ", ".").as_ref()),
                    Some(c) => row.push_str(format!("{:>2} ", c).as_ref()),
                });
                row
            })
            .collect();

        write!(f, "{}", output.join("\n"))
    }
}

/// Structure for holding puzzle related information
/// * `board`: the state of the board
/// * `fixed`: all the numbers which were given at puzzle setup,
/// aka all the numbers which cannot be changed during solving the puzzle
/// * `start`: position of the first number (1)
#[derive(Clone, Debug)]
struct Puzzle {
    board: Board,
    fixed: Vec<u32>,
    start: (usize, usize),
}

impl Puzzle {
    /// Creates a new puzzle
    /// * `initial_board` contains the  layout and the startin position.
    ///
    /// - Simple numbers in the `initial_board` are considered as "fixed",
    /// aka the solving does not change them
    ///
    /// - As the board can be non-rectangular, all cells which are invalid or cannot be used
    /// are marked with u32::MAX in the `initial_board`
    fn new(initial_board: Vec<Vec<u32>>) -> Self {
        let mut s: (usize, usize) = (0, 0);
        let mut f = initial_board
            .iter()
            .enumerate()
            .flat_map(|(y, r)| r.iter().enumerate().map(move |(x, c)| (y, x, *c)))
            .filter(|(_, _, c)| (1..u32::MAX).contains(c))
            .fold(Vec::new(), |mut fixed, (y, x, c)| {
                fixed.push(c);
                if c == 1 {
                    // store the position of the start
                    s = (y, x)
                };
                fixed
            });

        f.sort_unstable();

        Puzzle {
            board: Board::new(initial_board),
            fixed: f,
            start: s,
        }
    }

    pub fn print_board(&self) {
        println!("{}", self.board);
    }

    fn solver(&mut self, current: (usize, usize), n: &u32, mut next: usize) -> bool {
        // reached the last number, solving successful
        if n > self.fixed.last().unwrap() {
            return true;
        }

        // check for exit conditions
        match self.board[current] {
            // cell outside of the board
            None => return false,

            //cell is already has a number in it
            Some(c) if c != 0 && c != *n => return false,

            //cell is empty, but the to be placed number is already matching the next fixed number
            Some(c) if c == 0 && self.fixed[next] == *n => return false,

            // continue
            _ => (),
        }

        let mut backup: u32 = 0;
        if self.board[current] == Some(*n) {
            backup = *n;
            next += 1;
        }

        self.board[current] = Some(*n);

        for y in (max(current.0, 1) - 1)..=min(current.0 + 1, self.board.height() - 1) {
            for x in (max(current.1, 1) - 1)..=min(current.1 + 1, self.board.width() - 1) {
                if self.solver((y, x), &(n + 1), next) {
                    return true;
                }
            }
        }

        // unsuccessful branch, restore original value
        self.board[current] = Some(backup);
        false
    }

    pub fn solve(&mut self) {
        let start = self.start;
        self.solver(start, &1, 0);
    }
}

fn main() {
    let input = vec![
        vec![0, 33, 35, 0, 0, u32::MAX, u32::MAX, u32::MAX],
        vec![0, 0, 24, 22, 0, u32::MAX, u32::MAX, u32::MAX],
        vec![0, 0, 0, 21, 0, 0, u32::MAX, u32::MAX],
        vec![0, 26, 0, 13, 40, 11, u32::MAX, u32::MAX],
        vec![27, 0, 0, 0, 9, 0, 1, u32::MAX],
        vec![u32::MAX, u32::MAX, 0, 0, 18, 0, 0, u32::MAX],
        vec![u32::MAX, u32::MAX, u32::MAX, u32::MAX, 0, 7, 0, 0],
        vec![
            u32::MAX,
            u32::MAX,
            u32::MAX,
            u32::MAX,
            u32::MAX,
            u32::MAX,
            5,
            0,
        ],
    ];

    let mut p = Puzzle::new(input);
    p.print_board();
    p.solve();
    println!("\nSolution:");
    p.print_board();
}

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

    #[test]
    fn test_solver() {
        let input = vec![vec![0, 1], vec![4, 0]];
        let mut p = Puzzle::new(input);
        p.solve();
        assert_eq!(p.board, Board::new(vec![vec![2, 1], vec![4, 3]]));

        // test disabled cells
        let input = vec![vec![u32::MAX, 1, 0], vec![5, 0, 0]];
        let mut p = Puzzle::new(input);
        p.solve();
        assert_eq!(
            p.board,
            Board::new(vec![vec![u32::MAX, 1, 2], vec![5, 4, 3]])
        );
    }
}
<|endoftext|>