<commit_before>#![allow(unused)]

use interpreter::ast::*;
use interpreter::tokenizer::*;

use std::collections::HashMap;

pub type Memory = HashMap<Ident, Expr>;

impl Expr {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        match self.node {
            Node::Ident(ref id) => {
                memory
                    .get(id)
                    .ok_or_else(|| {
                        self.debug.to_string() + ", but " + &id.0 + " has not been defined yet."
                    })
                    .map(|val| val.clone())
            }
            Node::Op(ref operator) => operator.run(memory),
            Node::Function(ref function) => function.run(memory, Vec::new()),
            _ => Ok(self.clone()),
        }
    }
}

impl Operator {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        match *self {
            Operator::Assign(ref id, ref expr) => {
                let val = expr.run(memory)?;
                memory.insert(id.clone(), val);
                Ok(Expr::new(Node::Noop, expr.debug.clone()))
            }

            // Most of the following are the opposite of DRY. I should
            // figure out a way to reduce the needless duplication.
            Operator::Add(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Int(val1 + val2), DebugInfo::none()))
                    }
                    (Node::Float(val1), Node::Float(val2)) => {
                        Ok(Expr::new(Node::Float(val1 + val2), DebugInfo::none()))
                    }
                    _ => unimplemented!(),
                }
            }
            Operator::Sub(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Int(val1 - val2), DebugInfo::none()))
                    }
                    (Node::Float(val1), Node::Float(val2)) => {
                        Ok(Expr::new(Node::Float(val1 - val2), DebugInfo::none()))
                    }
                    _ => unimplemented!(),
                }
            }
            Operator::Mul(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Int(val1 * val2), DebugInfo::none()))
                    }
                    (Node::Float(val1), Node::Float(val2)) => {
                        Ok(Expr::new(Node::Float(val1 * val2), DebugInfo::none()))
                    }
                    _ => unimplemented!(),
                }
            }
            Operator::Div(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Int(val1 / val2), DebugInfo::none()))
                    }
                    (Node::Float(val1), Node::Float(val2)) => {
                        Ok(Expr::new(Node::Float(val1 / val2), DebugInfo::none()))
                    }
                    _ => unimplemented!(),
                }
            }
            Operator::Mod(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Int(val1 % val2), DebugInfo::none()))
                    }
                    _ => unimplemented!(),
                }
            }

            Operator::Negate(ref expr) => {
                let expr_res = expr.run(memory)?;
                match expr_res.node {
                    Node::Int(val) => Ok(Expr::new(Node::Int(-val), DebugInfo::none())),
                    Node::Float(val) => Ok(Expr::new(Node::Float(-val), DebugInfo::none())),
                    _ => {
                        Err(
                            expr.debug.to_string() +
                                ", attempt to negate something which is not a number",
                        )?
                    }
                }
            }

            Operator::Not(ref expr) => {
                let expr_res = expr.run(memory)?;
                match expr_res.node {
                    Node::Bool(val) => Ok(Expr::new(Node::Bool(!val), DebugInfo::none())),
                    _ => {
                        Err(
                            expr.debug.to_string() +
                                ", attempt to use logical NOT on something which is not a boolean",
                        )?
                    }
                }
            }
            Operator::And(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Bool(val1), Node::Bool(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 && val2), DebugInfo::none()))
                    }
                    _ => {
                        Err(
                            left.debug.to_string() + &right.debug.to_string() +
                                ", attempt to use logical AND on something which is not a boolean",
                        )?
                    }
                }
            }
            Operator::Or(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Bool(val1), Node::Bool(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 || val2), DebugInfo::none()))
                    }
                    _ => {
                        Err(
                            left.debug.to_string() + &right.debug.to_string() +
                                ", attempt to use logical AND on something which is not a boolean",
                        )?
                    }
                }
            }
            Operator::Equals(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Bool(val1), Node::Bool(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 == val2), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 == val2), DebugInfo::none()))
                    }
                    (Node::Str(val1), Node::Str(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 == val2), DebugInfo::none()))
                    }
                    (Node::Function(val1), Node::Function(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 == val2), DebugInfo::none()))
                    }
                    (Node::Float(_), _) => {
                        Err(
                            left.debug.to_string() +
                                ", floating point equality testing is not supported",
                        )?
                    }
                    (_, Node::Float(_)) => {
                        Err(
                            right.debug.to_string() +
                                ", floating point equality testing is not supported",
                        )?
                    }
                    _ => {
                        Err(
                            left.debug.to_string() + &right.debug.to_string() +
                                ", attempt to compare two values for equality which cannot be compared",
                        )?
                    }
                }
            }
            Operator::NotEquals(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Bool(val1), Node::Bool(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 != val2), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 != val2), DebugInfo::none()))
                    }
                    (Node::Str(val1), Node::Str(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 != val2), DebugInfo::none()))
                    }
                    (Node::Function(val1), Node::Function(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 != val2), DebugInfo::none()))
                    }
                    (Node::Float(_), _) => {
                        Err(
                            left.debug.to_string() +
                                ", floating point equality testing is not supported",
                        )?
                    }
                    (_, Node::Float(_)) => {
                        Err(
                            right.debug.to_string() +
                                ", floating point equality testing is not supported",
                        )?
                    }
                    _ => {
                        Err(
                            left.debug.to_string() + &right.debug.to_string() +
                                ", attempt to compare two values for equality which cannot be compared",
                        )?
                    }
                }
            }
            Operator::Less(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Bool(val1), Node::Bool(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 < val2), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 < val2), DebugInfo::none()))
                    }
                    (Node::Str(val1), Node::Str(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 < val2), DebugInfo::none()))
                    }
                    (Node::Function(val1), Node::Function(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 < val2), DebugInfo::none()))
                    }
                    (Node::Float(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 < (val2 as f64)), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Float(val2)) => {
                        Ok(Expr::new(Node::Bool((val1 as f64) < val2), DebugInfo::none()))
                    }
                    _ => {
                        Err(
                            left.debug.to_string() + &right.debug.to_string() +
                                ", attempt to compare two values for equality which cannot be compared",
                        )?
                    }
                }
            }
            Operator::LessOrEquals(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Bool(val1), Node::Bool(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 <= val2), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 <= val2), DebugInfo::none()))
                    }
                    (Node::Str(val1), Node::Str(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 <= val2), DebugInfo::none()))
                    }
                    (Node::Function(val1), Node::Function(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 <= val2), DebugInfo::none()))
                    }
                    (Node::Float(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 <= (val2 as f64)), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Float(val2)) => {
                        Ok(Expr::new(Node::Bool((val1 as f64) <= val2), DebugInfo::none()))
                    }
                    _ => {
                        Err(
                            left.debug.to_string() + &right.debug.to_string() +
                                ", attempt to compare two values for equality which cannot be compared",
                        )?
                    }
                }
            }
            Operator::Greater(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Bool(val1), Node::Bool(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 > val2), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 > val2), DebugInfo::none()))
                    }
                    (Node::Str(val1), Node::Str(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 > val2), DebugInfo::none()))
                    }
                    (Node::Function(val1), Node::Function(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 > val2), DebugInfo::none()))
                    }
                    (Node::Float(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 > (val2 as f64)), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Float(val2)) => {
                        Ok(Expr::new(Node::Bool((val1 as f64) > val2), DebugInfo::none()))
                    }
                    _ => {
                        Err(
                            left.debug.to_string() + &right.debug.to_string() +
                                ", attempt to compare two values for equality which cannot be compared",
                        )?
                    }
                }
            }
            Operator::GreaterOrEquals(ref left, ref right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Bool(val1), Node::Bool(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 >= val2), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 >= val2), DebugInfo::none()))
                    }
                    (Node::Str(val1), Node::Str(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 >= val2), DebugInfo::none()))
                    }
                    (Node::Function(val1), Node::Function(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 >= val2), DebugInfo::none()))
                    }
                    (Node::Float(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Bool(val1 >= (val2 as f64)), DebugInfo::none()))
                    }
                    (Node::Int(val1), Node::Float(val2)) => {
                        Ok(Expr::new(Node::Bool((val1 as f64) >= val2), DebugInfo::none()))
                    }
                    _ => {
                        Err(
                            left.debug.to_string() + &right.debug.to_string() +
                                ", attempt to compare two values for equality which cannot be compared",
                        )?
                    }
                }
            }
            _ => unimplemented!(),
        }
    }
}

/// get the list of visible identifiers at this scope level
fn get_scope(memory: &mut Memory) -> Vec<Ident> {
    memory.keys().cloned().collect()
}

/// remove any identifiers that were created during the scoped region
fn revert_scope(scope: Vec<Ident>, memory: &mut Memory) {
    memory.retain(|ident, _| scope.contains(ident));
}

fn revert_scope_after<T>(val: T, scope: Vec<Ident>, memory: &mut Memory) -> T {
    revert_scope(scope, memory);
    val
}

impl Function {
    fn run(&self, memory: &mut Memory, args: Vec<Expr>) -> Result<Expr, String> {
        if self.args.len() != args.len() {
            return Err(format!(
                "Function {} called with incorrect number of arguments. Expected {} but found {}.",
                self.name,
                self.args.len(),
                args.len()
            ))?;
        }
        let scope = get_scope(memory);
        for (id, val) in self.args.iter().zip(args) {
            memory.insert(id.clone(), val);
        }
        for mut expr in &self.body {
            match expr.node {
                Node::Return(_) => return revert_scope_after(expr.run(memory), scope, memory),
                _ => expr.run(memory)?,
            };
        }

        revert_scope(scope, memory);

        Ok(Expr::new(Node::Noop, DebugInfo::none()))
    }
}

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

    fn create_global(name: &str, memory: &mut Memory) {
        memory.insert(
            Ident(name.to_string()),
            Expr::new(Node::Noop, DebugInfo::none()),
        );
    }

    #[test]
    fn walk_assign() {
        let expr = Expr::parse("$someInt = 35").unwrap();
        let mut memory = &mut HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt", memory);

        let result = expr.run(memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    fn walk_assign2() {
        let expr = Expr::parse("$otherInt = 35; $someInt = $otherInt + 2;").unwrap();
        let mut memory = &mut HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt", memory);

        let result = expr.run(memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    #[should_panic]
    fn walk_assign_undefined() {
        let expr = Expr::parse("$someInt = 35; $otherInt = $someInt + $whatIsThis;").unwrap();
        let mut memory = &mut HashMap::new();
        let result = expr.run(memory).unwrap();
    }
}
<commit_msg>Rush, now 1 billion percent DRYer.<commit_after>#![allow(unused)]

use interpreter::ast::*;
use interpreter::tokenizer::*;

use std::collections::HashMap;

pub type Memory = HashMap<Ident, Expr>;

impl Expr {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        match self.node {
            Node::Ident(ref id) => {
                memory
                    .get(id)
                    .ok_or_else(|| {
                        self.debug.to_string() + ", but " + &id.0 + " has not been defined yet."
                    })
                    .map(|val| val.clone())
            }
            Node::Op(ref operator) => operator.run(memory),
            Node::Function(ref function) => function.run(memory, Vec::new()),
            _ => Ok(self.clone()),
        }
    }
}

/// avoids needlessly cloning an Expr if it is already resolved to
/// a primitive value. unfortunately, this requires passing in an
/// external storage variable for this case where the value has not
/// yet been resolved to a primitive, for lifetime reasons.
macro_rules! resolve {
    ($exp:ident, $memory:ident, $store:ident) => (
        match $exp.node {
            Node::Int(_) => $exp,
            Node::Float(_) => $exp,
            Node::Str(_) => $exp,
            Node::Bool(_) => $exp,
            _ => {$store = $exp.run($memory)?; &$store}
        }
    )
}

macro_rules! do_binary_op {
    ($op:tt, $memory:ident, $left:ident, $right:ident, $(($typeA:tt, $typeB:tt -> ($asType:tt:$asTypePrim:tt))),*; $(($typeA2:tt, $typeB2:tt -> ($asType2:tt))),*; _ => $defaultCase:expr) => ({
        let store1;
        let store2;
        let left_res = resolve!($left, $memory, store1);
        let right_res = resolve!($right, $memory, store2);
        match (&left_res.node, &right_res.node) {
            $(
                (&Node::$typeA(val1), &Node::$typeB(val2)) => {
                    Ok(Expr::new(Node::$asType((val1 as $asTypePrim) $op (val2 as $asTypePrim)), DebugInfo::none()))
                },
            )*
            $(
                (&Node::$typeA2(ref val1), &Node::$typeB2(ref val2)) => {
                    Ok(Expr::new(Node::$asType2(val1 $op val2), DebugInfo::none()))
                },
            )*
            _ => $defaultCase,
        }
    })
}

impl Operator {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        match *self {
            Operator::Assign(ref id, ref expr) => {
                let val = expr.run(memory)?;
                memory.insert(id.clone(), val);
                Ok(Expr::new(Node::Noop, expr.debug.clone()))
            }

            Operator::Add(ref left, ref right) => {
                do_binary_op!(+, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Sub(ref left, ref right) => {
                do_binary_op!(-, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Mul(ref left, ref right) => {
                do_binary_op!(*, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Div(ref left, ref right) => {
                do_binary_op!(/, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Mod(ref left, ref right) => {
                do_binary_op!(%, memory, left, right, (Int, Int -> (Int:i64));; _ => unimplemented!())
            }
            Operator::And(ref left, ref right) => {
                do_binary_op!(&&, memory, left, right, (Bool, Bool -> (Bool:bool));; _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to use logical AND on something which is not a boolean")?)
            }
            Operator::Or(ref left, ref right) => {
                do_binary_op!(||, memory, left, right, (Bool, Bool -> (Bool:bool));; _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to use logical OR on something which is not a boolean")?)
            }
            Operator::Less(ref left, ref right) => {
                do_binary_op!(<, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::LessOrEquals(ref left, ref right) => {
                do_binary_op!(<=, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::Greater(ref left, ref right) => {
                do_binary_op!(>, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::GreaterOrEquals(ref left, ref right) => {
                do_binary_op!(>=, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::Equals(ref left, ref right) => {
                do_binary_op!(==, memory, left, right,; (Int, Int -> (Bool)), (Float, Float -> (Bool)), (Str, Str -> (Bool)), (Bool, Bool -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::NotEquals(ref left, ref right) => {
                do_binary_op!(!=, memory, left, right,; (Int, Int -> (Bool)), (Float, Float -> (Bool)), (Str, Str -> (Bool)), (Bool, Bool -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }

            Operator::Negate(ref expr) => {
                let store;
                let expr_res = resolve!(expr, memory, store);
                match &expr_res.node {
                    &Node::Int(val) => Ok(Expr::new(Node::Int(-val), DebugInfo::none())),
                    &Node::Float(val) => Ok(Expr::new(Node::Float(-val), DebugInfo::none())),
                    _ => {
                        Err(
                            expr.debug.to_string() +
                                ", attempt to negate something which is not a number",
                        )?
                    }
                }
            }

            Operator::Not(ref expr) => {
                let store;
                let expr_res = resolve!(expr, memory, store);
                match &expr_res.node {
                    &Node::Bool(val) => Ok(Expr::new(Node::Bool(!val), DebugInfo::none())),
                    _ => {
                        Err(
                            expr.debug.to_string() +
                                ", attempt to use logical NOT on something which is not a boolean",
                        )?
                    }
                }
            }
            _ => unimplemented!(),
        }
    }
}

/// get the list of visible identifiers at this scope level
fn get_scope(memory: &mut Memory) -> Vec<Ident> {
    memory.keys().cloned().collect()
}

/// remove any identifiers that were created during the scoped region
fn revert_scope(scope: Vec<Ident>, memory: &mut Memory) {
    memory.retain(|ident, _| scope.contains(ident));
}

fn revert_scope_after<T>(val: T, scope: Vec<Ident>, memory: &mut Memory) -> T {
    revert_scope(scope, memory);
    val
}

impl Function {
    fn run(&self, memory: &mut Memory, args: Vec<Expr>) -> Result<Expr, String> {
        if self.args.len() != args.len() {
            return Err(format!(
                "Function {} called with incorrect number of arguments. Expected {} but found {}.",
                self.name,
                self.args.len(),
                args.len()
            ))?;
        }
        let scope = get_scope(memory);
        for (id, val) in self.args.iter().zip(args) {
            memory.insert(id.clone(), val);
        }
        for mut expr in &self.body {
            match expr.node {
                Node::Return(_) => return revert_scope_after(expr.run(memory), scope, memory),
                _ => expr.run(memory)?,
            };
        }

        revert_scope(scope, memory);

        Ok(Expr::new(Node::Noop, DebugInfo::none()))
    }
}

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

    fn create_global(name: &str, memory: &mut Memory) {
        memory.insert(
            Ident(name.to_string()),
            Expr::new(Node::Noop, DebugInfo::none()),
        );
    }

    #[test]
    fn walk_assign() {
        let expr = Expr::parse("$someInt = 35").unwrap();
        let mut memory = &mut HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt", memory);

        let result = expr.run(memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    fn walk_assign2() {
        let expr = Expr::parse("$otherInt = 35; $someInt = $otherInt + 2;").unwrap();
        let mut memory = &mut HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt", memory);

        let result = expr.run(memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    #[should_panic]
    fn walk_assign_undefined() {
        let expr = Expr::parse("$someInt = 35; $otherInt = $someInt + $whatIsThis;").unwrap();
        let mut memory = &mut HashMap::new();
        let result = expr.run(memory).unwrap();
    }
}
<|endoftext|>


<commit_before>//! This crates defines the trait resolution method and the type inference engine.
//!
//! - **Traits.** Trait resolution is implemented in the `traits` module.
//! - **Type inference.** The type inference code can be found in the `infer` module;
//!   this code handles low-level equality and subtyping operations. The
//!   type check pass in the compiler is found in the `librustc_typeck` crate.
//!
//! For more information about how rustc works, see the [rustc guide].
//!
//! [rustc guide]: https://rust-lang.github.io/rustc-guide/
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(arbitrary_self_types)]
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(const_fn)]
#![feature(const_transmute)]
#![feature(core_intrinsics)]
#![feature(drain_filter)]
#![cfg_attr(windows, feature(libc))]
#![feature(never_type)]
#![feature(exhaustive_patterns)]
#![feature(overlapping_marker_traits)]
#![feature(extern_types)]
#![feature(nll)]
#![feature(optin_builtin_traits)]
#![feature(option_expect_none)]
#![feature(range_is_empty)]
#![feature(specialization)]
#![feature(unboxed_closures)]
#![feature(thread_local)]
#![feature(trace_macros)]
#![feature(trusted_len)]
#![feature(stmt_expr_attributes)]
#![feature(integer_atomics)]
#![feature(test)]
#![feature(in_band_lifetimes)]
#![feature(crate_visibility_modifier)]
#![feature(log_syntax)]
#![feature(associated_type_bounds)]
#![feature(rustc_attrs)]
#![feature(hash_raw_entry)]
#![recursion_limit = "512"]

#[macro_use]
extern crate rustc_macros;
#[macro_use]
extern crate rustc_data_structures;
#[macro_use]
extern crate log;
#[macro_use]
extern crate rustc;

pub mod infer;
pub mod traits;
<commit_msg>Prune features.<commit_after>//! This crates defines the trait resolution method and the type inference engine.
//!
//! - **Traits.** Trait resolution is implemented in the `traits` module.
//! - **Type inference.** The type inference code can be found in the `infer` module;
//!   this code handles low-level equality and subtyping operations. The
//!   type check pass in the compiler is found in the `librustc_typeck` crate.
//!
//! For more information about how rustc works, see the [rustc guide].
//!
//! [rustc guide]: https://rust-lang.github.io/rustc-guide/
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(drain_filter)]
#![feature(never_type)]
#![feature(range_is_empty)]
#![feature(in_band_lifetimes)]
#![feature(crate_visibility_modifier)]
#![recursion_limit = "512"]

#[macro_use]
extern crate rustc_macros;
#[macro_use]
extern crate rustc_data_structures;
#[macro_use]
extern crate log;
#[macro_use]
extern crate rustc;

pub mod infer;
pub mod traits;
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use browser_host::{ServoCefBrowserHost, ServoCefBrowserHostExtensions};
use eutil::Downcast;
use frame::{ServoCefFrame, ServoCefFrameExtensions};
use interfaces::{CefBrowser, CefBrowserHost, CefClient, CefFrame, CefRequestContext};
use interfaces::{cef_browser_t, cef_browser_host_t, cef_client_t, cef_frame_t};
use interfaces::{cef_request_context_t};
use servo::Browser;
use types::{cef_browser_settings_t, cef_string_t, cef_window_info_t};
use window;

use compositing::windowing::{WindowNavigateMsg, WindowEvent};
use glutin_app;
use libc::c_int;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell, BorrowState};
use std::sync::atomic::{AtomicIsize, Ordering};

thread_local!(pub static ID_COUNTER: AtomicIsize = AtomicIsize::new(0));
thread_local!(pub static BROWSERS: RefCell<Vec<CefBrowser>> = RefCell::new(vec!()));

pub enum ServoBrowser {
    Invalid,
    OnScreen(Browser),
    OffScreen(Browser),
}

impl ServoBrowser {
    fn handle_event(&mut self, event: WindowEvent) {
        match *self {
            ServoBrowser::OnScreen(ref mut browser) => { browser.handle_event(event); }
            ServoBrowser::OffScreen(ref mut browser) => { browser.handle_event(event); }
            ServoBrowser::Invalid => {}
        }
    }

    pub fn get_title_for_main_frame(&self) {
        match *self {
            ServoBrowser::OnScreen(ref browser) => browser.get_title_for_main_frame(),
            ServoBrowser::OffScreen(ref browser) => browser.get_title_for_main_frame(),
            ServoBrowser::Invalid => {}
        }
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        match *self {
            ServoBrowser::OnScreen(ref browser) => browser.pinch_zoom_level(),
            ServoBrowser::OffScreen(ref browser) => browser.pinch_zoom_level(),
            ServoBrowser::Invalid => 1.0,
        }
    }
}

cef_class_impl! {
    ServoCefBrowser : CefBrowser, cef_browser_t {
        fn get_host(&this,) -> *mut cef_browser_host_t {{
            this.downcast().host.clone()
        }}

        fn go_back(&this,) -> () {{
            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Back));
        }}

        fn go_forward(&this,) -> () {{
            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Forward));
        }}

        // Returns the main (top-level) frame for the browser window.
        fn get_main_frame(&this,) -> *mut cef_frame_t {{
            this.downcast().frame.clone()
        }}
    }
}

pub struct ServoCefBrowser {
    /// A reference to the browser's primary frame.
    pub frame: CefFrame,
    /// A reference to the browser's host.
    pub host: CefBrowserHost,
    /// A reference to the browser client.
    pub client: CefClient,
    /// Whether the on-created callback has fired yet.
    pub callback_executed: Cell<bool>,

    id: isize,
    servo_browser: RefCell<ServoBrowser>,
    message_queue: RefCell<Vec<WindowEvent>>,
}

impl ServoCefBrowser {
    pub fn new(window_info: &cef_window_info_t, client: CefClient) -> ServoCefBrowser {
        let frame = ServoCefFrame::new().as_cef_interface();
        let host = ServoCefBrowserHost::new(client.clone()).as_cef_interface();

        let servo_browser = if window_info.windowless_rendering_enabled == 0 {
            let glutin_window = glutin_app::create_window();
            let servo_browser = Browser::new(Some(glutin_window.clone()));
            ServoBrowser::OnScreen(servo_browser)
        } else {
            ServoBrowser::Invalid
        };

        let id = ID_COUNTER.with(|counter| {
            counter.fetch_add(1, Ordering::SeqCst)
        });

        ServoCefBrowser {
            frame: frame,
            host: host,
            client: client,
            callback_executed: Cell::new(false),
            servo_browser: RefCell::new(servo_browser),
            message_queue: RefCell::new(vec!()),
            id: id,
        }
    }
}

pub trait ServoCefBrowserExtensions {
    fn init(&self, window_info: &cef_window_info_t);
    fn send_window_event(&self, event: WindowEvent);
    fn get_title_for_main_frame(&self);
    fn pinch_zoom_level(&self) -> f32;
}

impl ServoCefBrowserExtensions for CefBrowser {
    fn init(&self, window_info: &cef_window_info_t) {
        if window_info.windowless_rendering_enabled != 0 {
            let window = window::Window::new();
            let servo_browser = Browser::new(Some(window.clone()));
            window.set_browser(self.clone());
            *self.downcast().servo_browser.borrow_mut() = ServoBrowser::OffScreen(servo_browser);
        }

        self.downcast().host.set_browser((*self).clone());
        self.downcast().frame.set_browser((*self).clone());
        if window_info.parent_window != 0 {
            self.downcast().host.initialize_compositing();
        }
    }

    fn send_window_event(&self, event: WindowEvent) {
        self.downcast().message_queue.borrow_mut().push(event);

        loop {
            match self.downcast().servo_browser.borrow_state() {
                BorrowState::Unused => {
                    let event = match self.downcast().message_queue.borrow_mut().pop() {
                        None => return,
                        Some(event) => event,
                    };
                    self.downcast().servo_browser.borrow_mut().handle_event(event);
                }
                _ => {
                    // We're trying to send an event while processing another one. This will
                    // cause general badness, so queue up that event instead of immediately
                    // processing it.
                    break
                }
            }
        }
    }

    fn get_title_for_main_frame(&self) {
        self.downcast().servo_browser.borrow().get_title_for_main_frame()
    }

    fn pinch_zoom_level(&self) -> f32 {
        self.downcast().servo_browser.borrow().pinch_zoom_level()
    }
}

pub fn update() {
    BROWSERS.with(|browsers| {
        for browser in browsers.borrow().iter() {
            browser.send_window_event(WindowEvent::Idle);
        }
    });
}

pub fn close(browser: CefBrowser) {
    BROWSERS.with(|browsers| {
        let mut browsers = browsers.borrow_mut();
        browsers.iter()
                .position(|&ref n| n.downcast().id == browser.downcast().id)
                .map(|e| browsers.remove(e));
    });
}

pub fn browser_callback_after_created(browser: CefBrowser) {
    if browser.downcast().client.is_null_cef_object() {
        return
    }
    let client = browser.downcast().client.clone();
    let life_span_handler = client.get_life_span_handler();
    if life_span_handler.is_not_null_cef_object() {
        life_span_handler.on_after_created(browser.clone());
    }
    browser.downcast().callback_executed.set(true);
}

fn browser_host_create(window_info: &cef_window_info_t,
                       client: CefClient,
                       callback_executed: bool)
                       -> CefBrowser {
    let browser = ServoCefBrowser::new(window_info, client).as_cef_interface();
    browser.init(window_info);
    if callback_executed {
        browser_callback_after_created(browser.clone());
    }
    BROWSERS.with(|browsers| {
        browsers.borrow_mut().push(browser.clone());
    });
    browser
}

cef_static_method_impls! {
    fn cef_browser_host_create_browser(window_info: *const cef_window_info_t,
                                       client: *mut cef_client_t,
                                       _url: *const cef_string_t,
                                       _browser_settings: *const cef_browser_settings_t,
                                       _request_context: *mut cef_request_context_t)
                                       -> c_int {{
        let client: CefClient = client;
        let _url: &[u16] = _url;
        let _browser_settings: &cef_browser_settings_t = _browser_settings;
        let _request_context: CefRequestContext = _request_context;
        browser_host_create(window_info, client, false);
        1i32
    }}
    fn cef_browser_host_create_browser_sync(window_info: *const cef_window_info_t,
                                            client: *mut cef_client_t,
                                            _url: *const cef_string_t,
                                            _browser_settings: *const cef_browser_settings_t,
                                            _request_context: *mut cef_request_context_t)
                                            -> *mut cef_browser_t {{
        let client: CefClient = client;
        let _url: &[u16] = _url;
        let _browser_settings: &cef_browser_settings_t = _browser_settings;
        let _request_context: CefRequestContext = _request_context;
        browser_host_create(window_info, client, true)
    }}
}
<commit_msg>fix async browser on_created callback<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use browser_host::{ServoCefBrowserHost, ServoCefBrowserHostExtensions};
use eutil::Downcast;
use frame::{ServoCefFrame, ServoCefFrameExtensions};
use interfaces::{CefBrowser, CefBrowserHost, CefClient, CefFrame, CefRequestContext};
use interfaces::{cef_browser_t, cef_browser_host_t, cef_client_t, cef_frame_t};
use interfaces::{cef_request_context_t};
use servo::Browser;
use types::{cef_browser_settings_t, cef_string_t, cef_window_info_t};
use window;

use compositing::windowing::{WindowNavigateMsg, WindowEvent};
use glutin_app;
use libc::c_int;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell, BorrowState};
use std::sync::atomic::{AtomicIsize, Ordering};

thread_local!(pub static ID_COUNTER: AtomicIsize = AtomicIsize::new(0));
thread_local!(pub static BROWSERS: RefCell<Vec<CefBrowser>> = RefCell::new(vec!()));

pub enum ServoBrowser {
    Invalid,
    OnScreen(Browser),
    OffScreen(Browser),
}

impl ServoBrowser {
    fn handle_event(&mut self, event: WindowEvent) {
        match *self {
            ServoBrowser::OnScreen(ref mut browser) => { browser.handle_event(event); }
            ServoBrowser::OffScreen(ref mut browser) => { browser.handle_event(event); }
            ServoBrowser::Invalid => {}
        }
    }

    pub fn get_title_for_main_frame(&self) {
        match *self {
            ServoBrowser::OnScreen(ref browser) => browser.get_title_for_main_frame(),
            ServoBrowser::OffScreen(ref browser) => browser.get_title_for_main_frame(),
            ServoBrowser::Invalid => {}
        }
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        match *self {
            ServoBrowser::OnScreen(ref browser) => browser.pinch_zoom_level(),
            ServoBrowser::OffScreen(ref browser) => browser.pinch_zoom_level(),
            ServoBrowser::Invalid => 1.0,
        }
    }
}

cef_class_impl! {
    ServoCefBrowser : CefBrowser, cef_browser_t {
        fn get_host(&this,) -> *mut cef_browser_host_t {{
            this.downcast().host.clone()
        }}

        fn go_back(&this,) -> () {{
            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Back));
        }}

        fn go_forward(&this,) -> () {{
            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Forward));
        }}

        // Returns the main (top-level) frame for the browser window.
        fn get_main_frame(&this,) -> *mut cef_frame_t {{
            this.downcast().frame.clone()
        }}
    }
}

pub struct ServoCefBrowser {
    /// A reference to the browser's primary frame.
    pub frame: CefFrame,
    /// A reference to the browser's host.
    pub host: CefBrowserHost,
    /// A reference to the browser client.
    pub client: CefClient,
    /// Whether the on-created callback has fired yet.
    pub callback_executed: Cell<bool>,

    id: isize,
    servo_browser: RefCell<ServoBrowser>,
    message_queue: RefCell<Vec<WindowEvent>>,
}

impl ServoCefBrowser {
    pub fn new(window_info: &cef_window_info_t, client: CefClient) -> ServoCefBrowser {
        let frame = ServoCefFrame::new().as_cef_interface();
        let host = ServoCefBrowserHost::new(client.clone()).as_cef_interface();

        let servo_browser = if window_info.windowless_rendering_enabled == 0 {
            let glutin_window = glutin_app::create_window();
            let servo_browser = Browser::new(Some(glutin_window.clone()));
            ServoBrowser::OnScreen(servo_browser)
        } else {
            ServoBrowser::Invalid
        };

        let id = ID_COUNTER.with(|counter| {
            counter.fetch_add(1, Ordering::SeqCst)
        });

        ServoCefBrowser {
            frame: frame,
            host: host,
            client: client,
            callback_executed: Cell::new(false),
            servo_browser: RefCell::new(servo_browser),
            message_queue: RefCell::new(vec!()),
            id: id,
        }
    }
}

pub trait ServoCefBrowserExtensions {
    fn init(&self, window_info: &cef_window_info_t);
    fn send_window_event(&self, event: WindowEvent);
    fn get_title_for_main_frame(&self);
    fn pinch_zoom_level(&self) -> f32;
}

impl ServoCefBrowserExtensions for CefBrowser {
    fn init(&self, window_info: &cef_window_info_t) {
        if window_info.windowless_rendering_enabled != 0 {
            let window = window::Window::new();
            let servo_browser = Browser::new(Some(window.clone()));
            window.set_browser(self.clone());
            *self.downcast().servo_browser.borrow_mut() = ServoBrowser::OffScreen(servo_browser);
        }

        self.downcast().host.set_browser((*self).clone());
        self.downcast().frame.set_browser((*self).clone());
        if window_info.parent_window != 0 {
            self.downcast().host.initialize_compositing();
        }
    }

    fn send_window_event(&self, event: WindowEvent) {
        self.downcast().message_queue.borrow_mut().push(event);

        loop {
            match self.downcast().servo_browser.borrow_state() {
                BorrowState::Unused => {
                    let event = match self.downcast().message_queue.borrow_mut().pop() {
                        None => return,
                        Some(event) => event,
                    };
                    self.downcast().servo_browser.borrow_mut().handle_event(event);
                }
                _ => {
                    // We're trying to send an event while processing another one. This will
                    // cause general badness, so queue up that event instead of immediately
                    // processing it.
                    break
                }
            }
        }
    }

    fn get_title_for_main_frame(&self) {
        self.downcast().servo_browser.borrow().get_title_for_main_frame()
    }

    fn pinch_zoom_level(&self) -> f32 {
        self.downcast().servo_browser.borrow().pinch_zoom_level()
    }
}

pub fn update() {
    BROWSERS.with(|browsers| {
        for browser in browsers.borrow().iter() {
            if browser.downcast().callback_executed.get() == false {
                browser_callback_after_created(browser.clone());
            }
            browser.send_window_event(WindowEvent::Idle);
        }
    });
}

pub fn close(browser: CefBrowser) {
    BROWSERS.with(|browsers| {
        let mut browsers = browsers.borrow_mut();
        browsers.iter()
                .position(|&ref n| n.downcast().id == browser.downcast().id)
                .map(|e| browsers.remove(e));
    });
}

pub fn browser_callback_after_created(browser: CefBrowser) {
    if browser.downcast().client.is_null_cef_object() {
        return
    }
    let client = browser.downcast().client.clone();
    let life_span_handler = client.get_life_span_handler();
    if life_span_handler.is_not_null_cef_object() {
        life_span_handler.on_after_created(browser.clone());
    }
    browser.downcast().callback_executed.set(true);
}

fn browser_host_create(window_info: &cef_window_info_t,
                       client: CefClient,
                       callback_executed: bool)
                       -> CefBrowser {
    let browser = ServoCefBrowser::new(window_info, client).as_cef_interface();
    browser.init(window_info);
    if callback_executed {
        browser_callback_after_created(browser.clone());
    }
    BROWSERS.with(|browsers| {
        browsers.borrow_mut().push(browser.clone());
    });
    browser
}

cef_static_method_impls! {
    fn cef_browser_host_create_browser(window_info: *const cef_window_info_t,
                                       client: *mut cef_client_t,
                                       _url: *const cef_string_t,
                                       _browser_settings: *const cef_browser_settings_t,
                                       _request_context: *mut cef_request_context_t)
                                       -> c_int {{
        let client: CefClient = client;
        let _url: &[u16] = _url;
        let _browser_settings: &cef_browser_settings_t = _browser_settings;
        let _request_context: CefRequestContext = _request_context;
        browser_host_create(window_info, client, false);
        1i32
    }}
    fn cef_browser_host_create_browser_sync(window_info: *const cef_window_info_t,
                                            client: *mut cef_client_t,
                                            _url: *const cef_string_t,
                                            _browser_settings: *const cef_browser_settings_t,
                                            _request_context: *mut cef_request_context_t)
                                            -> *mut cef_browser_t {{
        let client: CefClient = client;
        let _url: &[u16] = _url;
        let _browser_settings: &cef_browser_settings_t = _browser_settings;
        let _request_context: CefRequestContext = _request_context;
        browser_host_create(window_info, client, true)
    }}
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use result::{ZipResult, ZipError};
use podio::{ReadPodExt, WritePodExt, LittleEndian};

pub const LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
pub const CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
const CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50;
pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06064b50;
const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE : u32 = 0x07064b50;

pub struct CentralDirectoryEnd
{
    pub disk_number: u16,
    pub disk_with_central_directory: u16,
    pub number_of_files_on_this_disk: u16,
    pub number_of_files: u16,
    pub central_directory_size: u32,
    pub central_directory_offset: u32,
    pub zip_file_comment: Vec<u8>,
}

impl CentralDirectoryEnd
{
    pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
    {
        let magic = try!(reader.read_u32::<LittleEndian>());
        if magic != CENTRAL_DIRECTORY_END_SIGNATURE
        {
            return Err(ZipError::InvalidArchive("Invalid digital signature header"))
        }
        let disk_number = try!(reader.read_u16::<LittleEndian>());
        let disk_with_central_directory = try!(reader.read_u16::<LittleEndian>());
        let number_of_files_on_this_disk = try!(reader.read_u16::<LittleEndian>());
        let number_of_files = try!(reader.read_u16::<LittleEndian>());
        let central_directory_size = try!(reader.read_u32::<LittleEndian>());
        let central_directory_offset = try!(reader.read_u32::<LittleEndian>());
        let zip_file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
        let zip_file_comment = try!(ReadPodExt::read_exact(reader, zip_file_comment_length));

        Ok(CentralDirectoryEnd
           {
               disk_number: disk_number,
               disk_with_central_directory: disk_with_central_directory,
               number_of_files_on_this_disk: number_of_files_on_this_disk,
               number_of_files: number_of_files,
               central_directory_size: central_directory_size,
               central_directory_offset: central_directory_offset,
               zip_file_comment: zip_file_comment,
           })
    }

    pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T) -> ZipResult<(CentralDirectoryEnd, u64)>
    {
        const HEADER_SIZE: u64 = 22;
        const BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE: u64 = HEADER_SIZE - 6;
        let file_length = try!(reader.seek(io::SeekFrom::End(0)));

        let search_upper_bound = file_length.checked_sub(HEADER_SIZE + ::std::u16::MAX as u64).unwrap_or(0);

        let mut pos = file_length - HEADER_SIZE;
        while pos >= search_upper_bound
        {
            try!(reader.seek(io::SeekFrom::Start(pos as u64)));
            if try!(reader.read_u32::<LittleEndian>()) == CENTRAL_DIRECTORY_END_SIGNATURE
            {
                try!(reader.seek(io::SeekFrom::Current(BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64)));
                let comment_length = try!(reader.read_u16::<LittleEndian>()) as u64;
                if file_length - pos - HEADER_SIZE == comment_length
                {
                    let cde_start_pos = try!(reader.seek(io::SeekFrom::Start(pos as u64)));
                    return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos));
                }
            }
            pos = match pos.checked_sub(1) {
                Some(p) => p,
                None => break,
            };
        }
        Err(ZipError::InvalidArchive("Could not find central directory end"))
    }

    pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()>
    {
        try!(writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE));
        try!(writer.write_u16::<LittleEndian>(self.disk_number));
        try!(writer.write_u16::<LittleEndian>(self.disk_with_central_directory));
        try!(writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk));
        try!(writer.write_u16::<LittleEndian>(self.number_of_files));
        try!(writer.write_u32::<LittleEndian>(self.central_directory_size));
        try!(writer.write_u32::<LittleEndian>(self.central_directory_offset));
        try!(writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16));
        try!(writer.write_all(&self.zip_file_comment));
        Ok(())
    }
}

pub struct Zip64CentralDirectoryEndLocator
{
    pub disk_with_central_directory: u32,
    pub end_of_central_directory_offset: u64,
    pub number_of_disks: u32,
}

impl Zip64CentralDirectoryEndLocator
{
    pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator>
    {
        let magic = try!(reader.read_u32::<LittleEndian>());
        if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE
        {
            return Err(ZipError::InvalidArchive("Invalid zip64 locator digital signature header"))
        }
        let disk_with_central_directory = try!(reader.read_u32::<LittleEndian>());
        let end_of_central_directory_offset = try!(reader.read_u64::<LittleEndian>());
        let number_of_disks = try!(reader.read_u32::<LittleEndian>());

        Ok(Zip64CentralDirectoryEndLocator
           {
               disk_with_central_directory: disk_with_central_directory,
               end_of_central_directory_offset: end_of_central_directory_offset,
               number_of_disks: number_of_disks,
           })
    }
}

pub struct Zip64CentralDirectoryEnd
{
    pub version_made_by: u16,
    pub version_needed_to_extract: u16,
    pub disk_number: u32,
    pub disk_with_central_directory: u32,
    pub number_of_files_on_this_disk: u64,
    pub number_of_files: u64,
    pub central_directory_size: u64,
    pub central_directory_offset: u64,
    //pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment.
}

impl Zip64CentralDirectoryEnd
{
    pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T,
                                            nominal_offset: u64,
                                            search_upper_bound: u64) -> ZipResult<(Zip64CentralDirectoryEnd, u64)>
    {
        let mut pos = nominal_offset;

        while pos <= search_upper_bound
        {
            reader.seek(io::SeekFrom::Start(pos))?;

            if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE
            {
                let archive_offset = pos - nominal_offset;

                let _record_size = try!(reader.read_u64::<LittleEndian>());
                // We would use this value if we did anything with the "zip64 extensible data sector".

                let version_made_by = try!(reader.read_u16::<LittleEndian>());
                let version_needed_to_extract = try!(reader.read_u16::<LittleEndian>());
                let disk_number = try!(reader.read_u32::<LittleEndian>());
                let disk_with_central_directory = try!(reader.read_u32::<LittleEndian>());
                let number_of_files_on_this_disk = try!(reader.read_u64::<LittleEndian>());
                let number_of_files = try!(reader.read_u64::<LittleEndian>());
                let central_directory_size = try!(reader.read_u64::<LittleEndian>());
                let central_directory_offset = try!(reader.read_u64::<LittleEndian>());

                return Ok((Zip64CentralDirectoryEnd
                          {
                              version_made_by: version_made_by,
                              version_needed_to_extract: version_needed_to_extract,
                              disk_number: disk_number,
                              disk_with_central_directory: disk_with_central_directory,
                              number_of_files_on_this_disk: number_of_files_on_this_disk,
                              number_of_files: number_of_files,
                              central_directory_size: central_directory_size,
                              central_directory_offset: central_directory_offset,
                          }, archive_offset));
            }

            pos += 1;
        }

        Err(ZipError::InvalidArchive("Could not find ZIP64 central directory end"))
    }
}
<commit_msg>Handle invalid zip header<commit_after>use std::io;
use std::io::prelude::*;
use result::{ZipResult, ZipError};
use podio::{ReadPodExt, WritePodExt, LittleEndian};

pub const LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
pub const CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
const CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50;
pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06064b50;
const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE : u32 = 0x07064b50;

pub struct CentralDirectoryEnd
{
    pub disk_number: u16,
    pub disk_with_central_directory: u16,
    pub number_of_files_on_this_disk: u16,
    pub number_of_files: u16,
    pub central_directory_size: u32,
    pub central_directory_offset: u32,
    pub zip_file_comment: Vec<u8>,
}

impl CentralDirectoryEnd
{
    pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
    {
        let magic = try!(reader.read_u32::<LittleEndian>());
        if magic != CENTRAL_DIRECTORY_END_SIGNATURE
        {
            return Err(ZipError::InvalidArchive("Invalid digital signature header"))
        }
        let disk_number = try!(reader.read_u16::<LittleEndian>());
        let disk_with_central_directory = try!(reader.read_u16::<LittleEndian>());
        let number_of_files_on_this_disk = try!(reader.read_u16::<LittleEndian>());
        let number_of_files = try!(reader.read_u16::<LittleEndian>());
        let central_directory_size = try!(reader.read_u32::<LittleEndian>());
        let central_directory_offset = try!(reader.read_u32::<LittleEndian>());
        let zip_file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
        let zip_file_comment = try!(ReadPodExt::read_exact(reader, zip_file_comment_length));

        Ok(CentralDirectoryEnd
           {
               disk_number: disk_number,
               disk_with_central_directory: disk_with_central_directory,
               number_of_files_on_this_disk: number_of_files_on_this_disk,
               number_of_files: number_of_files,
               central_directory_size: central_directory_size,
               central_directory_offset: central_directory_offset,
               zip_file_comment: zip_file_comment,
           })
    }

    pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T) -> ZipResult<(CentralDirectoryEnd, u64)>
    {
        const HEADER_SIZE: u64 = 22;
        const BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE: u64 = HEADER_SIZE - 6;
        let file_length = try!(reader.seek(io::SeekFrom::End(0)));

        let search_upper_bound = file_length.checked_sub(HEADER_SIZE + ::std::u16::MAX as u64).unwrap_or(0);

        if file_length < HEADER_SIZE {
            return Err(ZipError::InvalidArchive("Invalid zip header"));
        }

        let mut pos = file_length - HEADER_SIZE;
        while pos >= search_upper_bound
        {
            try!(reader.seek(io::SeekFrom::Start(pos as u64)));
            if try!(reader.read_u32::<LittleEndian>()) == CENTRAL_DIRECTORY_END_SIGNATURE
            {
                try!(reader.seek(io::SeekFrom::Current(BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64)));
                let comment_length = try!(reader.read_u16::<LittleEndian>()) as u64;
                if file_length - pos - HEADER_SIZE == comment_length
                {
                    let cde_start_pos = try!(reader.seek(io::SeekFrom::Start(pos as u64)));
                    return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos));
                }
            }
            pos = match pos.checked_sub(1) {
                Some(p) => p,
                None => break,
            };
        }
        Err(ZipError::InvalidArchive("Could not find central directory end"))
    }

    pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()>
    {
        try!(writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE));
        try!(writer.write_u16::<LittleEndian>(self.disk_number));
        try!(writer.write_u16::<LittleEndian>(self.disk_with_central_directory));
        try!(writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk));
        try!(writer.write_u16::<LittleEndian>(self.number_of_files));
        try!(writer.write_u32::<LittleEndian>(self.central_directory_size));
        try!(writer.write_u32::<LittleEndian>(self.central_directory_offset));
        try!(writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16));
        try!(writer.write_all(&self.zip_file_comment));
        Ok(())
    }
}

pub struct Zip64CentralDirectoryEndLocator
{
    pub disk_with_central_directory: u32,
    pub end_of_central_directory_offset: u64,
    pub number_of_disks: u32,
}

impl Zip64CentralDirectoryEndLocator
{
    pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator>
    {
        let magic = try!(reader.read_u32::<LittleEndian>());
        if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE
        {
            return Err(ZipError::InvalidArchive("Invalid zip64 locator digital signature header"))
        }
        let disk_with_central_directory = try!(reader.read_u32::<LittleEndian>());
        let end_of_central_directory_offset = try!(reader.read_u64::<LittleEndian>());
        let number_of_disks = try!(reader.read_u32::<LittleEndian>());

        Ok(Zip64CentralDirectoryEndLocator
           {
               disk_with_central_directory: disk_with_central_directory,
               end_of_central_directory_offset: end_of_central_directory_offset,
               number_of_disks: number_of_disks,
           })
    }
}

pub struct Zip64CentralDirectoryEnd
{
    pub version_made_by: u16,
    pub version_needed_to_extract: u16,
    pub disk_number: u32,
    pub disk_with_central_directory: u32,
    pub number_of_files_on_this_disk: u64,
    pub number_of_files: u64,
    pub central_directory_size: u64,
    pub central_directory_offset: u64,
    //pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment.
}

impl Zip64CentralDirectoryEnd
{
    pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T,
                                            nominal_offset: u64,
                                            search_upper_bound: u64) -> ZipResult<(Zip64CentralDirectoryEnd, u64)>
    {
        let mut pos = nominal_offset;

        while pos <= search_upper_bound
        {
            reader.seek(io::SeekFrom::Start(pos))?;

            if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE
            {
                let archive_offset = pos - nominal_offset;

                let _record_size = try!(reader.read_u64::<LittleEndian>());
                // We would use this value if we did anything with the "zip64 extensible data sector".

                let version_made_by = try!(reader.read_u16::<LittleEndian>());
                let version_needed_to_extract = try!(reader.read_u16::<LittleEndian>());
                let disk_number = try!(reader.read_u32::<LittleEndian>());
                let disk_with_central_directory = try!(reader.read_u32::<LittleEndian>());
                let number_of_files_on_this_disk = try!(reader.read_u64::<LittleEndian>());
                let number_of_files = try!(reader.read_u64::<LittleEndian>());
                let central_directory_size = try!(reader.read_u64::<LittleEndian>());
                let central_directory_offset = try!(reader.read_u64::<LittleEndian>());

                return Ok((Zip64CentralDirectoryEnd
                          {
                              version_made_by: version_made_by,
                              version_needed_to_extract: version_needed_to_extract,
                              disk_number: disk_number,
                              disk_with_central_directory: disk_with_central_directory,
                              number_of_files_on_this_disk: number_of_files_on_this_disk,
                              number_of_files: number_of_files,
                              central_directory_size: central_directory_size,
                              central_directory_offset: central_directory_offset,
                          }, archive_offset));
            }

            pos += 1;
        }

        Err(ZipError::InvalidArchive("Could not find ZIP64 central directory end"))
    }
}
<|endoftext|>


<commit_before>extern crate clap;

use clap::{App, Arg};

/// myprog -f -p=bob -- sloppy slop slop
fn main() {

    let matches = App::new("myprog")
        .arg(Arg::with_name("eff")
            .short("f"))
        .arg(Arg::with_name("pea")
             .short("p")
             .takes_value(true))
        .arg(Arg::with_name("slop")
             .multiple(true))
        .get_matches();

    println!("-f used: {:?}", matches.is_present("eff"));
    println!("-p's value: {:?}", matches.value_of("pea"));
    println!("'slops' values: {:?}", matches.values_of("slop").map(|vals| vals.collect::<Vec<_>>()));

    // Continued program logic goes here...
}
<commit_msg>Make -- mandatory for the slop example<commit_after>extern crate clap;

use clap::{App, Arg};

/// myprog -f -p=bob -- sloppy slop slop
fn main() {

    let matches = App::new("myprog")
        .arg(Arg::with_name("eff")
            .short("f"))
        .arg(Arg::with_name("pea")
             .short("p")
             .takes_value(true))
        .arg(Arg::with_name("slop")
             .multiple(true)
             .last(true))
        .get_matches();

    println!("-f used: {:?}", matches.is_present("eff"));
    println!("-p's value: {:?}", matches.value_of("pea"));
    println!("'slops' values: {:?}", matches.values_of("slop").map(|vals| vals.collect::<Vec<_>>()));

    // Continued program logic goes here...
}
<|endoftext|>


<commit_before>//! Unix-specific extensions to the `std::net` types.

use std::io;
use sys::c::{self, c_int};

use {TcpBuilder, UdpBuilder};
use ext::{self, AsSock};

/// Unix-specific extensions for the `TcpBuilder` type in this library.
pub trait UnixTcpBuilderExt {
    /// Set value for the `SO_REUSEPORT` option on this socket.
    ///
    /// This indicates that futher calls to `bind` may allow reuse of local
    /// addresses. For IPv4 sockets this means that a socket may bind even when
    /// there's a socket already listening on this port.
    fn reuse_port(&self, reuse: bool) -> io::Result<&Self>;

    /// Check the value of the `SO_REUSEPORT` option on this socket.
    fn get_reuse_port(&self) -> io::Result<bool>;
}

impl UnixTcpBuilderExt for TcpBuilder {
    fn reuse_port(&self, reuse: bool) -> io::Result<&Self> {
        ext::set_opt(self.as_sock(), c::SOL_SOCKET, c::SO_REUSEPORT,
                    reuse as c_int).map(|()| self)
    }

    fn get_reuse_port(&self) -> io::Result<bool> {
        ext::get_opt(self.as_sock(), c::SOL_SOCKET, c::SO_REUSEPORT)
            .map(ext::int2bool)
    }
}

/// Unix-specific extensions for the `UdpBuilder` type in this library.
pub trait UnixUdpBuilderExt {
    /// Set value for the `SO_REUSEPORT` option on this socket.
    ///
    /// This indicates that futher calls to `bind` may allow reuse of local
    /// addresses. For IPv4 sockets this means that a socket may bind even when
    /// there's a socket already listening on this port.
    fn reuse_port(&self, reuse: bool) -> io::Result<&Self>;

    /// Check the value of the `SO_REUSEPORT` option on this socket.
    fn get_reuse_port(&self) -> io::Result<bool>;
}

impl UnixUdpBuilderExt for UdpBuilder {
    fn reuse_port(&self, reuse: bool) -> io::Result<&Self> {
        ext::set_opt(self.as_sock(), c::SOL_SOCKET, c::SO_REUSEPORT,
                    reuse as c_int).map(|()| self)
    }

    fn get_reuse_port(&self) -> io::Result<bool> {
        ext::get_opt(self.as_sock(), c::SOL_SOCKET, c::SO_REUSEPORT)
            .map(ext::int2bool)
    }
}
<commit_msg>Fix typo in comment ('futher', missing an 'r')<commit_after>//! Unix-specific extensions to the `std::net` types.

use std::io;
use sys::c::{self, c_int};

use {TcpBuilder, UdpBuilder};
use ext::{self, AsSock};

/// Unix-specific extensions for the `TcpBuilder` type in this library.
pub trait UnixTcpBuilderExt {
    /// Set value for the `SO_REUSEPORT` option on this socket.
    ///
    /// This indicates that further calls to `bind` may allow reuse of local
    /// addresses. For IPv4 sockets this means that a socket may bind even when
    /// there's a socket already listening on this port.
    fn reuse_port(&self, reuse: bool) -> io::Result<&Self>;

    /// Check the value of the `SO_REUSEPORT` option on this socket.
    fn get_reuse_port(&self) -> io::Result<bool>;
}

impl UnixTcpBuilderExt for TcpBuilder {
    fn reuse_port(&self, reuse: bool) -> io::Result<&Self> {
        ext::set_opt(self.as_sock(), c::SOL_SOCKET, c::SO_REUSEPORT,
                    reuse as c_int).map(|()| self)
    }

    fn get_reuse_port(&self) -> io::Result<bool> {
        ext::get_opt(self.as_sock(), c::SOL_SOCKET, c::SO_REUSEPORT)
            .map(ext::int2bool)
    }
}

/// Unix-specific extensions for the `UdpBuilder` type in this library.
pub trait UnixUdpBuilderExt {
    /// Set value for the `SO_REUSEPORT` option on this socket.
    ///
    /// This indicates that further calls to `bind` may allow reuse of local
    /// addresses. For IPv4 sockets this means that a socket may bind even when
    /// there's a socket already listening on this port.
    fn reuse_port(&self, reuse: bool) -> io::Result<&Self>;

    /// Check the value of the `SO_REUSEPORT` option on this socket.
    fn get_reuse_port(&self) -> io::Result<bool>;
}

impl UnixUdpBuilderExt for UdpBuilder {
    fn reuse_port(&self, reuse: bool) -> io::Result<&Self> {
        ext::set_opt(self.as_sock(), c::SOL_SOCKET, c::SO_REUSEPORT,
                    reuse as c_int).map(|()| self)
    }

    fn get_reuse_port(&self) -> io::Result<bool> {
        ext::get_opt(self.as_sock(), c::SOL_SOCKET, c::SO_REUSEPORT)
            .map(ext::int2bool)
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::Write;

pub fn prompt_user(prompt: &str) -> String {
    warning!(prompt);
    io::stdout().flush().expect("Couldn't flush stdout!");

    let mut user_input = String::new();
    io::stdin()
        .read_line(&mut user_input)
        .ok()
        .expect("Couldn't read line!");
    String::from(user_input.trim())
}<commit_msg>Add developer documentation<commit_after>use std::io;
use std::io::Write;

/// Deliver a way to interact with the user, using the keyboard
pub fn prompt_user(prompt: &str) -> String {
    warning!(prompt);
    io::stdout().flush().expect("Couldn't flush stdout!");

    let mut user_input = String::new();
    io::stdin()
        .read_line(&mut user_input)
        .ok()
        .expect("Couldn't read line!");
    // Remove w+
    String::from(user_input.trim())
}<|endoftext|>