<commit_before>use std::collections::HashMap;
use std::rc::Rc;
use std::str;
use std::iter;
use std::hash;
use digit_slice::{DigitSlice, FromDigits};

/// A wrapper for referencing Noun-like patterns.
#[derive(Copy, Clone)]
pub enum Shape<A, N> {
    Atom(A),
    Cell(N, N),
}

/// A Nock noun, the basic unit of representation.
///
/// A noun is an atom or a cell. An atom is any natural number. A cell is any
/// ordered pair of nouns.
///
/// Atoms are represented by a little-endian byte array of 8-bit digits.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Noun(Inner);

#[derive(Clone, PartialEq, Eq, Debug)]
enum Inner {
    Atom(Rc<Vec<u8>>),
    Cell(Rc<Noun>, Rc<Noun>),
}

pub type NounShape<'a> = Shape<&'a [u8], &'a Noun>;

impl Noun {
    fn get<'a>(&'a self) -> NounShape<'a> {
        match self.0 {
            Inner::Atom(ref v) => Shape::Atom(&v),
            Inner::Cell(ref a, ref b) => Shape::Cell(&*a, &*b),
        }
    }

    /// Pattern-match a noun with shape [p q r].
    ///
    /// The digit sequence shows the branch length of each leaf node in the
    /// expression being matched. 122 has the leftmost leaf 1 step away from
    /// the root and the two leaves on the right both 2 steps away from the
    /// root.
    pub fn get_122<'a>
        (&'a self)
         -> Option<(NounShape<'a>, NounShape<'a>, NounShape<'a>)> {
        if let Shape::Cell(ref a, ref b) = self.get() {
            if let Shape::Cell(ref b, ref c) = b.get() {
                return Some((a.get(), b.get(), c.get()));
            }
        }
        None
    }

    /// Pattern-match a noun with shape [[p q] r].
    pub fn get_221<'a>
        (&'a self)
         -> Option<(NounShape<'a>, NounShape<'a>, NounShape<'a>)> {
        if let Shape::Cell(ref a, ref c) = self.get() {
            if let Shape::Cell(ref a, ref b) = a.get() {
                return Some((a.get(), b.get(), c.get()));
            }
        }
        None
    }

    /// Memory address or other unique identifier for the noun.
    fn addr(&self) -> usize {
        &*self as *const _ as usize
    }

    /// Build a new atom noun from a little-endian 8-bit digit sequence.
    pub fn atom(digits: &[u8]) -> Noun {
        Noun(Inner::Atom(Rc::new(digits.to_vec())))
    }

    /// Build a new cell noun from two existing nouns.
    pub fn cell(a: Noun, b: Noun) -> Noun {
        Noun(Inner::Cell(Rc::new(a), Rc::new(b)))
    }

    /// Build a noun from a convertible value.
    pub fn from<T: ToNoun>(item: T) -> Noun {
        item.to_noun()
    }

    /// Run a memoizing fold over the noun
    fn fold<'a, F, T>(&'a self, mut f: F) -> T
        where F: FnMut(Shape<&'a [u8], T>) -> T,
              T: Clone
    {
        fn h<'a, F, T>(noun: &'a Noun,
                       memo: &mut HashMap<usize, T>,
                       f: &mut F)
                       -> T
            where F: FnMut(Shape<&'a [u8], T>) -> T,
                  T: Clone
        {
            let key = noun.addr();

            if memo.contains_key(&key) {
                memo.get(&key).unwrap().clone()
            } else {
                let ret = match noun.get() {
                    Shape::Atom(x) => f(Shape::Atom(x)),
                    Shape::Cell(ref a, ref b) => {
                        let a = h(*a, memo, f);
                        let b = h(*b, memo, f);
                        let ret = f(Shape::Cell(a, b));
                        ret
                    }
                };
                memo.insert(key, ret.clone());
                ret
            }
        }

        h(self, &mut HashMap::new(), &mut f)
    }
}

impl hash::Hash for Noun {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        fn f<H: hash::Hasher>(state: &mut H, shape: Shape<&[u8], u64>) -> u64 {
            match shape {
                Shape::Atom(x) => x.hash(state),
                Shape::Cell(a, b) => {
                    a.hash(state);
                    b.hash(state);
                }
            }
            state.finish()
        }
        self.fold(|x| f(state, x))
            .hash(state);
    }
}

impl iter::FromIterator<Noun> for Noun {
    fn from_iter<T>(iterator: T) -> Self
        where T: IntoIterator<Item = Noun>
    {
        let mut v: Vec<Noun> = iterator.into_iter().collect();
        v.reverse();

        v.into_iter()
         .fold(None, move |acc, i| {
             acc.map_or_else(|| Some(i.clone()),
                             |a| Some(Noun::cell(i.clone(), a)))
         })
         .expect("Can't make noun from empty list")
    }
}

/// Trait for types that can convert themselves to a noun.
pub trait ToNoun {
    fn to_noun(&self) -> Noun;
}

impl<T> ToNoun for T where T: DigitSlice
{
    fn to_noun(&self) -> Noun {
        Noun::atom(self.as_digits())
    }
}

/// A trait for types that can be instantiated from a Nock noun.
pub trait FromNoun: Sized {
    /// The associated error.
    type Err;

    /// Try to convert a noun to an instance of the type.
    fn from_noun(n: &Noun) -> Result<Self, Self::Err>;
}

impl<T> FromNoun for T where T: FromDigits
{
    type Err = ();

    fn from_noun(n: &Noun) -> Result<Self, Self::Err> {
        match n.get() {
            Shape::Atom(x) => T::from_digits(x).map_err(|_| ()),
            _ => Err(()),
        }
    }
}

impl<T, U> FromNoun for (T, U)
    where T: FromNoun,
          U: FromNoun
{
    type Err = ();

    fn from_noun(n: &Noun) -> Result<Self, Self::Err> {
        match n.get() {
            Shape::Cell(a, b) => {
                let t = try!(T::from_noun(a).map_err(|_| ()));
                let u = try!(U::from_noun(b).map_err(|_| ()));
                Ok((t, u))
            }
            _ => Err(()),
        }
    }
}

// TODO: FromNoun for T: FromIterator<U: FromNoun>. Pair impl should give us
// a HashMap derivation then. Use ~-terminated cell sequence as backend.

// TODO: Turn a ~-terminated noun into a vec or an iter. Can fail if the last
// element isn't a ~, and we'll only know when we hit the last element...
// Return type is Option<Vec<&'a Noun>>?

// TODO: ToNoun for T: IntoIterator<U: ToNoun>.

// TODO: FromNoun/ToNoun for String, compatible with cord datatype.

// TODO: FromNoun/ToNoun for signed numbers using the Urbit representation
// convention.

// Into-conversion is only used so that we can put untyped numeric literals in
// the noun-constructing macro and have them typed as unsigned. If the noun
// constructor uses ToNoun, literals are assumed to be i32, which does not map
// to atoms in quite the way we want.
impl Into<Noun> for u64 {
    fn into(self) -> Noun {
        Noun::from(self)
    }
}

/// Macro for noun literals.
///
/// Rust n![1, 2, 3] corresponds to Nock [1 2 3]
#[macro_export]
macro_rules! n {
    [$x:expr, $y:expr] => { $crate::draft::Noun::cell($x.into(), $y.into()) };
    [$x:expr, $y:expr, $($ys:expr),+] => { $crate::draft::Noun::cell($x.into(), n![$y, $($ys),+]) };
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ParseError;

impl str::FromStr for Noun {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, ParseError> {
        return parse(&mut s.chars().peekable());

        fn parse<I: Iterator<Item = char>>(input: &mut iter::Peekable<I>)
                                           -> Result<Noun, ParseError> {
            eat_space(input);
            match input.peek().map(|&x| x) {
                Some(c) if c.is_digit(10) => parse_atom(input),
                Some(c) if c == '[' => parse_cell(input),
                _ => Err(ParseError),
            }
        }

        /// Parse an atom, a positive integer.
        fn parse_atom<I: Iterator<Item = char>>(input: &mut iter::Peekable<I>)
                                                -> Result<Noun, ParseError> {
            use num::BigUint;
            let mut buf = Vec::new();

            loop {
                if let Some(&c) = input.peek() {
                    if c.is_digit(10) {
                        input.next();
                        buf.push(c);
                    } else if c == '.' {
                        // Dot is used as a sequence separator (*not* as
                        // decimal point). It can show up anywhere in the
                        // digit sequence and will be ignored.
                        input.next();
                    } else if c == '[' || c == ']' || c.is_whitespace() {
                        // Whitespace or cell brackets can terminate the
                        // digit sequence.
                        break;
                    } else {
                        // Anything else in the middle of the digit sequence
                        // is an error.
                        return Err(ParseError);
                    }
                } else {
                    break;
                }
            }

            if buf.len() == 0 {
                return Err(ParseError);
            }

            let num: BigUint = buf.into_iter()
                                  .collect::<String>()
                                  .parse()
                                  .expect("Failed to parse atom");

            Ok(Noun::from(num))
        }

        /// Parse a cell, a bracketed pair of nouns.
        ///
        /// For additional complication, cells can have the form [a b c] which
        /// parses to [a [b c]].
        fn parse_cell<I: Iterator<Item = char>>(input: &mut iter::Peekable<I>)
                                                -> Result<Noun, ParseError> {
            let mut elts = Vec::new();

            if input.next() != Some('[') {
                panic!("Bad cell start");
            }

            // A cell must have at least two nouns in it.
            elts.push(try!(parse(input)));
            elts.push(try!(parse(input)));

            // It can have further trailing nouns.
            loop {
                eat_space(input);
                match input.peek().map(|&x| x) {
                    Some(c) if c.is_digit(10) => {
                        elts.push(try!(parse_atom(input)))
                    }
                    Some(c) if c == '[' => elts.push(try!(parse_cell(input))),
                    Some(c) if c == ']' => {
                        input.next();
                        break;
                    }
                    _ => return Err(ParseError),
                }
            }

            Ok(elts.into_iter().collect())
        }

        fn eat_space<I: Iterator<Item = char>>(input: &mut iter::Peekable<I>) {
            loop {
                match input.peek().map(|&x| x) {
                    Some(c) if c.is_whitespace() => {
                        input.next();
                    }
                    _ => return,
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use std::hash;
    use super::Noun;
    use num::BigUint;

    fn parses(input: &str, output: Noun) {
        assert_eq!(input.parse::<Noun>().ok().expect("Parsing failed"), output);
    }

    #[test]
    fn scratch() {
        let x = Noun::from(123u32);
        assert!(x == Noun::from(123u8));
    }

    fn hash<T: hash::Hash>(t: &T) -> u64 {
        use std::hash::Hasher;
        let mut s = hash::SipHasher::new();
        t.hash(&mut s);
        s.finish()
    }

    #[test]
    fn test_fold() {
        assert_eq!(hash(&n![1, 2, 3]), hash(&n![1, 2, 3]));
        assert!(hash(&n![n![1, 2], 3]) != hash(&n![1, 2, 3]));
        assert!(hash(&n![1, 2, 3]) != hash(&n![1, 2]));
    }

    #[test]
    fn test_parser() {
        use num::traits::Num;

        assert!("".parse::<Noun>().is_err());
        assert!("12ab".parse::<Noun>().is_err());
        assert!("[]".parse::<Noun>().is_err());
        assert!("[1]".parse::<Noun>().is_err());

        parses("0", Noun::from(0u32));
        parses("1", Noun::from(1u32));
        parses("1.000.000", Noun::from(1_000_000u32));

        parses("4294967295", Noun::from(4294967295u32));
        parses("4294967296", Noun::from(4294967296u64));

        parses("999.999.999.999.999.999.999.999.999.999.999.999.999.999.999.\
                999.999.999.999.999",
               Noun::from(BigUint::from_str_radix("999999999999999999999999\
                                                   999999999999999999999999\
                                                   999999999999",
                                                  10)
                              .unwrap()));

        parses("[1 2]", n![1, 2]);
        parses("[1 2 3]", n![1, 2, 3]);
        parses("[1 [2 3]]", n![1, 2, 3]);
        parses("[[1 2] 3]", n![n![1, 2], 3]);
    }
}
<commit_msg>Display code for new noun<commit_after>use std::collections::HashMap;
use std::rc::Rc;
use std::str;
use std::fmt;
use std::iter;
use std::hash;
use num::BigUint;
use digit_slice::{DigitSlice, FromDigits};

/// A wrapper for referencing Noun-like patterns.
#[derive(Copy, Clone)]
pub enum Shape<A, N> {
    Atom(A),
    Cell(N, N),
}

/// A Nock noun, the basic unit of representation.
///
/// A noun is an atom or a cell. An atom is any natural number. A cell is any
/// ordered pair of nouns.
///
/// Atoms are represented by a little-endian byte array of 8-bit digits.
#[derive(Clone, PartialEq, Eq)]
pub struct Noun(Inner);

#[derive(Clone, PartialEq, Eq)]
enum Inner {
    Atom(Rc<Vec<u8>>),
    Cell(Rc<Noun>, Rc<Noun>),
}

pub type NounShape<'a> = Shape<&'a [u8], &'a Noun>;

impl Noun {
    fn get<'a>(&'a self) -> NounShape<'a> {
        match self.0 {
            Inner::Atom(ref v) => Shape::Atom(&v),
            Inner::Cell(ref a, ref b) => Shape::Cell(&*a, &*b),
        }
    }

    /// Pattern-match a noun with shape [p q r].
    ///
    /// The digit sequence shows the branch length of each leaf node in the
    /// expression being matched. 122 has the leftmost leaf 1 step away from
    /// the root and the two leaves on the right both 2 steps away from the
    /// root.
    pub fn get_122<'a>
        (&'a self)
         -> Option<(NounShape<'a>, NounShape<'a>, NounShape<'a>)> {
        if let Shape::Cell(ref a, ref b) = self.get() {
            if let Shape::Cell(ref b, ref c) = b.get() {
                return Some((a.get(), b.get(), c.get()));
            }
        }
        None
    }

    /// Pattern-match a noun with shape [[p q] r].
    pub fn get_221<'a>
        (&'a self)
         -> Option<(NounShape<'a>, NounShape<'a>, NounShape<'a>)> {
        if let Shape::Cell(ref a, ref c) = self.get() {
            if let Shape::Cell(ref a, ref b) = a.get() {
                return Some((a.get(), b.get(), c.get()));
            }
        }
        None
    }

    /// Memory address or other unique identifier for the noun.
    fn addr(&self) -> usize {
        &*self as *const _ as usize
    }

    /// Build a new atom noun from a little-endian 8-bit digit sequence.
    pub fn atom(digits: &[u8]) -> Noun {
        Noun(Inner::Atom(Rc::new(digits.to_vec())))
    }

    /// Build a new cell noun from two existing nouns.
    pub fn cell(a: Noun, b: Noun) -> Noun {
        Noun(Inner::Cell(Rc::new(a), Rc::new(b)))
    }

    /// Build a noun from a convertible value.
    pub fn from<T: ToNoun>(item: T) -> Noun {
        item.to_noun()
    }

    /// Run a memoizing fold over the noun
    fn fold<'a, F, T>(&'a self, mut f: F) -> T
        where F: FnMut(Shape<&'a [u8], T>) -> T,
              T: Clone
    {
        fn h<'a, F, T>(noun: &'a Noun,
                       memo: &mut HashMap<usize, T>,
                       f: &mut F)
                       -> T
            where F: FnMut(Shape<&'a [u8], T>) -> T,
                  T: Clone
        {
            let key = noun.addr();

            if memo.contains_key(&key) {
                memo.get(&key).unwrap().clone()
            } else {
                let ret = match noun.get() {
                    Shape::Atom(x) => f(Shape::Atom(x)),
                    Shape::Cell(ref a, ref b) => {
                        let a = h(*a, memo, f);
                        let b = h(*b, memo, f);
                        let ret = f(Shape::Cell(a, b));
                        ret
                    }
                };
                memo.insert(key, ret.clone());
                ret
            }
        }

        h(self, &mut HashMap::new(), &mut f)
    }
}

impl hash::Hash for Noun {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        fn f<H: hash::Hasher>(state: &mut H, shape: Shape<&[u8], u64>) -> u64 {
            match shape {
                Shape::Atom(x) => x.hash(state),
                Shape::Cell(a, b) => {
                    a.hash(state);
                    b.hash(state);
                }
            }
            state.finish()
        }
        self.fold(|x| f(state, x))
            .hash(state);
    }
}

impl iter::FromIterator<Noun> for Noun {
    fn from_iter<T>(iterator: T) -> Self
        where T: IntoIterator<Item = Noun>
    {
        let mut v: Vec<Noun> = iterator.into_iter().collect();
        v.reverse();

        v.into_iter()
         .fold(None, move |acc, i| {
             acc.map_or_else(|| Some(i.clone()),
                             |a| Some(Noun::cell(i.clone(), a)))
         })
         .expect("Can't make noun from empty list")
    }
}

/// Trait for types that can convert themselves to a noun.
pub trait ToNoun {
    fn to_noun(&self) -> Noun;
}

impl<T> ToNoun for T where T: DigitSlice
{
    fn to_noun(&self) -> Noun {
        Noun::atom(self.as_digits())
    }
}

/// A trait for types that can be instantiated from a Nock noun.
pub trait FromNoun: Sized {
    /// The associated error.
    type Err;

    /// Try to convert a noun to an instance of the type.
    fn from_noun(n: &Noun) -> Result<Self, Self::Err>;
}

impl<T> FromNoun for T where T: FromDigits
{
    type Err = ();

    fn from_noun(n: &Noun) -> Result<Self, Self::Err> {
        match n.get() {
            Shape::Atom(x) => T::from_digits(x).map_err(|_| ()),
            _ => Err(()),
        }
    }
}

impl<T, U> FromNoun for (T, U)
    where T: FromNoun,
          U: FromNoun
{
    type Err = ();

    fn from_noun(n: &Noun) -> Result<Self, Self::Err> {
        match n.get() {
            Shape::Cell(a, b) => {
                let t = try!(T::from_noun(a).map_err(|_| ()));
                let u = try!(U::from_noun(b).map_err(|_| ()));
                Ok((t, u))
            }
            _ => Err(()),
        }
    }
}

// TODO: FromNoun for T: FromIterator<U: FromNoun>. Pair impl should give us
// a HashMap derivation then. Use ~-terminated cell sequence as backend.

// TODO: Turn a ~-terminated noun into a vec or an iter. Can fail if the last
// element isn't a ~, and we'll only know when we hit the last element...
// Return type is Option<Vec<&'a Noun>>?

// TODO: ToNoun for T: IntoIterator<U: ToNoun>.

// TODO: FromNoun/ToNoun for String, compatible with cord datatype.

// TODO: FromNoun/ToNoun for signed numbers using the Urbit representation
// convention.

// Into-conversion is only used so that we can put untyped numeric literals in
// the noun-constructing macro and have them typed as unsigned. If the noun
// constructor uses ToNoun, literals are assumed to be i32, which does not map
// to atoms in quite the way we want.
impl Into<Noun> for u64 {
    fn into(self) -> Noun {
        Noun::from(self)
    }
}

/// Macro for noun literals.
///
/// Rust n![1, 2, 3] corresponds to Nock [1 2 3]
#[macro_export]
macro_rules! n {
    [$x:expr, $y:expr] => { $crate::draft::Noun::cell($x.into(), $y.into()) };
    [$x:expr, $y:expr, $($ys:expr),+] => { $crate::draft::Noun::cell($x.into(), n![$y, $($ys),+]) };
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ParseError;

impl str::FromStr for Noun {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, ParseError> {
        return parse(&mut s.chars().peekable());

        fn parse<I: Iterator<Item = char>>(input: &mut iter::Peekable<I>)
                                           -> Result<Noun, ParseError> {
            eat_space(input);
            match input.peek().map(|&x| x) {
                Some(c) if c.is_digit(10) => parse_atom(input),
                Some(c) if c == '[' => parse_cell(input),
                _ => Err(ParseError),
            }
        }

        /// Parse an atom, a positive integer.
        fn parse_atom<I: Iterator<Item = char>>(input: &mut iter::Peekable<I>)
                                                -> Result<Noun, ParseError> {
            let mut buf = Vec::new();

            loop {
                if let Some(&c) = input.peek() {
                    if c.is_digit(10) {
                        input.next();
                        buf.push(c);
                    } else if c == '.' {
                        // Dot is used as a sequence separator (*not* as
                        // decimal point). It can show up anywhere in the
                        // digit sequence and will be ignored.
                        input.next();
                    } else if c == '[' || c == ']' || c.is_whitespace() {
                        // Whitespace or cell brackets can terminate the
                        // digit sequence.
                        break;
                    } else {
                        // Anything else in the middle of the digit sequence
                        // is an error.
                        return Err(ParseError);
                    }
                } else {
                    break;
                }
            }

            if buf.len() == 0 {
                return Err(ParseError);
            }

            let num: BigUint = buf.into_iter()
                                  .collect::<String>()
                                  .parse()
                                  .expect("Failed to parse atom");

            Ok(Noun::from(num))
        }

        /// Parse a cell, a bracketed pair of nouns.
        ///
        /// For additional complication, cells can have the form [a b c] which
        /// parses to [a [b c]].
        fn parse_cell<I: Iterator<Item = char>>(input: &mut iter::Peekable<I>)
                                                -> Result<Noun, ParseError> {
            let mut elts = Vec::new();

            if input.next() != Some('[') {
                panic!("Bad cell start");
            }

            // A cell must have at least two nouns in it.
            elts.push(try!(parse(input)));
            elts.push(try!(parse(input)));

            // It can have further trailing nouns.
            loop {
                eat_space(input);
                match input.peek().map(|&x| x) {
                    Some(c) if c.is_digit(10) => {
                        elts.push(try!(parse_atom(input)))
                    }
                    Some(c) if c == '[' => elts.push(try!(parse_cell(input))),
                    Some(c) if c == ']' => {
                        input.next();
                        break;
                    }
                    _ => return Err(ParseError),
                }
            }

            Ok(elts.into_iter().collect())
        }

        fn eat_space<I: Iterator<Item = char>>(input: &mut iter::Peekable<I>) {
            loop {
                match input.peek().map(|&x| x) {
                    Some(c) if c.is_whitespace() => {
                        input.next();
                    }
                    _ => return,
                }
            }
        }
    }
}

impl fmt::Display for Noun {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Inner::Atom(ref n) => return dot_separators(f, &n),
            Inner::Cell(ref a, ref b) => {
                try!(write!(f, "[{} ", a));
                // List pretty-printer.
                let mut cur = b;
                loop {
                    match cur.0 {
                        Inner::Cell(ref a, ref b) => {
                            try!(write!(f, "{} ", a));
                            cur = &b;
                        }
                        Inner::Atom(ref n) => {
                            try!(dot_separators(f, &n));
                            return write!(f, "]");
                        }
                    }
                }
            }
        }

        fn dot_separators(f: &mut fmt::Formatter,
                          digits: &[u8])
                          -> fmt::Result {
            let s = format!("{}", BigUint::from_digits(digits).unwrap());
            let phase = s.len() % 3;
            for (i, c) in s.chars().enumerate() {
                if i > 0 && i % 3 == phase {
                    try!(write!(f, "."));
                }
                try!(write!(f, "{}", c));
            }
            Ok(())
        }
    }
}

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

#[cfg(test)]
mod tests {
    use std::hash;
    use super::Noun;
    use num::BigUint;

    fn parses(input: &str, output: Noun) {
        assert_eq!(input.parse::<Noun>().ok().expect("Parsing failed"), output);
    }

    #[test]
    fn scratch() {
        let x = Noun::from(123u32);
        assert!(x == Noun::from(123u8));
    }

    fn hash<T: hash::Hash>(t: &T) -> u64 {
        use std::hash::Hasher;
        let mut s = hash::SipHasher::new();
        t.hash(&mut s);
        s.finish()
    }

    #[test]
    fn test_fold() {
        assert_eq!(hash(&n![1, 2, 3]), hash(&n![1, 2, 3]));
        assert!(hash(&n![n![1, 2], 3]) != hash(&n![1, 2, 3]));
        assert!(hash(&n![1, 2, 3]) != hash(&n![1, 2]));
    }

    #[test]
    fn test_parser() {
        use num::traits::Num;

        assert!("".parse::<Noun>().is_err());
        assert!("12ab".parse::<Noun>().is_err());
        assert!("[]".parse::<Noun>().is_err());
        assert!("[1]".parse::<Noun>().is_err());

        parses("0", Noun::from(0u32));
        parses("1", Noun::from(1u32));
        parses("1.000.000", Noun::from(1_000_000u32));

        parses("4294967295", Noun::from(4294967295u32));
        parses("4294967296", Noun::from(4294967296u64));

        parses("999.999.999.999.999.999.999.999.999.999.999.999.999.999.999.\
                999.999.999.999.999",
               Noun::from(BigUint::from_str_radix("999999999999999999999999\
                                                   999999999999999999999999\
                                                   999999999999",
                                                  10)
                              .unwrap()));

        parses("[1 2]", n![1, 2]);
        parses("[1 2 3]", n![1, 2, 3]);
        parses("[1 [2 3]]", n![1, 2, 3]);
        parses("[[1 2] 3]", n![n![1, 2], 3]);
    }
}
<|endoftext|>


<commit_before>//! Functions for detecting edges in images.

use std::f32;
use image::{GenericImage, GrayImage, ImageBuffer, Luma};
use gradients::{vertical_sobel, horizontal_sobel};
use definitions::{HasWhite, HasBlack};
use filter::gaussian_blur_f32;

/// Runs the canny edge detection algorithm.
///
/// Returns a binary image where edge pixels have a value of 255
///  and non-edge pixels a value of 0.
///
/// # Params
///
/// - `low_threshold`: Low threshold for the hysteresis procedure.
/// Edges with a strength higher than the low threshold will appear
/// in the output image, if there are strong edges nearby.
/// - `high_threshold`: High threshold for the hysteresis procedure.
/// Edges with a strength higher than the high threshold will always
/// appear as edges in the output image.
///
/// The greatest possible edge strength (and so largest sensible threshold)
/// is`sqrt(5) * 2 * 255`, or approximately 1140.39.
///
/// This odd looking value is the result of using a standard
/// definition of edge strength: the strength of an edge at a point `p` is
/// defined to be `sqrt(dx^2 + dy^2)`, where `dx` and `dy` are the values
/// of the horizontal and vertical Sobel gradients at `p`.
pub fn canny(image: &GrayImage,
             low_threshold: f32,
             high_threshold: f32)
             -> GrayImage {
    assert!(high_threshold >= low_threshold);
    // Heavily based on the implementation proposed by wikipedia.
    // 1. Gaussian blur.
    const SIGMA: f32 = 1.4;
    let blurred = gaussian_blur_f32(image, SIGMA);

    // 2. Intensity of gradients.
    let gx = horizontal_sobel(&blurred);
    let gy = vertical_sobel(&blurred);
    let g: Vec<f32> = gx.iter()
                        .zip(gy.iter())
                        .map(|(h, v)| (*h as f32).hypot(*v as f32))
                        .collect::<Vec<f32>>();

    let g = ImageBuffer::from_raw(image.width(), image.height(), g).unwrap();

    // 3. Non-maximum-suppression (Make edges thinner)
    let thinned = non_maximum_suppression(&g, &gx, &gy);

    // 4. Hysteresis to filter out edges based on thresholds.
    hysteresis(&thinned, low_threshold, high_threshold)
}

/// Finds local maxima to make the edges thinner.
fn non_maximum_suppression(g: &ImageBuffer<Luma<f32>, Vec<f32>>,
                           gx: &ImageBuffer<Luma<i16>, Vec<i16>>,
                           gy: &ImageBuffer<Luma<i16>, Vec<i16>>)
                           -> ImageBuffer<Luma<f32>, Vec<f32>> {
    const RADIANS_TO_DEGREES: f32 = 180f32 / f32::consts::PI;
    let mut out = ImageBuffer::from_pixel(g.width(), g.height(), Luma { data: [0.0] });
    for y in 1..g.height() - 1 {
        for x in 1..g.width() - 1 {
            let x_gradient = gx[(x, y)][0] as f32;
            let y_gradient = gy[(x, y)][0] as f32;
            let mut angle = (y_gradient).atan2(x_gradient) * RADIANS_TO_DEGREES;
            if angle < 0.0 {
                angle += 180.0
            }
            // Clamp angle.
            let clamped_angle = if angle >= 157.5 || angle < 22.5 {
                0
            } else if angle >= 22.5 && angle < 67.5 {
                45
            } else if angle >= 67.5 && angle < 112.5 {
                90
            } else if angle >= 112.5 && angle < 157.5 {
                135
            } else {
                unreachable!()
            };

            // Get the two perpendicular neighbors.
            let (cmp1, cmp2) = unsafe {
                match clamped_angle {
                    0 => (g.unsafe_get_pixel(x - 1, y), g.unsafe_get_pixel(x + 1, y)),
                    45 => {
                        (g.unsafe_get_pixel(x + 1, y + 1),
                         g.unsafe_get_pixel(x - 1, y - 1))
                    }
                    90 => (g.unsafe_get_pixel(x, y - 1), g.unsafe_get_pixel(x, y + 1)),
                    135 => {
                        (g.unsafe_get_pixel(x - 1, y + 1),
                         g.unsafe_get_pixel(x + 1, y - 1))
                    }
                    _ => unreachable!(),
                }
            };
            let pixel = *g.get_pixel(x, y);
            // If the pixel is not a local maximum, suppress it.
            if pixel[0] < cmp1[0] || pixel[0] < cmp2[0] {
                out.put_pixel(x, y, Luma { data: [0.0] });
            } else {
                out.put_pixel(x, y, pixel);
            }
        }
    }
    out
}

/// Filter out edges with the thresholds.
/// Non-recursive breadth-first search.
fn hysteresis(input: &ImageBuffer<Luma<f32>, Vec<f32>>,
              low_thresh: f32,
              high_thresh: f32)
              -> ImageBuffer<Luma<u8>, Vec<u8>> {

    let max_brightness = Luma::white();
    let min_brightness = Luma::black();
    // Init output image as all black.
    let mut out = ImageBuffer::from_pixel(input.width(), input.height(), min_brightness);
    // Stack. Possible optimization: Use previously allocated memory, i.e. gx.
    let mut edges = Vec::with_capacity(((input.width() * input.height()) / 2) as usize);
    for y in 1..input.height() - 1 {
        for x in 1..input.width() - 1 {
            let inp_pix = *input.get_pixel(x, y);
            let out_pix = *out.get_pixel(x, y);
            // If the edge strength is higher than high_thresh, mark it as an edge.
            if inp_pix[0] >= high_thresh && out_pix[0] == 0 {
                out.put_pixel(x, y, max_brightness);
                edges.push((x, y));
                // Track neighbors until no neighbor is >= low_thresh.
                while !edges.is_empty() {
                    let (nx, ny) = edges.pop().unwrap();
                    let neighbor_indices = [(nx + 1, ny),
                                            (nx + 1, ny + 1),
                                            (nx, ny + 1),
                                            (nx - 1, ny - 1),
                                            (nx - 1, ny),
                                            (nx - 1, ny + 1)];

                    for neighbor_idx in &neighbor_indices {
                        let in_neighbor = *input.get_pixel(neighbor_idx.0, neighbor_idx.1);
                        let out_neighbor = *out.get_pixel(neighbor_idx.0, neighbor_idx.1);
                        if in_neighbor[0] >= low_thresh && out_neighbor[0] == 0 {
                            out.put_pixel(neighbor_idx.0, neighbor_idx.1, max_brightness);
                            edges.push((neighbor_idx.0, neighbor_idx.1));
                        }
                    }
                }
            }
        }
    }
    out
}

#[cfg(test)]
mod test {
    use super::canny;
    use drawing::{draw_filled_rect_mut};
    use rect::Rect;
    use image::{GrayImage, Luma};
    use test;

    fn edge_detect_bench_image(width: u32, height: u32) -> GrayImage {
        let mut image = GrayImage::new(width, height);
        let (w, h) = (width as i32, height as i32);
        let large = Rect::at(w / 4, h / 4).of_size(width / 2, height / 2);
        let small = Rect::at(9, 9).of_size(3, 3);

        draw_filled_rect_mut(&mut image, large, Luma([255]));
        draw_filled_rect_mut(&mut image, small, Luma([255]));

        image
    }

    #[bench]
    fn bench_canny(b: &mut test::Bencher) {
        let image = edge_detect_bench_image(250, 250);
        b.iter(|| {
            let output = canny(&image, 250.0, 300.0);
            test::black_box(output);
        });
    }
}
<commit_msg>Run rustfmt on edges.rs<commit_after>//! Functions for detecting edges in images.

use std::f32;
use image::{GenericImage, GrayImage, ImageBuffer, Luma};
use gradients::{vertical_sobel, horizontal_sobel};
use definitions::{HasWhite, HasBlack};
use filter::gaussian_blur_f32;

/// Runs the canny edge detection algorithm.
///
/// Returns a binary image where edge pixels have a value of 255
///  and non-edge pixels a value of 0.
///
/// # Params
///
/// - `low_threshold`: Low threshold for the hysteresis procedure.
/// Edges with a strength higher than the low threshold will appear
/// in the output image, if there are strong edges nearby.
/// - `high_threshold`: High threshold for the hysteresis procedure.
/// Edges with a strength higher than the high threshold will always
/// appear as edges in the output image.
///
/// The greatest possible edge strength (and so largest sensible threshold)
/// is`sqrt(5) * 2 * 255`, or approximately 1140.39.
///
/// This odd looking value is the result of using a standard
/// definition of edge strength: the strength of an edge at a point `p` is
/// defined to be `sqrt(dx^2 + dy^2)`, where `dx` and `dy` are the values
/// of the horizontal and vertical Sobel gradients at `p`.
pub fn canny(image: &GrayImage, low_threshold: f32, high_threshold: f32) -> GrayImage {
    assert!(high_threshold >= low_threshold);
    // Heavily based on the implementation proposed by wikipedia.
    // 1. Gaussian blur.
    const SIGMA: f32 = 1.4;
    let blurred = gaussian_blur_f32(image, SIGMA);

    // 2. Intensity of gradients.
    let gx = horizontal_sobel(&blurred);
    let gy = vertical_sobel(&blurred);
    let g: Vec<f32> = gx.iter()
        .zip(gy.iter())
        .map(|(h, v)| (*h as f32).hypot(*v as f32))
        .collect::<Vec<f32>>();

    let g = ImageBuffer::from_raw(image.width(), image.height(), g).unwrap();

    // 3. Non-maximum-suppression (Make edges thinner)
    let thinned = non_maximum_suppression(&g, &gx, &gy);

    // 4. Hysteresis to filter out edges based on thresholds.
    hysteresis(&thinned, low_threshold, high_threshold)
}

/// Finds local maxima to make the edges thinner.
fn non_maximum_suppression(
    g: &ImageBuffer<Luma<f32>, Vec<f32>>,
    gx: &ImageBuffer<Luma<i16>, Vec<i16>>,
    gy: &ImageBuffer<Luma<i16>, Vec<i16>>,
) -> ImageBuffer<Luma<f32>, Vec<f32>> {
    const RADIANS_TO_DEGREES: f32 = 180f32 / f32::consts::PI;
    let mut out = ImageBuffer::from_pixel(g.width(), g.height(), Luma { data: [0.0] });
    for y in 1..g.height() - 1 {
        for x in 1..g.width() - 1 {
            let x_gradient = gx[(x, y)][0] as f32;
            let y_gradient = gy[(x, y)][0] as f32;
            let mut angle = (y_gradient).atan2(x_gradient) * RADIANS_TO_DEGREES;
            if angle < 0.0 {
                angle += 180.0
            }
            // Clamp angle.
            let clamped_angle = if angle >= 157.5 || angle < 22.5 {
                0
            } else if angle >= 22.5 && angle < 67.5 {
                45
            } else if angle >= 67.5 && angle < 112.5 {
                90
            } else if angle >= 112.5 && angle < 157.5 {
                135
            } else {
                unreachable!()
            };

            // Get the two perpendicular neighbors.
            let (cmp1, cmp2) = unsafe {
                match clamped_angle {
                    0 => (g.unsafe_get_pixel(x - 1, y), g.unsafe_get_pixel(x + 1, y)),
                    45 => {
                        (
                            g.unsafe_get_pixel(x + 1, y + 1),
                            g.unsafe_get_pixel(x - 1, y - 1),
                        )
                    }
                    90 => (g.unsafe_get_pixel(x, y - 1), g.unsafe_get_pixel(x, y + 1)),
                    135 => {
                        (
                            g.unsafe_get_pixel(x - 1, y + 1),
                            g.unsafe_get_pixel(x + 1, y - 1),
                        )
                    }
                    _ => unreachable!(),
                }
            };
            let pixel = *g.get_pixel(x, y);
            // If the pixel is not a local maximum, suppress it.
            if pixel[0] < cmp1[0] || pixel[0] < cmp2[0] {
                out.put_pixel(x, y, Luma { data: [0.0] });
            } else {
                out.put_pixel(x, y, pixel);
            }
        }
    }
    out
}

/// Filter out edges with the thresholds.
/// Non-recursive breadth-first search.
fn hysteresis(
    input: &ImageBuffer<Luma<f32>, Vec<f32>>,
    low_thresh: f32,
    high_thresh: f32,
) -> ImageBuffer<Luma<u8>, Vec<u8>> {

    let max_brightness = Luma::white();
    let min_brightness = Luma::black();
    // Init output image as all black.
    let mut out = ImageBuffer::from_pixel(input.width(), input.height(), min_brightness);
    // Stack. Possible optimization: Use previously allocated memory, i.e. gx.
    let mut edges = Vec::with_capacity(((input.width() * input.height()) / 2) as usize);
    for y in 1..input.height() - 1 {
        for x in 1..input.width() - 1 {
            let inp_pix = *input.get_pixel(x, y);
            let out_pix = *out.get_pixel(x, y);
            // If the edge strength is higher than high_thresh, mark it as an edge.
            if inp_pix[0] >= high_thresh && out_pix[0] == 0 {
                out.put_pixel(x, y, max_brightness);
                edges.push((x, y));
                // Track neighbors until no neighbor is >= low_thresh.
                while !edges.is_empty() {
                    let (nx, ny) = edges.pop().unwrap();
                    let neighbor_indices = [
                        (nx + 1, ny),
                        (nx + 1, ny + 1),
                        (nx, ny + 1),
                        (nx - 1, ny - 1),
                        (nx - 1, ny),
                        (nx - 1, ny + 1),
                    ];

                    for neighbor_idx in &neighbor_indices {
                        let in_neighbor = *input.get_pixel(neighbor_idx.0, neighbor_idx.1);
                        let out_neighbor = *out.get_pixel(neighbor_idx.0, neighbor_idx.1);
                        if in_neighbor[0] >= low_thresh && out_neighbor[0] == 0 {
                            out.put_pixel(neighbor_idx.0, neighbor_idx.1, max_brightness);
                            edges.push((neighbor_idx.0, neighbor_idx.1));
                        }
                    }
                }
            }
        }
    }
    out
}

#[cfg(test)]
mod test {
    use super::canny;
    use drawing::draw_filled_rect_mut;
    use rect::Rect;
    use image::{GrayImage, Luma};
    use test;

    fn edge_detect_bench_image(width: u32, height: u32) -> GrayImage {
        let mut image = GrayImage::new(width, height);
        let (w, h) = (width as i32, height as i32);
        let large = Rect::at(w / 4, h / 4).of_size(width / 2, height / 2);
        let small = Rect::at(9, 9).of_size(3, 3);

        draw_filled_rect_mut(&mut image, large, Luma([255]));
        draw_filled_rect_mut(&mut image, small, Luma([255]));

        image
    }

    #[bench]
    fn bench_canny(b: &mut test::Bencher) {
        let image = edge_detect_bench_image(250, 250);
        b.iter(|| {
            let output = canny(&image, 250.0, 300.0);
            test::black_box(output);
        });
    }
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate test;

extern crate nalgebra;
use self::nalgebra::{DMat};

const NSAMPLES: usize = 6 * 1000;
const NHIDDEN: usize = 20;

// rule of thumb:
// column major = iterate column first
// row major = iterate row first

#[bench]
fn bench_dmat_col_then_row_iteration(bencher: &mut test::Bencher) {
    let dividend = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let divisor = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let mut result = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    bencher.iter(|| {
        // USE THIS. IT'S FASTER.
        // DMat is stored in column major order.
        // it stores one consecutive column after another.
        // in column major order the first (row) index varies
        // faster as one steps through consecutive memory locations.
        // as a result this loops through contiguous memory locations
        // in the inner loop which is faster due to caching.
        for col in 0..result.ncols() {
            for row in 0..result.nrows() {
                let index = (row, col);
                result[index] = result[index] * dividend[index] / divisor[index];
            }
        }
    });
}

#[bench]
fn bench_dmat_row_then_col_iteration(bencher: &mut test::Bencher) {
    let dividend = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let divisor = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let mut result = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    bencher.iter(|| {
        // DONT USE THIS. IT'S SLOWER.
        for row in 0..result.nrows() {
            for col in 0..result.ncols() {
                let index = (row, col);
                result[index] = result[index] * dividend[index] / divisor[index];
            }
        }
    });
}
<commit_msg>also benchmark matrix iteration for ndarray and document it<commit_after>#![feature(test)]
extern crate test;

extern crate nalgebra;
use self::nalgebra::{DMat};

extern crate ndarray;
use ndarray::ArrayBase;

const NSAMPLES: usize = 6 * 1000;
const NHIDDEN: usize = 20;

// column major:
// stores one consecutive column after another.
// row index varies most rapidly as one steps through consecutive memory.
// iterate column in outer loop, row index in inner loop.
//
// row major:
// stores one consecutive row after another.
// column index varies most rapidly as one steps through consecutive memory.
// iterate row in outer loop, column index in inner loop.

// looping through contiguous memory locations
// in the inner loop is much faster due to CPU caching.

// DMat is stored column major.
// ArrayBase is stored row major.

// slow
#[bench]
fn bench_ndarray_col_then_row_iteration(bencher: &mut test::Bencher) {
    let dividend = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let divisor = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    bencher.iter(|| {
        for col in 0..result.shape()[1] {
            for row in 0..result.shape()[0] {
                let index = [row, col];
                result[index] = result[index] * dividend[index] / divisor[index];
            }
        }
    });
}

// 3rd fastest
#[bench]
fn bench_ndarray_row_then_col_iteration(bencher: &mut test::Bencher) {
    let dividend = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let divisor = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    bencher.iter(|| {
        for row in 0..result.shape()[0] {
            for col in 0..result.shape()[1] {
                let index = [row, col];
                result[index] = result[index] * dividend[index] / divisor[index];
            }
        }
    });
}

// slow
#[bench]
fn bench_ndarray_col_then_row_unchecked_iteration(bencher: &mut test::Bencher) {
    let dividend = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let divisor = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    bencher.iter(|| {
        for col in 0..result.shape()[1] {
            for row in 0..result.shape()[0] {
                let index = (row, col);
                unsafe {
                    *result.uget_mut(index) = result.uget(index) *
                        dividend.uget(index) / divisor.uget(index);
                }
            }
        }
    });
}

// FASTEST
#[bench]
fn bench_ndarray_row_then_col_unchecked_iteration(bencher: &mut test::Bencher) {
    let dividend = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let divisor = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    assert!(dividend.is_standard_layout());
    assert!(divisor.is_standard_layout());
    assert!(result.is_standard_layout());
    bencher.iter(|| {
        for row in 0..result.shape()[0] {
            for col in 0..result.shape()[1] {
                let index = (row, col);
                unsafe {
                    *result.uget_mut(index) = result.uget(index) *
                        dividend.uget(index) / divisor.uget(index);
                }
            }
        }
    });
}

// 2nd fastest
#[bench]
fn bench_ndarray_2x_zip_mut_with_iteration(bencher: &mut test::Bencher) {
    let dividend = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let divisor = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    bencher.iter(|| {
        result.zip_mut_with(&dividend, |dst, src| {
            *dst = dst.clone() * src;
        });
        result.zip_mut_with(&divisor, |dst, src| {
            *dst = dst.clone() / src;
        });
    });
}

#[bench]
fn bench_ndarray_zipped_iteration(bencher: &mut test::Bencher) {
    let dividend = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let divisor = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    bencher.iter(|| {
        for ((rresult, rdividend), rdivisor) in result.iter_mut().zip(dividend.iter()).zip(divisor.iter()) {
            *rresult = rresult.clone() * rdividend / rdivisor;
        }
    });
}

// fast
#[bench]
fn bench_dmat_col_then_row_iteration(bencher: &mut test::Bencher) {
    let dividend = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let divisor = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let mut result = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    bencher.iter(|| {
        for col in 0..result.ncols() {
            for row in 0..result.nrows() {
                let index = (row, col);
                result[index] = result[index] * dividend[index] / divisor[index];
            }
        }
    });
}

// slow
#[bench]
fn bench_dmat_row_then_col_iteration(bencher: &mut test::Bencher) {
    let dividend = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let divisor = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let mut result = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    bencher.iter(|| {
        for row in 0..result.nrows() {
            for col in 0..result.ncols() {
                let index = (row, col);
                result[index] = result[index] * dividend[index] / divisor[index];
            }
        }
    });
}
<|endoftext|>


<commit_before>import std::os;
import std::fs;
import std::os_fs;
import std::vec;
import std::map;
import std::str;
import std::uint;
import metadata::cstore;
import driver::session;
import util::filesearch;

export get_rpath_flags, test;

#[cfg(target_os="linux")]
#[cfg(target_os="macos")]
fn get_rpath_flags(sess: session::session, out_filename: str) -> [str] {
    log "preparing the RPATH!";

    let cwd = os::getcwd();
    let sysroot = sess.filesearch().sysroot();
    let output = out_filename;
    let libs = cstore::get_used_crate_files(sess.get_cstore());
    let target_triple = sess.get_opts().target_triple;
    let rpaths = get_rpaths(cwd, sysroot, output, libs, target_triple);
    rpaths_to_flags(rpaths);
    [] // FIXME: activate RPATH!
}

#[cfg(target_os="win32")]
fn get_rpath_flags(_sess: session::session, _out_filename: str) -> [str] {
    []
}

fn rpaths_to_flags(rpaths: [str]) -> [str] {
    vec::map({ |rpath| #fmt("-Wl,-rpath,%s",rpath)}, rpaths)
}

fn get_rpaths(cwd: fs::path, sysroot: fs::path,
              output: fs::path, libs: [fs::path],
              target_triple: str) -> [str] {
    log #fmt("cwd: %s", cwd);
    log #fmt("sysroot: %s", sysroot);
    log #fmt("output: %s", output);
    log #fmt("libs:");
    for libpath in libs {
        log #fmt("    %s", libpath);
    }
    log #fmt("target_triple: %s", target_triple);

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rel_rpaths = get_rpaths_relative_to_output(cwd, output, libs);

    // Make backup absolute paths to the libraries. Binaries can
    // be moved as long as the crates they link against don't move.
    let abs_rpaths = get_absolute_rpaths(cwd, libs);

    // And a final backup rpath to the global library location.
    let fallback_rpaths = [get_install_prefix_rpath(target_triple)];

    fn log_rpaths(desc: str, rpaths: [str]) {
        log #fmt("%s rpaths:", desc);
        for rpath in rpaths {
            log #fmt("    %s", rpath);
        }
    }

    log_rpaths("relative", rel_rpaths);
    log_rpaths("absolute", abs_rpaths);
    log_rpaths("fallback", fallback_rpaths);

    let rpaths = rel_rpaths + abs_rpaths + fallback_rpaths;

    // Remove duplicates
    let rpaths = minimize_rpaths(rpaths);
    ret rpaths;
}

fn get_rpaths_relative_to_output(cwd: fs::path,
                                 output: fs::path,
                                 libs: [fs::path]) -> [str] {
    vec::map(bind get_rpath_relative_to_output(cwd, output, _), libs)
}

fn get_rpath_relative_to_output(cwd: fs::path,
                                output: fs::path,
                                lib: fs::path) -> str {
    "$ORIGIN" + fs::path_sep() + get_relative_to(
        get_absolute(cwd, output),
        get_absolute(cwd, lib))
}

// Find the relative path from one file to another
fn get_relative_to(abs1: fs::path, abs2: fs::path) -> fs::path {
    assert fs::path_is_absolute(abs1);
    assert fs::path_is_absolute(abs2);
    log #fmt("finding relative path from %s to %s",
             abs1, abs2);
    let normal1 = fs::normalize(abs1);
    let normal2 = fs::normalize(abs2);
    let split1 = str::split(normal1, os_fs::path_sep as u8);
    let split2 = str::split(normal2, os_fs::path_sep as u8);
    let len1 = vec::len(split1);
    let len2 = vec::len(split2);
    assert len1 > 0u;
    assert len2 > 0u;

    let max_common_path = uint::min(len1, len2) - 1u;
    let start_idx = 0u;
    while start_idx < max_common_path
        && split1[start_idx] == split2[start_idx] {
        start_idx += 1u;
    }

    let path = [];

    for each _ in uint::range(start_idx, len1 - 1u) {
        path += [".."];
    }

    path += vec::slice(split2, start_idx, len2 - 1u);

    if check vec::is_not_empty(path) {
        ret fs::connect_many(path);
    } else {
        ret ".";
    }
}

fn get_absolute_rpaths(cwd: fs::path, libs: [fs::path]) -> [str] {
    vec::map(bind get_absolute_rpath(cwd, _), libs)
}

fn get_absolute_rpath(cwd: fs::path, lib: fs::path) -> str {
    fs::dirname(get_absolute(cwd, lib))
}

fn get_absolute(cwd: fs::path, lib: fs::path) -> fs::path {
    if fs::path_is_absolute(lib) {
        lib
    } else {
        fs::connect(cwd, lib)
    }
}

fn get_install_prefix_rpath(target_triple: str) -> str {
    let install_prefix = #env("CFG_PREFIX");

    if install_prefix == "" {
        fail "rustc compiled without CFG_PREFIX environment variable";
    }

    let path = [install_prefix]
        + filesearch::relative_target_lib_path(target_triple);
    check vec::is_not_empty(path);
    fs::connect_many(path)
}

fn minimize_rpaths(rpaths: [str]) -> [str] {
    let set = map::new_str_hash::<()>();
    let minimized = [];
    for rpath in rpaths {
        if !set.contains_key(rpath) {
            minimized += [rpath];
            set.insert(rpath, ());
        }
    }
    ret minimized;
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
mod test {
    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags(["path1", "path2"]);
        assert flags == ["-Wl,-rpath,path1", "-Wl,-rpath,path2"];
    }

    #[test]
    fn test_get_absolute1() {
        let cwd = "/dir";
        let lib = "some/path/lib";
        let res = get_absolute(cwd, lib);
        assert res == "/dir/some/path/lib";
    }

    #[test]
    fn test_get_absolute2() {
        let cwd = "/dir";
        let lib = "/some/path/lib";
        let res = get_absolute(cwd, lib);
        assert res == "/some/path/lib";
    }

    #[test]
    fn test_prefix_rpath() {
        let res = get_install_prefix_rpath("triple");
        assert res == #env("CFG_PREFIX") + "/lib/rustc/triple/lib";
    }

    #[test]
    fn test_minimize1() {
        let res = minimize_rpaths(["rpath1", "rpath2", "rpath1"]);
        assert res == ["rpath1", "rpath2"];
    }

    #[test]
    fn test_minimize2() {
        let res = minimize_rpaths(["1a", "2", "2", "1a", "4a",
                                   "1a", "2", "3", "4a", "3"]);
        assert res == ["1a", "2", "4a", "3"];
    }

    #[test]
    fn test_relative_to1() {
        let p1 = "/usr/bin/rustc";
        let p2 = "/usr/lib/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    fn test_relative_to2() {
        let p1 = "/usr/bin/rustc";
        let p2 = "/usr/bin/../lib/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    fn test_relative_to3() {
        let p1 = "/usr/bin/whatever/rustc";
        let p2 = "/usr/lib/whatever/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../../lib/whatever";
    }

    #[test]
    fn test_relative_to4() {
        let p1 = "/usr/bin/whatever/../rustc";
        let p2 = "/usr/lib/whatever/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib/whatever";
    }

    #[test]
    fn test_relative_to5() {
        let p1 = "/usr/bin/whatever/../rustc";
        let p2 = "/usr/lib/whatever/../mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    fn test_relative_to6() {
        let p1 = "/1";
        let p2 = "/2/3";
        let res = get_relative_to(p1, p2);
        assert res == "2";
    }

    #[test]
    fn test_relative_to7() {
        let p1 = "/1/2";
        let p2 = "/3";
        let res = get_relative_to(p1, p2);
        assert res == "..";
    }

    #[test]
    fn test_relative_to8() {
        let p1 = "/home/brian/Dev/rust/build/"
            + "stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so";
        let p2 = "/home/brian/Dev/rust/build/stage2/bin/.."
            + "/lib/rustc/i686-unknown-linux-gnu/lib/libstd.so";
        let res = get_relative_to(p1, p2);
        assert res == ".";
    }

    #[test]
    fn test_rpath_relative() {
        let res = get_rpath_relative_to_output(
            "/usr", "bin/rustc", "lib/libstd.so");
        assert res == "$ORIGIN/../lib";
    }

    #[test]
    fn test_get_absolute_rpath() {
        let res = get_absolute_rpath("/usr", "lib/libstd.so");
        assert res == "/usr/lib";
    }
}
<commit_msg>rpath the path to the runtime<commit_after>import std::os;
import std::fs;
import std::os_fs;
import std::vec;
import std::map;
import std::str;
import std::uint;
import metadata::cstore;
import driver::session;
import util::filesearch;

export get_rpath_flags, test;

#[cfg(target_os="linux")]
#[cfg(target_os="macos")]
fn get_rpath_flags(sess: session::session, out_filename: str) -> [str] {
    log "preparing the RPATH!";

    let cwd = os::getcwd();
    let sysroot = sess.filesearch().sysroot();
    let output = out_filename;
    let libs = cstore::get_used_crate_files(sess.get_cstore());
    // We don't currently rpath native libraries, but we know
    // where rustrt is and we know every rust program needs it
    let libs = libs + [get_sysroot_absolute_rt_lib(sess)];

    let target_triple = sess.get_opts().target_triple;
    let rpaths = get_rpaths(cwd, sysroot, output, libs, target_triple);
    rpaths_to_flags(rpaths);
    [] // FIXME: Activate RPATH!
}

#[cfg(target_os="win32")]
fn get_rpath_flags(_sess: session::session, _out_filename: str) -> [str] {
    []
}

fn get_sysroot_absolute_rt_lib(sess: session::session) -> fs::path {
    let path = [sess.filesearch().sysroot()]
        + filesearch::relative_target_lib_path(
            sess.get_opts().target_triple)
        + [os::dylib_filename("rustrt")];
    check vec::is_not_empty(path);
    fs::connect_many(path)
}

fn rpaths_to_flags(rpaths: [str]) -> [str] {
    vec::map({ |rpath| #fmt("-Wl,-rpath,%s",rpath)}, rpaths)
}

fn get_rpaths(cwd: fs::path, sysroot: fs::path,
              output: fs::path, libs: [fs::path],
              target_triple: str) -> [str] {
    log #fmt("cwd: %s", cwd);
    log #fmt("sysroot: %s", sysroot);
    log #fmt("output: %s", output);
    log #fmt("libs:");
    for libpath in libs {
        log #fmt("    %s", libpath);
    }
    log #fmt("target_triple: %s", target_triple);

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rel_rpaths = get_rpaths_relative_to_output(cwd, output, libs);

    // Make backup absolute paths to the libraries. Binaries can
    // be moved as long as the crates they link against don't move.
    let abs_rpaths = get_absolute_rpaths(cwd, libs);

    // And a final backup rpath to the global library location.
    let fallback_rpaths = [get_install_prefix_rpath(target_triple)];

    fn log_rpaths(desc: str, rpaths: [str]) {
        log #fmt("%s rpaths:", desc);
        for rpath in rpaths {
            log #fmt("    %s", rpath);
        }
    }

    log_rpaths("relative", rel_rpaths);
    log_rpaths("absolute", abs_rpaths);
    log_rpaths("fallback", fallback_rpaths);

    let rpaths = rel_rpaths + abs_rpaths + fallback_rpaths;

    // Remove duplicates
    let rpaths = minimize_rpaths(rpaths);
    ret rpaths;
}

fn get_rpaths_relative_to_output(cwd: fs::path,
                                 output: fs::path,
                                 libs: [fs::path]) -> [str] {
    vec::map(bind get_rpath_relative_to_output(cwd, output, _), libs)
}

fn get_rpath_relative_to_output(cwd: fs::path,
                                output: fs::path,
                                lib: fs::path) -> str {
    "$ORIGIN" + fs::path_sep() + get_relative_to(
        get_absolute(cwd, output),
        get_absolute(cwd, lib))
}

// Find the relative path from one file to another
fn get_relative_to(abs1: fs::path, abs2: fs::path) -> fs::path {
    assert fs::path_is_absolute(abs1);
    assert fs::path_is_absolute(abs2);
    log #fmt("finding relative path from %s to %s",
             abs1, abs2);
    let normal1 = fs::normalize(abs1);
    let normal2 = fs::normalize(abs2);
    let split1 = str::split(normal1, os_fs::path_sep as u8);
    let split2 = str::split(normal2, os_fs::path_sep as u8);
    let len1 = vec::len(split1);
    let len2 = vec::len(split2);
    assert len1 > 0u;
    assert len2 > 0u;

    let max_common_path = uint::min(len1, len2) - 1u;
    let start_idx = 0u;
    while start_idx < max_common_path
        && split1[start_idx] == split2[start_idx] {
        start_idx += 1u;
    }

    let path = [];

    for each _ in uint::range(start_idx, len1 - 1u) {
        path += [".."];
    }

    path += vec::slice(split2, start_idx, len2 - 1u);

    if check vec::is_not_empty(path) {
        ret fs::connect_many(path);
    } else {
        ret ".";
    }
}

fn get_absolute_rpaths(cwd: fs::path, libs: [fs::path]) -> [str] {
    vec::map(bind get_absolute_rpath(cwd, _), libs)
}

fn get_absolute_rpath(cwd: fs::path, lib: fs::path) -> str {
    fs::dirname(get_absolute(cwd, lib))
}

fn get_absolute(cwd: fs::path, lib: fs::path) -> fs::path {
    if fs::path_is_absolute(lib) {
        lib
    } else {
        fs::connect(cwd, lib)
    }
}

fn get_install_prefix_rpath(target_triple: str) -> str {
    let install_prefix = #env("CFG_PREFIX");

    if install_prefix == "" {
        fail "rustc compiled without CFG_PREFIX environment variable";
    }

    let path = [install_prefix]
        + filesearch::relative_target_lib_path(target_triple);
    check vec::is_not_empty(path);
    fs::connect_many(path)
}

fn minimize_rpaths(rpaths: [str]) -> [str] {
    let set = map::new_str_hash::<()>();
    let minimized = [];
    for rpath in rpaths {
        if !set.contains_key(rpath) {
            minimized += [rpath];
            set.insert(rpath, ());
        }
    }
    ret minimized;
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
mod test {
    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags(["path1", "path2"]);
        assert flags == ["-Wl,-rpath,path1", "-Wl,-rpath,path2"];
    }

    #[test]
    fn test_get_absolute1() {
        let cwd = "/dir";
        let lib = "some/path/lib";
        let res = get_absolute(cwd, lib);
        assert res == "/dir/some/path/lib";
    }

    #[test]
    fn test_get_absolute2() {
        let cwd = "/dir";
        let lib = "/some/path/lib";
        let res = get_absolute(cwd, lib);
        assert res == "/some/path/lib";
    }

    #[test]
    fn test_prefix_rpath() {
        let res = get_install_prefix_rpath("triple");
        assert res == #env("CFG_PREFIX") + "/lib/rustc/triple/lib";
    }

    #[test]
    fn test_minimize1() {
        let res = minimize_rpaths(["rpath1", "rpath2", "rpath1"]);
        assert res == ["rpath1", "rpath2"];
    }

    #[test]
    fn test_minimize2() {
        let res = minimize_rpaths(["1a", "2", "2", "1a", "4a",
                                   "1a", "2", "3", "4a", "3"]);
        assert res == ["1a", "2", "4a", "3"];
    }

    #[test]
    fn test_relative_to1() {
        let p1 = "/usr/bin/rustc";
        let p2 = "/usr/lib/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    fn test_relative_to2() {
        let p1 = "/usr/bin/rustc";
        let p2 = "/usr/bin/../lib/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    fn test_relative_to3() {
        let p1 = "/usr/bin/whatever/rustc";
        let p2 = "/usr/lib/whatever/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../../lib/whatever";
    }

    #[test]
    fn test_relative_to4() {
        let p1 = "/usr/bin/whatever/../rustc";
        let p2 = "/usr/lib/whatever/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib/whatever";
    }

    #[test]
    fn test_relative_to5() {
        let p1 = "/usr/bin/whatever/../rustc";
        let p2 = "/usr/lib/whatever/../mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    fn test_relative_to6() {
        let p1 = "/1";
        let p2 = "/2/3";
        let res = get_relative_to(p1, p2);
        assert res == "2";
    }

    #[test]
    fn test_relative_to7() {
        let p1 = "/1/2";
        let p2 = "/3";
        let res = get_relative_to(p1, p2);
        assert res == "..";
    }

    #[test]
    fn test_relative_to8() {
        let p1 = "/home/brian/Dev/rust/build/"
            + "stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so";
        let p2 = "/home/brian/Dev/rust/build/stage2/bin/.."
            + "/lib/rustc/i686-unknown-linux-gnu/lib/libstd.so";
        let res = get_relative_to(p1, p2);
        assert res == ".";
    }

    #[test]
    fn test_rpath_relative() {
        let res = get_rpath_relative_to_output(
            "/usr", "bin/rustc", "lib/libstd.so");
        assert res == "$ORIGIN/../lib";
    }

    #[test]
    fn test_get_absolute_rpath() {
        let res = get_absolute_rpath("/usr", "lib/libstd.so");
        assert res == "/usr/lib";
    }
}
<|endoftext|>


<commit_before>//! Parse tree -> AST conversion

// TODO Do resolve and fold, possibly some lints in this pass

use super::*;
use parser::parsetree;
use parser::span::Spanned;

use lea_core::Const;

impl<'a> From<parsetree::Function<'a>> for Function<'a> {
    fn from(func: parsetree::Function<'a>) -> Self {
        Function {
            params: func.params,
            varargs: func.varargs,
            body: func.body.into(),
            locals: Default::default(),
            upvalues: Default::default(),
        }
    }
}

impl<'a> From<parsetree::Block<'a>> for Block<'a> {
    fn from(block: parsetree::Block<'a>) -> Self {
        Block {
            span: block.span,
            stmts: conv_vec(block.stmts),
            localmap: Default::default(),
        }
    }
}

impl<'a> From<parsetree::_Variable<'a>> for _Variable<'a> {
    fn from(var: parsetree::_Variable<'a>) -> Self {
        match var {
            parsetree::VNamed(name) => {
                VNamed(name)
            }
            parsetree::VIndex(var, index) => {
                VIndex(Box::new(spanned_into(*var)), match index {
                    parsetree::VarIndex::DotIndex(name) => {
                        Box::new(Spanned::new(name.span,
                            ELit(Const::Str(name.value.to_owned()))))
                    }
                    parsetree::VarIndex::ExprIndex(expr) => {
                        Box::new(spanned_into(*expr))
                    }
                })
            }
        }
    }
}

impl<'a> From<parsetree::_Expr<'a>> for _Expr<'a> {
    fn from(expr: parsetree::_Expr<'a>) -> Self {
        match expr {
            parsetree::ELit(lit) => ELit(lit),
            parsetree::EBinOp(lhs, op, rhs) =>
                EBinOp(Box::new(spanned_into(*lhs)), op, Box::new(spanned_into(*rhs))),
            parsetree::EUnOp(op, expr) => EUnOp(op, Box::new(spanned_into(*expr))),
            parsetree::EBraced(expr) => expr.value.into(),

            parsetree::EVar(var) => EVar(spanned_into(var)),

            parsetree::ECall(call) => ECall(match call {
                parsetree::SimpleCall(callee, args) => {
                    SimpleCall(Box::new(spanned_into(*callee)), conv_args(args))
                }
                parsetree::MethodCall(obj, name, args) => {
                    MethodCall(Box::new(spanned_into(*obj)), name, conv_args(args))
                }
            }),

            parsetree::ETable(cons) => {
                ETable(conv_table(cons))
            }

            parsetree::EFunc(func) => EFunc(func.into()),
            parsetree::EArray(elems) => EArray(vec_into(elems)),
            parsetree::EVarArgs => EVarArgs,
        }
    }
}

/// Implemented for parse tree nodes. Converts them into `n` instances of `Target`.
trait Conv<'a> {
    type Target;

    fn conv<F>(self, push: F) where F: FnMut(Self::Target);
}

fn conv_table(cons: parsetree::TableCons) -> Vec<(Expr, Expr)> {
    let mut entries: Vec<(Expr, Expr)> = Vec::new();
    let mut i = 0;  // XXX 1?
    for entry in cons {
        entries.push(match entry {
            parsetree::TableEntry::IdentPair(key, value) => {
                (Spanned::new(key.span, ELit(Const::Str(key.value.into()))), spanned_into(value))
            }
            parsetree::TableEntry::Pair(key, value) => {
                (spanned_into(key), spanned_into(value))
            }
            parsetree::TableEntry::Elem(value) => {
                let idx = i;
                i += 1;
                (Spanned::new(value.span, ELit(Const::Int(idx))), spanned_into(value))
            }
        });
    }

    entries
}

fn conv_vec<'a, T: Conv<'a>>(v: Vec<Spanned<T>>) -> Vec<Spanned<T::Target>> {
    let mut res = Vec::new();
    for item in v.into_iter() {
        let span = item.span;
        item.value.conv(|i| res.push(Spanned::new(span, i)));
    }

    res
}

fn conv_args(args: parsetree::CallArgs) -> Vec<Expr> {
    let mut v = Vec::new();
    args.conv(|e| v.push(e));

    v
}

fn vec_into<U, T: Into<U>>(v: Vec<Spanned<T>>) -> Vec<Spanned<U>> {
    v.into_iter().map(spanned_into).collect()
}

// Needed because Spanned<T> cannot impl Into. Not even specialization can help there.
fn spanned_into<U, T: Into<U>>(item: Spanned<T>) -> Spanned<U> {
    Spanned::new(item.span, item.value.into())
}

impl<'a> Conv<'a> for parsetree::_Stmt<'a> {
    type Target = _Stmt<'a>;

    fn conv<F>(self, mut push: F) where F: FnMut(Self::Target) {
        match self {
            parsetree::SDecl(names, exprs) => {
                push(_Stmt::SDecl(names, vec_into(exprs)));
            }

            parsetree::SAssign(vars, exprs) => {
                push(_Stmt::SAssign(vec_into(vars), vec_into(exprs)));
            }

            parsetree::SDo(block) => {
                push(_Stmt::SDo(block.into()));
            }

            parsetree::SBreak => {
                push(_Stmt::SBreak);
            }

            parsetree::SSemi => {}

            parsetree::SReturn(exprs) => {
                push(_Stmt::SReturn(vec_into(exprs)));
            }

            parsetree::SCall(call) => {
                push(_Stmt::SCall(match call {
                    parsetree::SimpleCall(callee, args) => {
                        SimpleCall(Box::new(spanned_into(*callee)), conv_args(args))
                    }
                    parsetree::MethodCall(obj, name, args) => {
                        MethodCall(Box::new(spanned_into(*obj)), name, conv_args(args))
                    }
                }));
            }

            parsetree::SFunc(var, func) => {
                push(_Stmt::SAssign(
                    vec![spanned_into(var)],
                    vec![Spanned::new(func.body.span, EFunc(func.into()))]
                ));
            }

            parsetree::SMethod(var, name, func) => {
                // var:name(args...)   =>   var.name = function(self, args...)
                let mut func: Function<'a> = func.into();
                func.params.insert(0, Spanned::new(name.span, "self"));

                push(_Stmt::SAssign(
                    vec![Spanned::new(var.span, VIndex(
                        Box::new(spanned_into(var)),
                        Box::new(Spanned::new(name.span, ELit(Const::Str(name.value.to_owned()))))
                    ))],
                    vec![Spanned::new(func.body.span, EFunc(func))]
                ));
            }

            parsetree::SLFunc(local, func) => {
                push(_Stmt::SDecl(vec![local], vec![]));
                push(_Stmt::SAssign(
                    vec![Spanned::new(local.span, VNamed(local.value))],
                    vec![Spanned::new(func.body.span, EFunc(func.into()))]
                ));
            }

            parsetree::SIf { cond, body, elifs, el } => {
                // Build else block for this if statement
                // Start with "pure" else at the end
                let mut myelse: Option<Block> = el.map(|el| el.into());

                for elif in elifs {
                    let (cond, body) = elif.value;

                    myelse = Some(Block {
                        localmap: Default::default(),
                        span: elif.span,
                        stmts: vec![
                            Spanned::new(elif.span, SIf {
                                cond: spanned_into(cond),
                                body: body.into(),
                                el: myelse, // Old else block here
                            }),
                        ],
                    });
                }

                push(_Stmt::SIf {
                    cond: spanned_into(cond),
                    body: body.into(),
                    el: myelse,
                });
            }

            parsetree::SWhile { cond, body } => {
                push(_Stmt::SWhile {
                    cond: spanned_into(cond),
                    body: body.into(),
                });
            }

            parsetree::SRepeat { abort_on, body } => {
                push(_Stmt::SRepeat {
                    abort_on: spanned_into(abort_on),
                    body: body.into(),
                });
            }

            parsetree::SFor { var, start, step, end, body } => {
                push(_Stmt::SFor {
                    var: var,
                    start: spanned_into(start),
                    step: step.map(spanned_into),
                    end: spanned_into(end),
                    body: body.into(),
                });
            }

            parsetree::SForIn { vars, iter, body } => {
                push(_Stmt::SForIn {
                    vars: vars,
                    iter: vec_into(iter),
                    body: body.into(),
                });
            }
        }
    }
}

impl<'a> Conv<'a> for parsetree::CallArgs<'a> {
    type Target = Expr<'a>;

    fn conv<F>(self, mut push: F) where F: FnMut(Self::Target) {
        match self {
            parsetree::CallArgs::Normal(exprs) => {
                for expr in exprs {
                    push(spanned_into(expr))
                }
            }
            parsetree::CallArgs::String(s) => {
                // TODO use real span
                push(Spanned::default(ELit(Const::Str(s))))
            }
            parsetree::CallArgs::Table(cons) => {
                push(Spanned::default(ETable(conv_table(cons))))
            }
        }
    }
}
<commit_msg>Rewrite AST conversion to allow passing a state<commit_after>//! Parse tree -> AST conversion

// TODO Do resolve and fold, possibly some lints in this pass

use super::*;
use parser::parsetree;
use parser::span::{Span, Spanned};

use lea_core::Const;

use std::vec;
use std::mem;

enum ConvResult<T> {
    Zero,
    One(T),
    Two(T, T),

    #[allow(dead_code)] // for future expansion
    Many(Vec<T>),
}

impl<T> ConvResult<T> {
    /// Spans all values with `span` and returns a new result that yields spanned values.
    fn spanned(self, span: Span) -> ConvResult<Spanned<T>> {
        match self {
            ConvResult::Zero => ConvResult::Zero,
            ConvResult::One(t) => ConvResult::One(Spanned::new(span, t)),
            ConvResult::Two(t, t2) => ConvResult::Two(Spanned::new(span, t), Spanned::new(span, t2)),
            ConvResult::Many(v) => ConvResult::Many(
                v.into_iter().map(|elem| Spanned::new(span, elem)).collect()
            ),
        }
    }
}

impl<T> IntoIterator for ConvResult<T> {
    type Item = T;
    type IntoIter = ConvResultIter<T>;

    fn into_iter(self) -> Self::IntoIter {
        match self {
            ConvResult::Zero => ConvResultIter::Zero,
            ConvResult::One(t) => ConvResultIter::One(t),
            ConvResult::Two(t, t2) => ConvResultIter::Two(t, t2),
            ConvResult::Many(v) => ConvResultIter::Many(v.into_iter()),
        }
    }
}

enum ConvResultIter<T> {
    Zero,
    One(T),
    Two(T, T),
    Many(vec::IntoIter<T>),
}

impl<T> Iterator for ConvResultIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        let current = mem::replace(self, ConvResultIter::Zero);

        let (new, result) = match current {
            ConvResultIter::Zero => (ConvResultIter::Zero, None),
            ConvResultIter::One(t) => (ConvResultIter::Zero, Some(t)),
            ConvResultIter::Two(t, t2) => (ConvResultIter::One(t2), Some(t)),
            ConvResultIter::Many(mut v) => {
                let next = v.next();
                (ConvResultIter::Many(v), next)
            }
        };

        mem::replace(self, new);

        result
    }
}

struct AstConv;

impl AstConv {
    fn conv_func<'a>(&mut self, func: parsetree::Function<'a>) -> Function<'a> {
        Function {
            params: func.params,
            varargs: func.varargs,
            body: self.conv_block(func.body),
            locals: Default::default(),
            upvalues: Default::default(),
        }
    }

    fn conv_block<'a>(&mut self, block: parsetree::Block<'a>) -> Block<'a> {
        Block {
            span: block.span,
            stmts: block.stmts.into_iter().flat_map(|s| self.conv_stmt(s)).collect(),
            localmap: Default::default(),
        }
    }

    fn conv_stmt<'a>(&mut self, stmt: parsetree::Stmt<'a>) -> ConvResult<Stmt<'a>> {
        match stmt.value {
            parsetree::SDecl(names, exprs) => {
                ConvResult::One(_Stmt::SDecl(
                    names,
                    exprs.into_iter().map(|e| self.conv_expr(e)).collect()
                ))
            }
            parsetree::SAssign(vars, exprs) => {
                ConvResult::One(_Stmt::SAssign(
                    vars.into_iter().map(|var| self.conv_var(var)).collect(),
                    exprs.into_iter().map(|e| self.conv_expr(e)).collect()
                ))
            }
            parsetree::SDo(block) => {
                ConvResult::One(_Stmt::SDo(self.conv_block(block)))
            }
            parsetree::SBreak => {
                ConvResult::One(_Stmt::SBreak)
            }
            parsetree::SSemi => {
                ConvResult::Zero
            }
            parsetree::SReturn(exprs) => {
                ConvResult::One(_Stmt::SReturn(
                    exprs.into_iter().map(|e| self.conv_expr(e)).collect()
                ))
            }
            parsetree::SCall(call) => {
                ConvResult::One(_Stmt::SCall(match call {
                    parsetree::SimpleCall(callee, args) => {
                        SimpleCall(Box::new(self.conv_expr(*callee)), self.conv_args(args))
                    }
                    parsetree::MethodCall(obj, name, args) => {
                        MethodCall(Box::new(self.conv_expr(*obj)), name, self.conv_args(args))
                    }
                }))
            }
            parsetree::SFunc(var, func) => {
                ConvResult::One(_Stmt::SAssign(
                    vec![self.conv_var(var)],
                    vec![Spanned::new(func.body.span, EFunc(self.conv_func(func)))]
                ))
            }
            parsetree::SMethod(var, name, func) => {
                // var:name(args...)   =>   var.name = function(self, args...)
                let mut func: Function<'a> = self.conv_func(func);
                func.params.insert(0, Spanned::new(name.span, "self"));

                ConvResult::One(_Stmt::SAssign(
                    vec![Spanned::new(var.span, VIndex(
                        Box::new(self.conv_var(var)),
                        Box::new(Spanned::new(name.span, ELit(Const::Str(name.value.to_owned()))))
                    ))],
                    vec![Spanned::new(func.body.span, EFunc(func))]
                ))
            }
            parsetree::SLFunc(local, func) => {
                ConvResult::Two(
                    _Stmt::SDecl(vec![local], vec![]),
                    _Stmt::SAssign(
                        vec![Spanned::new(local.span, VNamed(local.value))],
                        vec![Spanned::new(func.body.span, EFunc(self.conv_func(func)))]
                    )
                )
            }
            parsetree::SIf { cond, body, elifs, el } => {
                // Build else block for this if statement
                // Start with "pure" else at the end
                let mut myelse: Option<Block> = el.map(|el| self.conv_block(el));

                for elif in elifs {
                    let (cond, body) = elif.value;

                    myelse = Some(Block {
                        localmap: Default::default(),
                        span: elif.span,
                        stmts: vec![
                            Spanned::new(elif.span, SIf {
                                cond: self.conv_expr(cond),
                                body: self.conv_block(body),
                                el: myelse, // Old else block here
                            }),
                        ],
                    });
                }

                ConvResult::One(_Stmt::SIf {
                    cond: self.conv_expr(cond),
                    body: self.conv_block(body),
                    el: myelse,
                })
            }
            parsetree::SWhile { cond, body } => {
                ConvResult::One(_Stmt::SWhile {
                    cond: self.conv_expr(cond),
                    body: self.conv_block(body),
                })
            }
            parsetree::SRepeat { abort_on, body } => {
                ConvResult::One(_Stmt::SRepeat {
                    abort_on: self.conv_expr(abort_on),
                    body: self.conv_block(body),
                })
            }
            parsetree::SFor { var, start, step, end, body } => {
                ConvResult::One(_Stmt::SFor {
                    var: var,
                    start: self.conv_expr(start),
                    step: step.map(|e| self.conv_expr(e)),
                    end: self.conv_expr(end),
                    body: self.conv_block(body),
                })
            }
            parsetree::SForIn { vars, iter, body } => {
                ConvResult::One(_Stmt::SForIn {
                    vars: vars,
                    iter: iter.into_iter().map(|e| self.conv_expr(e)).collect(),
                    body: self.conv_block(body),
                })
            }
        }.spanned(stmt.span)
    }

    fn conv_var<'a>(&mut self, var: parsetree::Variable<'a>) -> Variable<'a> {
        Spanned::new(var.span, match var.value {
            parsetree::VNamed(name) => {
                VNamed(name)
            }
            parsetree::VIndex(var, index) => {
                VIndex(Box::new(self.conv_var(*var)), match index {
                    parsetree::VarIndex::DotIndex(name) => {
                        Box::new(Spanned::new(name.span,
                            ELit(Const::Str(name.value.to_owned()))))
                    }
                    parsetree::VarIndex::ExprIndex(expr) => {
                        Box::new(self.conv_expr(*expr))
                    }
                })
            }
        })
    }

    fn conv_expr<'a>(&mut self, expr: parsetree::Expr<'a>) -> Expr<'a> {
        Spanned::new(expr.span, match expr.value {
            parsetree::ELit(lit) => ELit(lit),
            parsetree::EBinOp(lhs, op, rhs) =>
                EBinOp(Box::new(self.conv_expr(*lhs)), op, Box::new(self.conv_expr(*rhs))),
            parsetree::EUnOp(op, expr) => EUnOp(op, Box::new(self.conv_expr(*expr))),
            parsetree::EBraced(expr) => self.conv_expr(*expr).value,
            parsetree::EVar(var) => EVar(self.conv_var(var)),
            parsetree::ECall(call) => ECall(match call {
                parsetree::SimpleCall(callee, args) => {
                    SimpleCall(Box::new(self.conv_expr(*callee)), self.conv_args(args))
                }
                parsetree::MethodCall(obj, name, args) => {
                    MethodCall(Box::new(self.conv_expr(*obj)), name, self.conv_args(args))
                }
            }),
            parsetree::ETable(cons) => {
                ETable(self.conv_table(cons))
            }
            parsetree::EFunc(func) => EFunc(self.conv_func(func)),
            parsetree::EArray(elems) =>
                EArray(elems.into_iter().map(|e| self.conv_expr(e)).collect()),
            parsetree::EVarArgs => EVarArgs,
        })
    }

    fn conv_table<'a>(&mut self, cons: parsetree::TableCons<'a>) -> Vec<(Expr<'a>, Expr<'a>)> {
        let mut entries: Vec<(Expr, Expr)> = Vec::new();
        let mut i = 0;  // XXX 1?
        for entry in cons {
            entries.push(match entry {
                parsetree::TableEntry::IdentPair(key, value) => {
                    (Spanned::new(key.span, ELit(Const::Str(key.value.into()))), self.conv_expr(value))
                }
                parsetree::TableEntry::Pair(key, value) => {
                    (self.conv_expr(key), self.conv_expr(value))
                }
                parsetree::TableEntry::Elem(value) => {
                    let idx = i;
                    i += 1;
                    (Spanned::new(value.span, ELit(Const::Int(idx))), self.conv_expr(value))
                }
            });
        }

        entries
    }

    fn conv_args<'a>(&mut self, args: parsetree::CallArgs<'a>) -> Vec<Expr<'a>> {
        match args {
            parsetree::CallArgs::Normal(exprs) => {
                exprs.into_iter().map(|e| self.conv_expr(e)).collect()
            }
            parsetree::CallArgs::String(s) => {
                // TODO use real span
                vec![Spanned::default(ELit(Const::Str(s)))]
            }
            parsetree::CallArgs::Table(cons) => {
                vec![Spanned::default(ETable(self.conv_table(cons)))]
            }
        }
    }
}

impl<'a> From<parsetree::Function<'a>> for Function<'a> {
    fn from(func: parsetree::Function<'a>) -> Self {
        let mut conv = AstConv;

        conv.conv_func(func)
    }
}
<|endoftext|>


<commit_before>//! Error handling.

use std::fmt::Display;
use std::io;
use std::result;

/// Library errors.
#[derive(Debug)]
pub enum Error {
    /// I/O error.
    IOError(io::Error),
    /// LZMA error.
    LZMAError(String),
    /// XZ error.
    XZError(String),
}

/// Library result alias.
pub type Result<T> = result::Result<T, Error>;

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Error {
        Error::IOError(e)
    }
}

impl Display for Error {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::IOError(e) => write!(fmt, "io error: {}", e),
            Error::LZMAError(e) => write!(fmt, "lzma error: {}", e),
            Error::XZError(e) => write!(fmt, "xz error: {}", e),
        }
    }
}

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

    #[test]
    fn test_display() {
        assert_eq!(
            Error::IOError(std::io::Error::new(
                std::io::ErrorKind::Other,
                "this is an error"
            ))
            .to_string(),
            "io error: this is an error"
        );
        assert_eq!(
            Error::LZMAError("this is an error".to_string()).to_string(),
            "lzma error: this is an error"
        );
        assert_eq!(
            Error::XZError("this is an error".to_string()).to_string(),
            "xz error: this is an error"
        );
    }
}
<commit_msg>error: implement std::error::Error<commit_after>//! Error handling.

use std::fmt::Display;
use std::io;
use std::result;

/// Library errors.
#[derive(Debug)]
pub enum Error {
    /// I/O error.
    IOError(io::Error),
    /// LZMA error.
    LZMAError(String),
    /// XZ error.
    XZError(String),
}

/// Library result alias.
pub type Result<T> = result::Result<T, Error>;

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Error {
        Error::IOError(e)
    }
}

impl Display for Error {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::IOError(e) => write!(fmt, "io error: {}", e),
            Error::LZMAError(e) => write!(fmt, "lzma error: {}", e),
            Error::XZError(e) => write!(fmt, "xz error: {}", e),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::IOError(e) => Some(e),
            Error::LZMAError(_) | Error::XZError(_) => None,
        }
    }
}

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

    #[test]
    fn test_display() {
        assert_eq!(
            Error::IOError(std::io::Error::new(
                std::io::ErrorKind::Other,
                "this is an error"
            ))
            .to_string(),
            "io error: this is an error"
        );
        assert_eq!(
            Error::LZMAError("this is an error".to_string()).to_string(),
            "lzma error: this is an error"
        );
        assert_eq!(
            Error::XZError("this is an error".to_string()).to_string(),
            "xz error: this is an error"
        );
    }
}
<|endoftext|>


<commit_before>use std::ffi::CString;

pub struct DlDescription(pub(crate) CString);

impl std::fmt::Debug for DlDescription {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::fmt::Debug::fmt(&self.0, f)
    }
}

pub struct WindowsError(pub(crate) std::io::Error);

impl std::fmt::Debug for WindowsError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::fmt::Debug::fmt(&self.0, f)
    }
}

#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
    /// The `dlopen` call failed.
    DlOpen { desc: DlDescription },
    /// The `dlopen` call failed and system did not report an error.
    DlOpenUnknown,
    /// The `dlsym` call failed.
    DlSym { desc: DlDescription },
    /// The `dlsym` call failed and system did not report an error.
    DlSymUnknown,
    /// The `dlclose` call failed.
    DlClose { desc: DlDescription },
    /// The `dlclose` call failed and system did not report an error.
    DlCloseUnknown,
    /// The `LoadLibraryW` call failed.
    LoadLibraryExW { source: WindowsError },
    /// The `LoadLibraryW` call failed and system did not report an error.
    LoadLibraryExWUnknown,
    /// The `GetProcAddress` call failed.
    GetProcAddress { source: WindowsError },
    /// The `GetProcAddressUnknown` call failed and system did not report an error.
    GetProcAddressUnknown,
    /// The `FreeLibrary` call failed.
    FreeLibrary { source: WindowsError },
    /// The `FreeLibrary` call failed and system did not report an error.
    FreeLibraryUnknown,
    /// The requested type cannot possibly work.
    IncompatibleSize,
    /// Could not create a new CString.
    CreateCString { source: std::ffi::NulError },
    /// Could not create a new CString from bytes with trailing null.
    CreateCStringWithTrailing { source: std::ffi::FromBytesWithNulError },
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;
        match *self {
            CreateCString { ref source } => Some(source),
            CreateCStringWithTrailing { ref source } => Some(source),
            LoadLibraryExW { ref source } => Some(&source.0),
            GetProcAddress { ref source } => Some(&source.0),
            FreeLibrary { ref source } => Some(&source.0),
            _ => None,
        }
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;
        match *self {
            DlOpen { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
            DlOpenUnknown => write!(f, "dlopen failed, but system did not report the error"),
            DlSym { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
            DlSymUnknown => write!(f, "dlsym failed, but system did not report the error"),
            DlClose { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
            DlCloseUnknown => write!(f, "dlclose failed, but system did not report the error"),
            LoadLibraryExW { .. } => write!(f, "LoadLibraryW failed"),
            LoadLibraryExWUnknown =>
                write!(f, "LoadLibraryW failed, but system did not report the error"),
            GetProcAddress { .. } => write!(f, "GetProcAddress failed"),
            GetProcAddressUnknown =>
                write!(f, "GetProcAddress failed, but system did not report the error"),
            FreeLibrary { .. } => write!(f, "FreeLibrary failed"),
            FreeLibraryUnknown =>
                write!(f, "FreeLibrary failed, but system did not report the error"),
            CreateCString { .. } => write!(f, "could not create a C string from bytes"),
            CreateCStringWithTrailing { .. } =>
                write!(f, "could not create a C string from bytes with trailing null"),
            IncompatibleSize => write!(f, "requested type cannot possibly work"),
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn error_send() {
        fn assert_send<T: Send>() {}
        assert_send::<super::Error>();
    }

    #[test]
    fn error_sync() {
        fn assert_sync<T: Sync>() {}
        assert_sync::<super::Error>();
    }

    #[test]
    fn error_display() {
        fn assert_display<T: std::fmt::Display>() {}
        assert_display::<super::Error>();
    }
}
<commit_msg>Change the comment as well into LoadLibraryExW<commit_after>use std::ffi::CString;

pub struct DlDescription(pub(crate) CString);

impl std::fmt::Debug for DlDescription {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::fmt::Debug::fmt(&self.0, f)
    }
}

pub struct WindowsError(pub(crate) std::io::Error);

impl std::fmt::Debug for WindowsError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::fmt::Debug::fmt(&self.0, f)
    }
}

#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
    /// The `dlopen` call failed.
    DlOpen { desc: DlDescription },
    /// The `dlopen` call failed and system did not report an error.
    DlOpenUnknown,
    /// The `dlsym` call failed.
    DlSym { desc: DlDescription },
    /// The `dlsym` call failed and system did not report an error.
    DlSymUnknown,
    /// The `dlclose` call failed.
    DlClose { desc: DlDescription },
    /// The `dlclose` call failed and system did not report an error.
    DlCloseUnknown,
    /// The `LoadLibraryExW` call failed.
    LoadLibraryExW { source: WindowsError },
    /// The `LoadLibraryExW` call failed and system did not report an error.
    LoadLibraryExWUnknown,
    /// The `GetProcAddress` call failed.
    GetProcAddress { source: WindowsError },
    /// The `GetProcAddressUnknown` call failed and system did not report an error.
    GetProcAddressUnknown,
    /// The `FreeLibrary` call failed.
    FreeLibrary { source: WindowsError },
    /// The `FreeLibrary` call failed and system did not report an error.
    FreeLibraryUnknown,
    /// The requested type cannot possibly work.
    IncompatibleSize,
    /// Could not create a new CString.
    CreateCString { source: std::ffi::NulError },
    /// Could not create a new CString from bytes with trailing null.
    CreateCStringWithTrailing { source: std::ffi::FromBytesWithNulError },
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;
        match *self {
            CreateCString { ref source } => Some(source),
            CreateCStringWithTrailing { ref source } => Some(source),
            LoadLibraryExW { ref source } => Some(&source.0),
            GetProcAddress { ref source } => Some(&source.0),
            FreeLibrary { ref source } => Some(&source.0),
            _ => None,
        }
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;
        match *self {
            DlOpen { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
            DlOpenUnknown => write!(f, "dlopen failed, but system did not report the error"),
            DlSym { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
            DlSymUnknown => write!(f, "dlsym failed, but system did not report the error"),
            DlClose { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
            DlCloseUnknown => write!(f, "dlclose failed, but system did not report the error"),
            LoadLibraryExW { .. } => write!(f, "LoadLibraryW failed"),
            LoadLibraryExWUnknown =>
                write!(f, "LoadLibraryW failed, but system did not report the error"),
            GetProcAddress { .. } => write!(f, "GetProcAddress failed"),
            GetProcAddressUnknown =>
                write!(f, "GetProcAddress failed, but system did not report the error"),
            FreeLibrary { .. } => write!(f, "FreeLibrary failed"),
            FreeLibraryUnknown =>
                write!(f, "FreeLibrary failed, but system did not report the error"),
            CreateCString { .. } => write!(f, "could not create a C string from bytes"),
            CreateCStringWithTrailing { .. } =>
                write!(f, "could not create a C string from bytes with trailing null"),
            IncompatibleSize => write!(f, "requested type cannot possibly work"),
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn error_send() {
        fn assert_send<T: Send>() {}
        assert_send::<super::Error>();
    }

    #[test]
    fn error_sync() {
        fn assert_sync<T: Sync>() {}
        assert_sync::<super::Error>();
    }

    #[test]
    fn error_display() {
        fn assert_display<T: std::fmt::Display>() {}
        assert_display::<super::Error>();
    }
}
<|endoftext|>