<commit_before>#[macro_use]
mod macros;

use coerce::{AsBool,AsNumber};

use french_press::ScopeManager;
use french_press::alloc::AllocBox;
use js_types::binding::Binding;
use js_types::js_fn::JsFnStruct;
use js_types::js_obj::JsObjStruct;
use js_types::js_str::JsStrStruct;
use js_types::js_var::{JsVar, JsType, JsPtrEnum, JsKey, JsPtrTag};
use js_types::js_var::JsType::*;

use jsrs_parser::lalr::parse_Stmt;
use jsrs_common::ast::*;
use jsrs_common::ast::Exp::*;
use jsrs_common::ast::BinOp::*;
use jsrs_common::ast::Stmt::*;

type VarWithPtr = (JsVar, Option<JsPtrEnum>);

// Helper to avoid repeating this everywhere
fn scalar(v: JsType) -> (JsVar, Option<JsPtrEnum>) {
    (JsVar::new(v), None)
}

/// Evaluate a string containing some JavaScript statements (or sequences of statements).
/// Returns a JsVar which is the return value of those statements.
pub fn eval_string(string: &str, state: &mut ScopeManager) -> JsVar {
    match parse_Stmt(string) {
        Ok(stmt) => {
            eval_stmt(&stmt, state).0
        }
        Err(e) => panic!("parse error: {:?}", e),
    }
}

/// Evaluate a single JS statement (which may be a block or sequence of statements).
/// Returns tuple of (evaluated final value, return value), where return value requires that
/// `return` be used to generate it.
pub fn eval_stmt(s: &Stmt, mut state: &mut ScopeManager) -> (JsVar, Option<JsVar>) {
    match *s {
        // var_string = exp;
        Assign(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());

            // Clone the js_var to store into the ScopeManager
            let cloned = js_var.clone();
            match state.alloc(cloned, js_ptr) {
                Ok(_) => (),
                e @ Err(_) => println!("{:?}", e),
            }

            (js_var, None)
        },

        // exp;
        BareExp(ref exp) => (eval_exp(exp, &mut state).0, None),

        // var var_string = exp
        Decl(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());
            match state.alloc(js_var.clone(), js_ptr) {
                Ok(_) => (js_var, None),
                e @ Err(_) => panic!("{:?}", e),
            }
        },

        // Empty statement (?)
        Empty => (JsVar::new(JsUndef), None),

        // if (condition) { if_block } else { else_block }
        If(ref condition, ref if_block, ref else_block) => {
            // evaluate expression
            if eval_exp(&condition, state).0.as_bool() {
                // condition = true, evaluate if-block.
                eval_stmt(&*if_block, state)
            } else {
                // condition = false
                // evaluate else-block if it exists, otherwise return undefined.
                if let Some(ref block) = *else_block {
                    eval_stmt(&*block, state)
                } else {
                    (JsVar::new(JsUndef), None)
                }
            }
        },

        // return exp
        Ret(ref exp) => {
            let js_var = eval_exp(&exp, &mut state).0;
            (js_var.clone(), Some(js_var))
        }

        // a sequence of any two expressions
        Seq(ref s1, ref s2) => {
            eval_stmt(&*s1, &mut state);
            eval_stmt(&*s2, &mut state)
        },

        // while (condition) { block }
        While(ref condition, ref block) => {
            let mut ret_val = None;
            loop {
                if eval_exp(&condition, state).0.as_bool() {
                    // TODO: check to see if a return stmt has been reached.
                    let (_, v) = eval_stmt(&*block, state);
                    ret_val = v;
                } else {
                    // condition is no longer true, return a return value
                    return (JsVar::new(JsUndef), ret_val);
                }
            }
        }
    }
}

/// Evaluate an expression into a JsVar.
pub fn eval_exp(e: &Exp, mut state: &mut ScopeManager) -> (JsVar, Option<JsPtrEnum>) {
    match e {
        // e1 [op] e2
        &BinExp(ref e1, ref op, ref e2) => {
            let val1 = eval_exp(e1, state).0;
            let val2 = eval_exp(e2, state).0;

            match *op {
                And => scalar(JsBool(val1.as_bool() && val2.as_bool())),
                Or  => scalar(JsBool(val1.as_bool() || val2.as_bool())),

                Ge  => scalar(JsBool(val1.as_bool() >= val2.as_bool())),
                Gt  => scalar(JsBool(val1.as_bool() >  val2.as_bool())),
                Le  => scalar(JsBool(val1.as_bool() <= val2.as_bool())),
                Lt  => scalar(JsBool(val1.as_bool() <  val2.as_bool())),
                Neq => scalar(JsBool(val1.as_bool() != val2.as_bool())),
                Eql => scalar(JsBool(val1.as_bool() == val2.as_bool())),

                Minus => scalar(JsNum(val1.as_number() - val2.as_number())),
                Plus  => scalar(JsNum(val1.as_number() + val2.as_number())),
                Slash => scalar(JsNum(val1.as_number() / val2.as_number())),
                Star  => scalar(JsNum(val1.as_number() * val2.as_number())),
            }
        }
        &Bool(b) => scalar(JsBool(b)),

        // fun_name([arg_exp1, arg_exps])
        &Call(ref fun_name, ref arg_exps) => {
            let fun_binding = eval_exp(fun_name, state).0;

            // Create vector of arguments, evaluated to JsVars.
            let mut args = Vec::new();
            for exp in arg_exps {
                args.push(eval_exp(exp, state));
            }

            match state.load(&fun_binding.binding) {
                Ok((_, Some(JsPtrEnum::JsFn(js_fn_struct)))) => {
                    state.push_scope(e);

                    for param in js_fn_struct.params {
                        let mut arg = if args.is_empty() {
                            scalar(JsUndef)
                        } else {
                            args.remove(0)
                        };

                        arg.0.binding = Binding::new(param.to_owned());
                        state.alloc(arg.0, arg.1)
                            .expect("Unable to store function argument in scope");
                    }

                    let (_, v) = eval_stmt(&js_fn_struct.stmt, state);

                    // Should we yield here? Not sure, so for now it doesn't
                    state.pop_scope(false).expect("Unable to clear scope for function");
                    // TODO handle obj
                    v.map(|x| (x, None)).unwrap_or(scalar(JsUndef))
                }
                Ok(_) => panic!("Invalid call object"),
                _ => panic!("ReferenceError: {:?} is not defined", fun_name)
            }
        },

        // function([param1, params]) { body }
        // function opt_binding([param1, params]) { body }
        &Defun(ref opt_binding, ref params, ref body) => {
            if let &Some(ref binding) = opt_binding {
                let js_fun = JsFnStruct::new(opt_binding, params, &**body);
                state.alloc(JsVar::bind(binding.to_owned(), JsPtr(JsPtrTag::JsFn)),
                    Some(JsPtrEnum::JsFn(js_fun.clone())))
                    .expect("Error storing function into state");
                (JsVar::bind(binding.to_owned(), JsPtr(JsPtrTag::JsFn)),
                Some(JsPtrEnum::JsFn(js_fun)))
            } else {
                panic!("functions without bindings are not yet supported.")
            }
        },

        // var.binding
        &InstanceVar(ref instance_exp, ref var) => {
            // TODO: this needs better type-reasoning and errors
            let (instance_var, var_ptr) = eval_exp(instance_exp, state);
            if let JsPtr(_) = instance_var.t {
                if let Some(JsPtrEnum::JsObj(obj_struct)) = var_ptr {
                    let try_inner_var = obj_struct.dict.get(&JsKey::JsStr(JsStrStruct::new(var)));
                    if let Some(inner_var) = try_inner_var {
                        return (inner_var.clone(), None);
                    } else {
                        panic!("illegal access of instance variable"); // TODO: better error here?
                    }
                } else {
                    panic!("illegal access of instance variable"); // TODO: better error here?
                }
            } else {
                panic!("illegal access of instance variable"); // TODO: better error here?
            }
        },

        &Method(_, _, _) => {
            unimplemented!();
        },

        &Null => {
            scalar(JsNull)
        },

        &Float(f) => scalar(JsType::JsNum(f)),
        &Neg(ref exp) => scalar(JsNum(-eval_exp(exp, state).0.as_number())),
        &Pos(ref exp) => scalar(JsNum(eval_exp(exp, state).0.as_number())),

        &PostDec(ref exp) => (eval_float_post_op!(exp, f, f - 1.0, state), None),
        &PostInc(ref exp) => (eval_float_post_op!(exp, f, f + 1.0, state), None),
        &PreDec(ref exp)  => (eval_float_pre_op!(exp, f, f - 1.0, state),  None),
        &PreInc(ref exp)  => (eval_float_pre_op!(exp, f, f + 1.0, state),  None),

        &NewObject(_, _) => unimplemented!(),
        &Object(ref fields) => {
            let mut kv_tuples = Vec::new();
            for f in fields {
                let f_key = JsKey::JsStr(JsStrStruct::new(&f.0));
                // TODO: handle obj as key/value pair
                let f_exp = eval_exp(&*f.1, state).0;
                kv_tuples.push((f_key, f_exp, None));
            }
            let obj = JsObjStruct::new(None, "", kv_tuples, &mut AllocBox::new());
            (JsVar::new(JsPtr(JsPtrTag::JsObj)), Some(JsPtrEnum::JsObj(obj)))
        },

        &Undefined => scalar(JsUndef),
        &Var(ref var_binding) => {
            state.load(&Binding::new(var_binding.clone()))
                .expect("ReferenceError: {} is not defined")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::hash_set::HashSet;
    use french_press::init_gc;
    use js_types::js_var::JsType;
    use js_types::binding::Binding;

    #[test]
    fn test_eval_literals() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(5.0f64), eval_string("5.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.0f64), eval_string("0.0;\n", &mut state).t);
        assert_eq!(JsType::JsUndef, eval_string("undefined;\n", &mut state).t);
    }

    //// TODO: handle `var` and no `var` separately
    //#[test]
    //fn test_store_state() {
    //    let mut state = HashMap::new();
    //    assert_eq!(JsUndefined, eval_string("var a = 1;\n", &mut state));
    //    assert_eq!(JsNumber(2.0f64), eval_string("a = 2;\n", &mut state));
    //    assert_eq!(JsUndefined, eval_string("var b = 3;\n", &mut state));
    //    assert_eq!(JsNumber(4.0f64), eval_string("c = 4;\n", &mut state));
    //}

    #[test]
    fn test_inc_dec() {
        let mut state = init_gc();
        //assert_eq!(JsType::JsNum(1.0f64), eval_string("var a = 1;\n", &mut state).t);
        //assert_eq!(&JsType::JsNum(1.0), state.load(&Binding::new("a")).unwrap());

        //assert_eq!(JsType::JsNum(1.0f64), eval_string("a++;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("++a;\n", &mut state));
        //assert_eq!(&JsNumber(3.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("a--;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(1.0f64), eval_string("--a;\n", &mut state));
        //assert_eq!(&JsNumber(1.0f64), state.get("a").unwrap());
    }

    #[test]
    fn test_binexp() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(6.0f64),  eval_string("2.0 + 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.5f64),  eval_string("2.0 / 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(-2.0f64), eval_string("2.0 - 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(8.0f64),  eval_string("2.0 * 4.0;\n", &mut state).t);
    }
}
<commit_msg>change errors<commit_after>#[macro_use]
mod macros;

use coerce::{AsBool,AsNumber};

use french_press::ScopeManager;
use french_press::alloc::AllocBox;
use js_types::binding::Binding;
use js_types::js_fn::JsFnStruct;
use js_types::js_obj::JsObjStruct;
use js_types::js_str::JsStrStruct;
use js_types::js_var::{JsVar, JsType, JsPtrEnum, JsKey, JsPtrTag};
use js_types::js_var::JsType::*;

use jsrs_parser::lalr::parse_Stmt;
use jsrs_common::ast::*;
use jsrs_common::ast::Exp::*;
use jsrs_common::ast::BinOp::*;
use jsrs_common::ast::Stmt::*;

type VarWithPtr = (JsVar, Option<JsPtrEnum>);

// Helper to avoid repeating this everywhere
fn scalar(v: JsType) -> (JsVar, Option<JsPtrEnum>) {
    (JsVar::new(v), None)
}

/// Evaluate a string containing some JavaScript statements (or sequences of statements).
/// Returns a JsVar which is the return value of those statements.
pub fn eval_string(string: &str, state: &mut ScopeManager) -> JsVar {
    match parse_Stmt(string) {
        Ok(stmt) => {
            eval_stmt(&stmt, state).0
        }
        Err(e) => panic!("parse error: {:?}", e),
    }
}

/// Evaluate a single JS statement (which may be a block or sequence of statements).
/// Returns tuple of (evaluated final value, return value), where return value requires that
/// `return` be used to generate it.
pub fn eval_stmt(s: &Stmt, mut state: &mut ScopeManager) -> (JsVar, Option<JsVar>) {
    match *s {
        // var_string = exp;
        Assign(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());

            // Clone the js_var to store into the ScopeManager
            let cloned = js_var.clone();
            match state.alloc(cloned, js_ptr) {
                Ok(_) => (),
                e @ Err(_) => println!("{:?}", e),
            }

            (js_var, None)
        },

        // exp;
        BareExp(ref exp) => (eval_exp(exp, &mut state).0, None),

        // var var_string = exp
        Decl(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());
            match state.alloc(js_var.clone(), js_ptr) {
                Ok(_) => (js_var, None),
                e @ Err(_) => panic!("{:?}", e),
            }
        },

        // Empty statement (?)
        Empty => (JsVar::new(JsUndef), None),

        // if (condition) { if_block } else { else_block }
        If(ref condition, ref if_block, ref else_block) => {
            // evaluate expression
            if eval_exp(&condition, state).0.as_bool() {
                // condition = true, evaluate if-block.
                eval_stmt(&*if_block, state)
            } else {
                // condition = false
                // evaluate else-block if it exists, otherwise return undefined.
                if let Some(ref block) = *else_block {
                    eval_stmt(&*block, state)
                } else {
                    (JsVar::new(JsUndef), None)
                }
            }
        },

        // return exp
        Ret(ref exp) => {
            let js_var = eval_exp(&exp, &mut state).0;
            (js_var.clone(), Some(js_var))
        }

        // a sequence of any two expressions
        Seq(ref s1, ref s2) => {
            eval_stmt(&*s1, &mut state);
            eval_stmt(&*s2, &mut state)
        },

        // while (condition) { block }
        While(ref condition, ref block) => {
            let mut ret_val = None;
            loop {
                if eval_exp(&condition, state).0.as_bool() {
                    // TODO: check to see if a return stmt has been reached.
                    let (_, v) = eval_stmt(&*block, state);
                    ret_val = v;
                } else {
                    // condition is no longer true, return a return value
                    return (JsVar::new(JsUndef), ret_val);
                }
            }
        }
    }
}

/// Evaluate an expression into a JsVar.
pub fn eval_exp(e: &Exp, mut state: &mut ScopeManager) -> (JsVar, Option<JsPtrEnum>) {
    match e {
        // e1 [op] e2
        &BinExp(ref e1, ref op, ref e2) => {
            let val1 = eval_exp(e1, state).0;
            let val2 = eval_exp(e2, state).0;

            match *op {
                And => scalar(JsBool(val1.as_bool() && val2.as_bool())),
                Or  => scalar(JsBool(val1.as_bool() || val2.as_bool())),

                Ge  => scalar(JsBool(val1.as_bool() >= val2.as_bool())),
                Gt  => scalar(JsBool(val1.as_bool() >  val2.as_bool())),
                Le  => scalar(JsBool(val1.as_bool() <= val2.as_bool())),
                Lt  => scalar(JsBool(val1.as_bool() <  val2.as_bool())),
                Neq => scalar(JsBool(val1.as_bool() != val2.as_bool())),
                Eql => scalar(JsBool(val1.as_bool() == val2.as_bool())),

                Minus => scalar(JsNum(val1.as_number() - val2.as_number())),
                Plus  => scalar(JsNum(val1.as_number() + val2.as_number())),
                Slash => scalar(JsNum(val1.as_number() / val2.as_number())),
                Star  => scalar(JsNum(val1.as_number() * val2.as_number())),
            }
        }
        &Bool(b) => scalar(JsBool(b)),

        // fun_name([arg_exp1, arg_exps])
        &Call(ref fun_name, ref arg_exps) => {
            let fun_binding = eval_exp(fun_name, state).0;

            // Create vector of arguments, evaluated to JsVars.
            let mut args = Vec::new();
            for exp in arg_exps {
                args.push(eval_exp(exp, state));
            }

            match state.load(&fun_binding.binding) {
                Ok((_, Some(JsPtrEnum::JsFn(js_fn_struct)))) => {
                    state.push_scope(e);

                    for param in js_fn_struct.params {
                        let mut arg = if args.is_empty() {
                            scalar(JsUndef)
                        } else {
                            args.remove(0)
                        };

                        arg.0.binding = Binding::new(param.to_owned());
                        state.alloc(arg.0, arg.1)
                            .expect("Unable to store function argument in scope");
                    }

                    let (_, v) = eval_stmt(&js_fn_struct.stmt, state);

                    // Should we yield here? Not sure, so for now it doesn't
                    state.pop_scope(false).expect("Unable to clear scope for function");
                    // TODO handle obj
                    v.map(|x| (x, None)).unwrap_or(scalar(JsUndef))
                }
                Ok(_) => panic!("Invalid call object"),
                _ => panic!("ReferenceError: {:?} is not defined", fun_name)
            }
        },

        // function([param1, params]) { body }
        // function opt_binding([param1, params]) { body }
        &Defun(ref opt_binding, ref params, ref body) => {
            if let &Some(ref binding) = opt_binding {
                let js_fun = JsFnStruct::new(opt_binding, params, &**body);
                state.alloc(JsVar::bind(binding.to_owned(), JsPtr(JsPtrTag::JsFn)),
                    Some(JsPtrEnum::JsFn(js_fun.clone())))
                    .expect("Error storing function into state");
                (JsVar::bind(binding.to_owned(), JsPtr(JsPtrTag::JsFn)),
                Some(JsPtrEnum::JsFn(js_fun)))
            } else {
                panic!("functions without bindings are not yet supported.")
            }
        },

        // var.binding
        &InstanceVar(ref instance_exp, ref var) => {
            // TODO: this needs better type-reasoning and errors
            let (instance_var, var_ptr) = eval_exp(instance_exp, state);
            if let JsPtr(_) = instance_var.t {
                match var_ptr {
                    Some(JsPtrEnum::JsObj(obj_struct)) => {
                        let try_inner = obj_struct.dict.get(&JsKey::JsStr(JsStrStruct::new(var)));
                        if let Some(inner_var) = try_inner {
                            return (inner_var.clone(), None);
                        } else {
                            return scalar(JsUndef);
                        }
                    },
                    // TODO: all JsPtrs can have instance vars/methods, not just JsObjs
                    _ => unimplemented!()
                }
            } else {
                // TODO: Things which are not ptrs can also have instance vars/methods
                unimplemented!()
            }
        },

        &Method(_, _, _) => {
            unimplemented!();
        },

        &Null => {
            scalar(JsNull)
        },

        &Float(f) => scalar(JsType::JsNum(f)),
        &Neg(ref exp) => scalar(JsNum(-eval_exp(exp, state).0.as_number())),
        &Pos(ref exp) => scalar(JsNum(eval_exp(exp, state).0.as_number())),

        &PostDec(ref exp) => (eval_float_post_op!(exp, f, f - 1.0, state), None),
        &PostInc(ref exp) => (eval_float_post_op!(exp, f, f + 1.0, state), None),
        &PreDec(ref exp)  => (eval_float_pre_op!(exp, f, f - 1.0, state),  None),
        &PreInc(ref exp)  => (eval_float_pre_op!(exp, f, f + 1.0, state),  None),

        &NewObject(_, _) => unimplemented!(),
        &Object(ref fields) => {
            let mut kv_tuples = Vec::new();
            for f in fields {
                let f_key = JsKey::JsStr(JsStrStruct::new(&f.0));
                // TODO: handle obj as key/value pair
                let f_exp = eval_exp(&*f.1, state).0;
                kv_tuples.push((f_key, f_exp, None));
            }
            let obj = JsObjStruct::new(None, "", kv_tuples, &mut AllocBox::new());
            (JsVar::new(JsPtr(JsPtrTag::JsObj)), Some(JsPtrEnum::JsObj(obj)))
        },

        &Undefined => scalar(JsUndef),
        &Var(ref var_binding) => {
            state.load(&Binding::new(var_binding.clone()))
                .expect("ReferenceError: {} is not defined")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::hash_set::HashSet;
    use french_press::init_gc;
    use js_types::js_var::JsType;
    use js_types::binding::Binding;

    #[test]
    fn test_eval_literals() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(5.0f64), eval_string("5.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.0f64), eval_string("0.0;\n", &mut state).t);
        assert_eq!(JsType::JsUndef, eval_string("undefined;\n", &mut state).t);
    }

    //// TODO: handle `var` and no `var` separately
    //#[test]
    //fn test_store_state() {
    //    let mut state = HashMap::new();
    //    assert_eq!(JsUndefined, eval_string("var a = 1;\n", &mut state));
    //    assert_eq!(JsNumber(2.0f64), eval_string("a = 2;\n", &mut state));
    //    assert_eq!(JsUndefined, eval_string("var b = 3;\n", &mut state));
    //    assert_eq!(JsNumber(4.0f64), eval_string("c = 4;\n", &mut state));
    //}

    #[test]
    fn test_inc_dec() {
        let mut state = init_gc();
        //assert_eq!(JsType::JsNum(1.0f64), eval_string("var a = 1;\n", &mut state).t);
        //assert_eq!(&JsType::JsNum(1.0), state.load(&Binding::new("a")).unwrap());

        //assert_eq!(JsType::JsNum(1.0f64), eval_string("a++;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("++a;\n", &mut state));
        //assert_eq!(&JsNumber(3.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("a--;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(1.0f64), eval_string("--a;\n", &mut state));
        //assert_eq!(&JsNumber(1.0f64), state.get("a").unwrap());
    }

    #[test]
    fn test_binexp() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(6.0f64),  eval_string("2.0 + 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.5f64),  eval_string("2.0 / 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(-2.0f64), eval_string("2.0 - 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(8.0f64),  eval_string("2.0 * 4.0;\n", &mut state).t);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 fd developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

// TODO: Possible optimization could avoid pushing characters on a buffer.
mod command;
mod input;
mod job;
mod token;

use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};

use lazy_static::lazy_static;
use regex::Regex;

use crate::exit_codes::ExitCode;

use self::command::execute_command;
use self::input::{basename, dirname, remove_extension};
pub use self::job::{batch, job};
use self::token::Token;

/// Execution mode of the command
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExecutionMode {
    /// Command is executed for each search result
    OneByOne,
    /// Command is run for a batch of results at once
    Batch,
}

/// Represents a template that is utilized to generate command strings.
///
/// The template is meant to be coupled with an input in order to generate a command. The
/// `generate_and_execute()` method will be used to generate a command and execute it.
#[derive(Debug, Clone, PartialEq)]
pub struct CommandTemplate {
    args: Vec<ArgumentTemplate>,
    mode: ExecutionMode,
}

impl CommandTemplate {
    pub fn new<I, S>(input: I) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        Self::build(input, ExecutionMode::OneByOne)
    }

    pub fn new_batch<I, S>(input: I) -> Result<CommandTemplate, &'static str>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        let cmd = Self::build(input, ExecutionMode::Batch);
        if cmd.number_of_tokens() > 1 {
            return Err("Only one placeholder allowed for batch commands");
        }
        if cmd.args[0].has_tokens() {
            return Err("First argument of exec-batch is expected to be a fixed executable");
        }
        Ok(cmd)
    }

    fn build<I, S>(input: I, mode: ExecutionMode) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        lazy_static! {
            static ref PLACEHOLDER_PATTERN: Regex = Regex::new(r"\{(/?\.?|//)\}").unwrap();
        }

        let mut args = Vec::new();
        let mut has_placeholder = false;

        for arg in input {
            let arg = arg.as_ref();

            let mut tokens = Vec::new();
            let mut start = 0;

            for placeholder in PLACEHOLDER_PATTERN.find_iter(arg) {
                // Leading text before the placeholder.
                if placeholder.start() > start {
                    tokens.push(Token::Text(arg[start..placeholder.start()].to_owned()));
                }

                start = placeholder.end();

                match placeholder.as_str() {
                    "{}" => tokens.push(Token::Placeholder),
                    "{.}" => tokens.push(Token::NoExt),
                    "{/}" => tokens.push(Token::Basename),
                    "{//}" => tokens.push(Token::Parent),
                    "{/.}" => tokens.push(Token::BasenameNoExt),
                    _ => panic!("Unhandled placeholder"),
                }

                has_placeholder = true;
            }

            // Without a placeholder, the argument is just fixed text.
            if tokens.is_empty() {
                args.push(ArgumentTemplate::Text(arg.to_owned()));
                continue;
            }

            if start < arg.len() {
                // Trailing text after last placeholder.
                tokens.push(Token::Text(arg[start..].to_owned()));
            }

            args.push(ArgumentTemplate::Tokens(tokens));
        }

        // If a placeholder token was not supplied, append one at the end of the command.
        if !has_placeholder {
            args.push(ArgumentTemplate::Tokens(vec![Token::Placeholder]));
        }

        CommandTemplate { args, mode }
    }

    fn number_of_tokens(&self) -> usize {
        self.args.iter().filter(|arg| arg.has_tokens()).count()
    }

    fn prepare_path(input: &Path) -> String {
        input
            .strip_prefix(".")
            .unwrap_or(input)
            .to_string_lossy()
            .into_owned()
    }

    /// Generates and executes a command.
    ///
    /// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
    /// build. Once all arguments have been processed, the command is executed.
    pub fn generate_and_execute(&self, input: &Path, out_perm: Arc<Mutex<()>>) {
        let input = Self::prepare_path(input);

        let mut cmd = Command::new(self.args[0].generate(&input).as_ref());
        for arg in &self.args[1..] {
            cmd.arg(arg.generate(&input).as_ref());
        }

        execute_command(cmd, &out_perm);
    }

    pub fn in_batch_mode(&self) -> bool {
        self.mode == ExecutionMode::Batch
    }

    pub fn generate_and_execute_batch<I>(&self, paths: I) -> ExitCode
    where
        I: Iterator<Item = PathBuf>,
    {
        let mut cmd = Command::new(self.args[0].generate("").as_ref());
        cmd.stdin(Stdio::inherit());
        cmd.stdout(Stdio::inherit());
        cmd.stderr(Stdio::inherit());

        let mut paths: Vec<String> = paths.map(|p| Self::prepare_path(&p)).collect();
        let mut has_path = false;

        for arg in &self.args[1..] {
            if arg.has_tokens() {
                paths.sort();

                // A single `Tokens` is expected
                // So we can directly consume the iterator once and for all
                for path in &mut paths {
                    cmd.arg(arg.generate(&path).as_ref());
                    has_path = true;
                }
            } else {
                cmd.arg(arg.generate("").as_ref());
            }
        }

        if has_path {
            execute_command(cmd, &Mutex::new(()))
        } else {
            ExitCode::Success
        }
    }
}

/// Represents a template for a single command argument.
///
/// The argument is either a collection of `Token`s including at least one placeholder variant, or
/// a fixed text.
#[derive(Clone, Debug, PartialEq)]
enum ArgumentTemplate {
    Tokens(Vec<Token>),
    Text(String),
}

impl ArgumentTemplate {
    pub fn has_tokens(&self) -> bool {
        match self {
            ArgumentTemplate::Tokens(_) => true,
            _ => false,
        }
    }

    pub fn generate<'a>(&'a self, path: &str) -> Cow<'a, str> {
        use self::Token::*;

        match *self {
            ArgumentTemplate::Tokens(ref tokens) => {
                let mut s = String::new();
                for token in tokens {
                    match *token {
                        Basename => s += basename(path),
                        BasenameNoExt => s += remove_extension(basename(path)),
                        NoExt => s += remove_extension(path),
                        Parent => s += dirname(path),
                        Placeholder => s += path,
                        Text(ref string) => s += string,
                    }
                }
                Cow::Owned(s)
            }
            ArgumentTemplate::Text(ref text) => Cow::Borrowed(text),
        }
    }
}

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

    #[test]
    fn tokens_with_placeholder() {
        assert_eq!(
            CommandTemplate::new(&[&"echo", &"${SHELL}:"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Text("${SHELL}:".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Basename]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_parent() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{//}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Parent]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::BasenameNoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_multiple() {
        assert_eq!(
            CommandTemplate::new(&["cp", "{}", "{/.}.ext"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("cp".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                    ArgumentTemplate::Tokens(vec![
                        Token::BasenameNoExt,
                        Token::Text(".ext".into())
                    ]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_single_batch() {
        assert_eq!(
            CommandTemplate::new_batch(&["echo", "{.}"]).unwrap(),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::Batch,
            }
        );
    }

    #[test]
    fn tokens_multiple_batch() {
        assert!(CommandTemplate::new_batch(&["echo", "{.}", "{}"]).is_err());
    }
}
<commit_msg>Restun exit code for generate_and_execute?<commit_after>// Copyright (c) 2017 fd developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

// TODO: Possible optimization could avoid pushing characters on a buffer.
mod command;
mod input;
mod job;
mod token;

use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};

use lazy_static::lazy_static;
use regex::Regex;

use crate::exit_codes::ExitCode;

use self::command::execute_command;
use self::input::{basename, dirname, remove_extension};
pub use self::job::{batch, job};
use self::token::Token;

/// Execution mode of the command
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExecutionMode {
    /// Command is executed for each search result
    OneByOne,
    /// Command is run for a batch of results at once
    Batch,
}

/// Represents a template that is utilized to generate command strings.
///
/// The template is meant to be coupled with an input in order to generate a command. The
/// `generate_and_execute()` method will be used to generate a command and execute it.
#[derive(Debug, Clone, PartialEq)]
pub struct CommandTemplate {
    args: Vec<ArgumentTemplate>,
    mode: ExecutionMode,
}

impl CommandTemplate {
    pub fn new<I, S>(input: I) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        Self::build(input, ExecutionMode::OneByOne)
    }

    pub fn new_batch<I, S>(input: I) -> Result<CommandTemplate, &'static str>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        let cmd = Self::build(input, ExecutionMode::Batch);
        if cmd.number_of_tokens() > 1 {
            return Err("Only one placeholder allowed for batch commands");
        }
        if cmd.args[0].has_tokens() {
            return Err("First argument of exec-batch is expected to be a fixed executable");
        }
        Ok(cmd)
    }

    fn build<I, S>(input: I, mode: ExecutionMode) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        lazy_static! {
            static ref PLACEHOLDER_PATTERN: Regex = Regex::new(r"\{(/?\.?|//)\}").unwrap();
        }

        let mut args = Vec::new();
        let mut has_placeholder = false;

        for arg in input {
            let arg = arg.as_ref();

            let mut tokens = Vec::new();
            let mut start = 0;

            for placeholder in PLACEHOLDER_PATTERN.find_iter(arg) {
                // Leading text before the placeholder.
                if placeholder.start() > start {
                    tokens.push(Token::Text(arg[start..placeholder.start()].to_owned()));
                }

                start = placeholder.end();

                match placeholder.as_str() {
                    "{}" => tokens.push(Token::Placeholder),
                    "{.}" => tokens.push(Token::NoExt),
                    "{/}" => tokens.push(Token::Basename),
                    "{//}" => tokens.push(Token::Parent),
                    "{/.}" => tokens.push(Token::BasenameNoExt),
                    _ => panic!("Unhandled placeholder"),
                }

                has_placeholder = true;
            }

            // Without a placeholder, the argument is just fixed text.
            if tokens.is_empty() {
                args.push(ArgumentTemplate::Text(arg.to_owned()));
                continue;
            }

            if start < arg.len() {
                // Trailing text after last placeholder.
                tokens.push(Token::Text(arg[start..].to_owned()));
            }

            args.push(ArgumentTemplate::Tokens(tokens));
        }

        // If a placeholder token was not supplied, append one at the end of the command.
        if !has_placeholder {
            args.push(ArgumentTemplate::Tokens(vec![Token::Placeholder]));
        }

        CommandTemplate { args, mode }
    }

    fn number_of_tokens(&self) -> usize {
        self.args.iter().filter(|arg| arg.has_tokens()).count()
    }

    fn prepare_path(input: &Path) -> String {
        input
            .strip_prefix(".")
            .unwrap_or(input)
            .to_string_lossy()
            .into_owned()
    }

    /// Generates and executes a command.
    ///
    /// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
    /// build. Once all arguments have been processed, the command is executed.
    pub fn generate_and_execute(&self, input: &Path, out_perm: Arc<Mutex<()>>) -> ExitCode {
        let input = Self::prepare_path(input);

        let mut cmd = Command::new(self.args[0].generate(&input).as_ref());
        for arg in &self.args[1..] {
            cmd.arg(arg.generate(&input).as_ref());
        }

        execute_command(cmd, &out_perm)
    }

    pub fn in_batch_mode(&self) -> bool {
        self.mode == ExecutionMode::Batch
    }

    pub fn generate_and_execute_batch<I>(&self, paths: I) -> ExitCode
    where
        I: Iterator<Item = PathBuf>,
    {
        let mut cmd = Command::new(self.args[0].generate("").as_ref());
        cmd.stdin(Stdio::inherit());
        cmd.stdout(Stdio::inherit());
        cmd.stderr(Stdio::inherit());

        let mut paths: Vec<String> = paths.map(|p| Self::prepare_path(&p)).collect();
        let mut has_path = false;

        for arg in &self.args[1..] {
            if arg.has_tokens() {
                paths.sort();

                // A single `Tokens` is expected
                // So we can directly consume the iterator once and for all
                for path in &mut paths {
                    cmd.arg(arg.generate(&path).as_ref());
                    has_path = true;
                }
            } else {
                cmd.arg(arg.generate("").as_ref());
            }
        }

        if has_path {
            execute_command(cmd, &Mutex::new(()))
        } else {
            ExitCode::Success
        }
    }
}

/// Represents a template for a single command argument.
///
/// The argument is either a collection of `Token`s including at least one placeholder variant, or
/// a fixed text.
#[derive(Clone, Debug, PartialEq)]
enum ArgumentTemplate {
    Tokens(Vec<Token>),
    Text(String),
}

impl ArgumentTemplate {
    pub fn has_tokens(&self) -> bool {
        match self {
            ArgumentTemplate::Tokens(_) => true,
            _ => false,
        }
    }

    pub fn generate<'a>(&'a self, path: &str) -> Cow<'a, str> {
        use self::Token::*;

        match *self {
            ArgumentTemplate::Tokens(ref tokens) => {
                let mut s = String::new();
                for token in tokens {
                    match *token {
                        Basename => s += basename(path),
                        BasenameNoExt => s += remove_extension(basename(path)),
                        NoExt => s += remove_extension(path),
                        Parent => s += dirname(path),
                        Placeholder => s += path,
                        Text(ref string) => s += string,
                    }
                }
                Cow::Owned(s)
            }
            ArgumentTemplate::Text(ref text) => Cow::Borrowed(text),
        }
    }
}

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

    #[test]
    fn tokens_with_placeholder() {
        assert_eq!(
            CommandTemplate::new(&[&"echo", &"${SHELL}:"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Text("${SHELL}:".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Basename]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_parent() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{//}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Parent]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::BasenameNoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_multiple() {
        assert_eq!(
            CommandTemplate::new(&["cp", "{}", "{/.}.ext"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("cp".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                    ArgumentTemplate::Tokens(vec![
                        Token::BasenameNoExt,
                        Token::Text(".ext".into())
                    ]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_single_batch() {
        assert_eq!(
            CommandTemplate::new_batch(&["echo", "{.}"]).unwrap(),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::Batch,
            }
        );
    }

    #[test]
    fn tokens_multiple_batch() {
        assert!(CommandTemplate::new_batch(&["echo", "{.}", "{}"]).is_err());
    }
}
<|endoftext|>


<commit_before>//! Simplistic obj loader.

use std::io::fs::File;
use std::io::Reader;
use std::vec;
use std::str;
use std::str::WordIterator;
use std::from_str::FromStr;
use std::hashmap::HashMap;
use extra::arc::Arc;
use nalgebra::na::{Vec3, Vec2, Indexable};
use nalgebra::na;
use mesh::{Mesh, Coord, Normal, UV, SharedImmutable};
use mesh;
use mtl::MtlMaterial;
use mtl;

fn error(line: uint, err: &str) -> ! {
    fail!("At line " + line.to_str() + ": " + err)
}

fn warn(line: uint, err: &str) {
    println("At line " + line.to_str() + ": " + err)
}

/// Parses an obj file.
pub fn parse_file(path: &Path, mtl_base_dir: &Path, basename: &str) -> Option<~[(~str, Mesh, Option<MtlMaterial>)]> {
    if !path.exists() {
        None
    }
    else {
        let s   = File::open(path).expect("Cannot open the file: " + path.as_str().unwrap()).read_to_end();
        let obj = str::from_utf8_owned(s);
        Some(parse(obj, mtl_base_dir, basename))
    }
}

/// Parses a string representing an obj file.
pub fn parse(string: &str, mtl_base_dir: &Path, basename: &str) -> ~[(~str, Mesh, Option<MtlMaterial>)] {
    let mut coords:     ~[Coord]            = ~[];
    let mut normals:    ~[Normal]           = ~[];
    let mut uvs:        ~[UV]               = ~[];
    let mut groups:     HashMap<~str, uint> = HashMap::new();
    let mut groups_ids: ~[~[Vec3<i32>]]     = ~[];
    let mut curr_group: uint                = 0;
    let mut ignore_normals                  = false;
    let mut ignore_uvs                      = false;
    let mut mtllib                          = HashMap::new();
    let mut group2mtl                       = HashMap::new();
    let mut curr_mtl                        = None::<MtlMaterial>;

    groups_ids.push(~[]);
    groups.insert(basename.to_owned(), 0);

    for (l, line) in string.lines_any().enumerate() {
        let mut words = line.words();
        let tag = words.next();
        match tag {
            None    => { },
            Some(w) => {
                if w.len() != 0 && w[0] != ('#' as u8) {
                    match w {
                        &"v"      => coords.push(parse_v_or_vn(l, words)),
                        &"vn"     => if !ignore_normals { normals.push(parse_v_or_vn(l, words)) },
                        &"f"      => parse_f(l, words, &mut ignore_uvs, &mut ignore_normals, &mut groups_ids, curr_group),
                        &"vt"     => if !ignore_uvs { uvs.push(parse_vt(l, words)) },
                        &"g"      => {
                            curr_group = parse_g(l, words, basename, &mut groups, &mut groups_ids);
                            curr_mtl.as_ref().map(|mtl| group2mtl.insert(curr_group, mtl.clone()));
                        },
                        &"mtllib" => parse_mtllib(l, words, mtl_base_dir, &mut mtllib),
                        &"usemtl" => curr_group = parse_usemtl(l, words, curr_group, &mtllib, &mut group2mtl, &mut groups, &mut groups_ids, &mut curr_mtl),
                        _         => {
                            println("Warning: unknown line " + l.to_str() + " ignored: `" + line + "'");
                        }
                    }
                }
            }
        }
    }

    if !uvs.is_empty() && ignore_uvs {
        println("Warning: some texture coordinates are missing. Dropping texture coordinates"
                + " infos for every vertex.");
    }

    if !normals.is_empty() && ignore_normals {
        println("Warning: some normals are missing. Dropping normals infos for every vertex.");
    }

    reformat(
        coords,
        if ignore_normals { None } else { Some(normals) },
        if ignore_uvs { None } else { Some(uvs) },
        groups_ids,
        groups,
        group2mtl)
}

fn parse_usemtl<'a>(l:          uint,
                    mut ws:     WordIterator<'a>,
                    curr_group: uint,
                    mtllib:     &HashMap<~str, MtlMaterial>,
                    group2mtl:  &mut HashMap<uint, MtlMaterial>,
                    groups:     &mut HashMap<~str, uint>,
                    groups_ids: &mut ~[~[Vec3<i32>]],
                    curr_mtl:   &mut Option<MtlMaterial>)
                    -> uint {
    let mname = ws.to_owned_vec().connect(" ");
    let none  = "None";
    if mname.as_slice() != none.as_slice() {
        match mtllib.find(&mname) {
            None    => {
                *curr_mtl = None;
                warn(l, "could not find the material " + mname);

                curr_group
            },
            Some(m) => {
                if !group2mtl.contains_key(&curr_group) {
                    group2mtl.insert(curr_group, m.clone());
                    *curr_mtl = Some(m.clone());
                    curr_group
                }
                else {
                    // multiple usemtls for one group
                    // NOTE: this is a violation of the obj specification, but we support it anyway
                    let g         = curr_group.to_str() + mname;
                    let new_group = parse_g(l, g.words(), "auto_generated_group_", groups, groups_ids);

                    group2mtl.insert(new_group, m.clone());
                    *curr_mtl = Some(m.clone());

                    new_group
                }
            }
        }
    }
    else {
        *curr_mtl = None;
        curr_group
    }
}

fn parse_mtllib<'a>(l:            uint,
                    mut ws:       WordIterator<'a>,
                    mtl_base_dir: &Path,
                    mtllib:       &mut HashMap<~str, MtlMaterial>) {
    let filename = ws.to_owned_vec().connect(" ");

    let mut path = mtl_base_dir.clone();

    path.push(filename);

    let ms = mtl::parse_file(&path);

    match ms {
        Some(ms) =>
            for m in ms.move_iter() {
                mtllib.insert(m.name.clone(), m);
            },
        None => warn(l, "could not find the mtl file " + path.as_str().unwrap())
    }
}

fn parse_v_or_vn<'a>(l: uint, mut ws: WordIterator<'a>) -> Vec3<f32> {
    let sx = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 0."));
    let sy = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 1."));
    let sz = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 2."));

    let x: Option<f32> = FromStr::from_str(sx);
    let y: Option<f32> = FromStr::from_str(sy);
    let z: Option<f32> = FromStr::from_str(sz);

    let x = x.unwrap_or_else(|| error(l, "failed to parse `" + sx + "' as a f32."));
    let y = y.unwrap_or_else(|| error(l, "failed to parse `" + sy + "' as a f32."));
    let z = z.unwrap_or_else(|| error(l, "failed to parse `" + sz + "' as a f32."));

    Vec3::new(x, y, z)
}

fn parse_f<'a>(l:              uint,
               ws:             WordIterator<'a>,
               ignore_uvs:     &mut bool,
               ignore_normals: &mut bool,
               groups_ids:     &mut ~[~[Vec3<i32>]],
               curr_group:     uint) {
    // Four formats possible: v   v/t   v//n   v/t/n
    for (i, word) in ws.enumerate() {
        let mut curr_ids: Vec3<i32> = Bounded::max_value();

        for (i, w) in word.split('/').enumerate() {
            if i == 0 || w.len() != 0 {
                let idx: Option<i32> = FromStr::from_str(w);
                match idx {
                    Some(id) => curr_ids.set(i, id - 1),
                    None     => error(l, "failed to parse `" + w + "' as a i32.")
                }
            }
        }

        if i > 2 {
            // on the fly triangulation as trangle fan
            let g = &mut groups_ids[curr_group];
            let p1 = g[g.len() - i];
            let p2 = g[g.len() - 1];
            g.push(p1);
            g.push(p2);
        }

        if curr_ids.y == Bounded::max_value() {
            *ignore_uvs = true;
        }
        if curr_ids.z == Bounded::max_value() {
            *ignore_normals = true;
        }

        groups_ids[curr_group].push(curr_ids);
    }
}

fn parse_vt<'a>(l: uint, mut ws: WordIterator<'a>) -> UV {
    let _0 = "0.0";
    let sx  = ws.next().unwrap_or_else(|| error(l, "at least 2 components were expected, found 0."));
    let sy  = ws.next().unwrap_or_else(|| error(l, "at least 2 components were expected, found 1."));
    // let sz  = ws.next().unwrap_or(_0);

    let x: Option<f32> = FromStr::from_str(sx);
    let y: Option<f32> = FromStr::from_str(sy);
    // let z: Option<f32> = FromStr::from_str(sz);

    let x = x.unwrap_or_else(|| error(l, "failed to parse `" + sx + "' as a f32."));
    let y = y.unwrap_or_else(|| error(l, "failed to parse `" + sy + "' as a f32."));
    // let z = z.unwrap_or_else(|| error(l, "failed to parse `" + sz + "' as a f32."));

    Vec2::new(x, y)
}

fn parse_g<'a>(_:          uint,
               mut ws:     WordIterator<'a>,
               prefix:     &str,
               groups:     &mut HashMap<~str, uint>,
               groups_ids: &mut ~[~[Vec3<i32>]])
               -> uint {
    let suffix = ws.to_owned_vec().connect(" ");
    let name   = if suffix.len() == 0 { prefix.to_owned() } else { prefix + "/" + suffix };

    *groups.find_or_insert_with(name, |_| { groups_ids.push(~[]); groups_ids.len() - 1 })
}

fn reformat(coords:     ~[Coord],
            normals:    Option<~[Normal]>,
            uvs:        Option<~[UV]>,
            groups_ids: ~[~[Vec3<i32>]],
            groups:     HashMap<~str, uint>,
            group2mtl:  HashMap<uint, MtlMaterial>) -> ~[(~str, Mesh, Option<MtlMaterial>)] {
    let mut vt2id:  HashMap<Vec3<i32>, u32> = HashMap::new();
    let mut vertex_ids: ~[u32]      = ~[];
    let mut resc: ~[Coord]          = ~[];
    let mut resn: Option<~[Normal]> = normals.as_ref().map(|_| ~[]);
    let mut resu: Option<~[UV]>     = uvs.as_ref().map(|_| ~[]);
    let mut resfs: ~[~[Vec3<u32>]]  = ~[];
    let mut allfs: ~[Vec3<u32>]     = ~[];
    let mut names: ~[~str]          = ~[];
    let mut mtls:  ~[Option<MtlMaterial>] = ~[];

    for (name, i) in groups.move_iter() {
        names.push(name);
        mtls.push(group2mtl.find(&i).map(|m| m.clone()));

        for point in groups_ids[i].iter() {
            let mut abs_point = *point;
            if abs_point.x < 0 { abs_point.x = coords.len() as i32 + abs_point.x + 1 };
            uvs.as_ref().map(|uvs| if abs_point.y < 0
                    { abs_point.y = uvs.len() as i32 + abs_point.y + 1 });
            normals.as_ref().map(|normals| if abs_point.z < 0
                        { abs_point.z = normals.len() as i32 + abs_point.z + 1 });

            let idx = match vt2id.find(&abs_point) {
                Some(i) => { vertex_ids.push(*i); None },
                None    => {
                    let idx = resc.len() as u32;
                    resc.push(coords[abs_point.x]);
                    resu.as_mut().map(|l| l.push(uvs.get_ref()[abs_point.y]));
                    resn.as_mut().map(|l| l.push(normals.get_ref()[abs_point.z]));

                    vertex_ids.push(idx);

                    Some(idx)
                }
            };

            idx.map(|i| vt2id.insert(abs_point, i));
        }

        let mut resf = vec::with_capacity(vertex_ids.len() / 3);

        assert!(vertex_ids.len() % 3 == 0);

        for f in vertex_ids.chunks(3) {
            resf.push(Vec3::new(f[0], f[1], f[2]));
            allfs.push(Vec3::new(f[0], f[1], f[2]));
        }

        resfs.push(resf);
        vertex_ids.clear();
    }

    let resn = resn.unwrap_or_else(|| mesh::compute_normals_array(resc, allfs));
    let resn = SharedImmutable(Arc::new(resn));
    let resu = resu.unwrap_or_else(|| vec::from_elem(resc.len(), na::zero()));
    let resu = SharedImmutable(Arc::new(resu));
    let resc = SharedImmutable(Arc::new(resc));

    let mut meshes = ~[];
    for ((fs, name), mtl) in resfs.move_iter().zip(names.move_iter()).zip(mtls.move_iter()) {
        if fs.len() != 0 {
            let fs   = SharedImmutable(Arc::new(fs));
            let mesh = Mesh::new(resc.clone(), fs, Some(resn.clone()), Some(resu.clone()), false);
            meshes.push((name, mesh, mtl))
        }
    }

    meshes
}
<commit_msg>Fix the handling of relative (negative) indices on obj files.<commit_after>//! Simplistic obj loader.

use std::io::fs::File;
use std::io::Reader;
use std::vec;
use std::str;
use std::str::WordIterator;
use std::from_str::FromStr;
use std::hashmap::HashMap;
use extra::arc::Arc;
use nalgebra::na::{Vec3, Vec2, Indexable};
use nalgebra::na;
use mesh::{Mesh, Coord, Normal, UV, SharedImmutable};
use mesh;
use mtl::MtlMaterial;
use mtl;

fn error(line: uint, err: &str) -> ! {
    fail!("At line " + line.to_str() + ": " + err)
}

fn warn(line: uint, err: &str) {
    println("At line " + line.to_str() + ": " + err)
}

/// Parses an obj file.
pub fn parse_file(path: &Path, mtl_base_dir: &Path, basename: &str) -> Option<~[(~str, Mesh, Option<MtlMaterial>)]> {
    if !path.exists() {
        None
    }
    else {
        let s   = File::open(path).expect("Cannot open the file: " + path.as_str().unwrap()).read_to_end();
        let obj = str::from_utf8_owned(s);
        Some(parse(obj, mtl_base_dir, basename))
    }
}

/// Parses a string representing an obj file.
pub fn parse(string: &str, mtl_base_dir: &Path, basename: &str) -> ~[(~str, Mesh, Option<MtlMaterial>)] {
    let mut coords:     ~[Coord]            = ~[];
    let mut normals:    ~[Normal]           = ~[];
    let mut uvs:        ~[UV]               = ~[];
    let mut groups:     HashMap<~str, uint> = HashMap::new();
    let mut groups_ids: ~[~[Vec3<u32>]]     = ~[];
    let mut curr_group: uint                = 0;
    let mut ignore_normals                  = false;
    let mut ignore_uvs                      = false;
    let mut mtllib                          = HashMap::new();
    let mut group2mtl                       = HashMap::new();
    let mut curr_mtl                        = None::<MtlMaterial>;

    groups_ids.push(~[]);
    groups.insert(basename.to_owned(), 0);

    for (l, line) in string.lines_any().enumerate() {
        let mut words = line.words();
        let tag = words.next();
        match tag {
            None    => { },
            Some(w) => {
                if w.len() != 0 && w[0] != ('#' as u8) {
                    match w {
                        &"v"      => coords.push(parse_v_or_vn(l, words)),
                        &"vn"     => if !ignore_normals { normals.push(parse_v_or_vn(l, words)) },
                        &"f"      => parse_f(l, words, coords, uvs, normals, &mut ignore_uvs, &mut ignore_normals, &mut groups_ids, curr_group),
                        &"vt"     => if !ignore_uvs { uvs.push(parse_vt(l, words)) },
                        &"g"      => {
                            curr_group = parse_g(l, words, basename, &mut groups, &mut groups_ids);
                            curr_mtl.as_ref().map(|mtl| group2mtl.insert(curr_group, mtl.clone()));
                        },
                        &"mtllib" => parse_mtllib(l, words, mtl_base_dir, &mut mtllib),
                        &"usemtl" => curr_group = parse_usemtl(l, words, curr_group, &mtllib, &mut group2mtl, &mut groups, &mut groups_ids, &mut curr_mtl),
                        _         => {
                            println("Warning: unknown line " + l.to_str() + " ignored: `" + line + "'");
                        }
                    }
                }
            }
        }
    }

    if !uvs.is_empty() && ignore_uvs {
        println("Warning: some texture coordinates are missing. Dropping texture coordinates"
                + " infos for every vertex.");
    }

    if !normals.is_empty() && ignore_normals {
        println("Warning: some normals are missing. Dropping normals infos for every vertex.");
    }

    reformat(
        coords,
        if ignore_normals { None } else { Some(normals) },
        if ignore_uvs { None } else { Some(uvs) },
        groups_ids,
        groups,
        group2mtl)
}

fn parse_usemtl<'a>(l:          uint,
                    mut ws:     WordIterator<'a>,
                    curr_group: uint,
                    mtllib:     &HashMap<~str, MtlMaterial>,
                    group2mtl:  &mut HashMap<uint, MtlMaterial>,
                    groups:     &mut HashMap<~str, uint>,
                    groups_ids: &mut ~[~[Vec3<u32>]],
                    curr_mtl:   &mut Option<MtlMaterial>)
                    -> uint {
    let mname = ws.to_owned_vec().connect(" ");
    let none  = "None";
    if mname.as_slice() != none.as_slice() {
        match mtllib.find(&mname) {
            None    => {
                *curr_mtl = None;
                warn(l, "could not find the material " + mname);

                curr_group
            },
            Some(m) => {
                if !group2mtl.contains_key(&curr_group) {
                    group2mtl.insert(curr_group, m.clone());
                    *curr_mtl = Some(m.clone());
                    curr_group
                }
                else {
                    // multiple usemtls for one group
                    // NOTE: this is a violation of the obj specification, but we support it anyway
                    let g         = curr_group.to_str() + mname;
                    let new_group = parse_g(l, g.words(), "auto_generated_group_", groups, groups_ids);

                    group2mtl.insert(new_group, m.clone());
                    *curr_mtl = Some(m.clone());

                    new_group
                }
            }
        }
    }
    else {
        *curr_mtl = None;
        curr_group
    }
}

fn parse_mtllib<'a>(l:            uint,
                    mut ws:       WordIterator<'a>,
                    mtl_base_dir: &Path,
                    mtllib:       &mut HashMap<~str, MtlMaterial>) {
    let filename = ws.to_owned_vec().connect(" ");

    let mut path = mtl_base_dir.clone();

    path.push(filename);

    let ms = mtl::parse_file(&path);

    match ms {
        Some(ms) =>
            for m in ms.move_iter() {
                mtllib.insert(m.name.clone(), m);
            },
        None => warn(l, "could not find the mtl file " + path.as_str().unwrap())
    }
}

fn parse_v_or_vn<'a>(l: uint, mut ws: WordIterator<'a>) -> Vec3<f32> {
    let sx = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 0."));
    let sy = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 1."));
    let sz = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 2."));

    let x: Option<f32> = FromStr::from_str(sx);
    let y: Option<f32> = FromStr::from_str(sy);
    let z: Option<f32> = FromStr::from_str(sz);

    let x = x.unwrap_or_else(|| error(l, "failed to parse `" + sx + "' as a f32."));
    let y = y.unwrap_or_else(|| error(l, "failed to parse `" + sy + "' as a f32."));
    let z = z.unwrap_or_else(|| error(l, "failed to parse `" + sz + "' as a f32."));

    Vec3::new(x, y, z)
}

fn parse_f<'a>(l:              uint,
               mut ws:         WordIterator<'a>,
               coords:         &[Vec3<f32>],
               uvs:            &[Vec2<f32>],
               normals:        &[Vec3<f32>],
               ignore_uvs:     &mut bool,
               ignore_normals: &mut bool,
               groups_ids:     &mut ~[~[Vec3<u32>]],
               curr_group:     uint) {
    // Four formats possible: v   v/t   v//n   v/t/n
    let mut i = 0;
    for word in ws {
        let mut curr_ids: Vec3<i32> = Bounded::max_value();

        for (i, w) in word.split('/').enumerate() {
            if i == 0 || w.len() != 0 {
                let idx: Option<i32> = FromStr::from_str(w);
                match idx {
                    Some(id) => curr_ids.set(i, id - 1),
                    None     => error(l, "failed to parse `" + w + "' as a i32.")
                }
            }
        }

        if i > 2 {
            // on the fly triangulation as trangle fan
            let g = &mut groups_ids[curr_group];
            let p1 = g[g.len() - i];
            let p2 = g[g.len() - 1];
            g.push(p1);
            g.push(p2);
        }

        if curr_ids.y == Bounded::max_value() {
            *ignore_uvs = true;
        }

        if curr_ids.z == Bounded::max_value() {
            *ignore_normals = true;
        }

        // Handle relatives indice
        let x;
        let y;
        let z;

        if curr_ids.x < 0 {
            x = (coords.len() as i32 + curr_ids.x + 1) as u32;
        }
        else {
            x = curr_ids.x as u32;
        }

        if curr_ids.y < 0 {
            y = (uvs.len() as i32 + curr_ids.y + 1) as u32;
        }
        else {
            y = curr_ids.y as u32;
        }

        if curr_ids.z < 0 {
            z = (normals.len() as i32 + curr_ids.z + 1) as u32;
        }
        else {
            z = curr_ids.z as u32;
        }

        groups_ids[curr_group].push(Vec3::new(x, y, z));

        i = i + 1;
    }

    // there is not enough vertex to form a triangle. Complete it.
    if i < 2 {
        for _ in range(0u, 3 - i) {
            let last = groups_ids[curr_group].last().clone();
            groups_ids[curr_group].push(last);
        }
    }
}

fn parse_vt<'a>(l: uint, mut ws: WordIterator<'a>) -> UV {
    let _0 = "0.0";
    let sx  = ws.next().unwrap_or_else(|| error(l, "at least 2 components were expected, found 0."));
    let sy  = ws.next().unwrap_or_else(|| error(l, "at least 2 components were expected, found 1."));
    // let sz  = ws.next().unwrap_or(_0);

    let x: Option<f32> = FromStr::from_str(sx);
    let y: Option<f32> = FromStr::from_str(sy);
    // let z: Option<f32> = FromStr::from_str(sz);

    let x = x.unwrap_or_else(|| error(l, "failed to parse `" + sx + "' as a f32."));
    let y = y.unwrap_or_else(|| error(l, "failed to parse `" + sy + "' as a f32."));
    // let z = z.unwrap_or_else(|| error(l, "failed to parse `" + sz + "' as a f32."));

    Vec2::new(x, y)
}

fn parse_g<'a>(_:          uint,
               mut ws:     WordIterator<'a>,
               prefix:     &str,
               groups:     &mut HashMap<~str, uint>,
               groups_ids: &mut ~[~[Vec3<u32>]])
               -> uint {
    let suffix = ws.to_owned_vec().connect(" ");
    let name   = if suffix.len() == 0 { prefix.to_owned() } else { prefix + "/" + suffix };

    *groups.find_or_insert_with(name, |_| { groups_ids.push(~[]); groups_ids.len() - 1 })
}

fn reformat(coords:     ~[Coord],
            normals:    Option<~[Normal]>,
            uvs:        Option<~[UV]>,
            groups_ids: ~[~[Vec3<u32>]],
            groups:     HashMap<~str, uint>,
            group2mtl:  HashMap<uint, MtlMaterial>) -> ~[(~str, Mesh, Option<MtlMaterial>)] {
    let mut vt2id:  HashMap<Vec3<u32>, u32> = HashMap::new();
    let mut vertex_ids: ~[u32]      = ~[];
    let mut resc: ~[Coord]          = ~[];
    let mut resn: Option<~[Normal]> = normals.as_ref().map(|_| ~[]);
    let mut resu: Option<~[UV]>     = uvs.as_ref().map(|_| ~[]);
    let mut resfs: ~[~[Vec3<u32>]]  = ~[];
    let mut allfs: ~[Vec3<u32>]     = ~[];
    let mut names: ~[~str]          = ~[];
    let mut mtls:  ~[Option<MtlMaterial>] = ~[];

    for (name, i) in groups.move_iter() {
        names.push(name);
        mtls.push(group2mtl.find(&i).map(|m| m.clone()));

        for point in groups_ids[i].iter() {
            let idx = match vt2id.find(point) {
                Some(i) => { vertex_ids.push(*i); None },
                None    => {
                    let idx = resc.len() as u32;
                    resc.push(coords[point.x]);
                    resu.as_mut().map(|l| l.push(uvs.get_ref()[point.y]));
                    resn.as_mut().map(|l| l.push(normals.get_ref()[point.z]));

                    vertex_ids.push(idx);

                    Some(idx)
                }
            };

            idx.map(|i| vt2id.insert(point.clone(), i));
        }

        let mut resf = vec::with_capacity(vertex_ids.len() / 3);

        assert!(vertex_ids.len() % 3 == 0);

        for f in vertex_ids.chunks(3) {
            resf.push(Vec3::new(f[0], f[1], f[2]));
            allfs.push(Vec3::new(f[0], f[1], f[2]));
        }

        resfs.push(resf);
        vertex_ids.clear();
    }

    let resn = resn.unwrap_or_else(|| mesh::compute_normals_array(resc, allfs));
    let resn = SharedImmutable(Arc::new(resn));
    let resu = resu.unwrap_or_else(|| vec::from_elem(resc.len(), na::zero()));
    let resu = SharedImmutable(Arc::new(resu));
    let resc = SharedImmutable(Arc::new(resc));

    let mut meshes = ~[];
    for ((fs, name), mtl) in resfs.move_iter().zip(names.move_iter()).zip(mtls.move_iter()) {
        if fs.len() != 0 {
            let fs   = SharedImmutable(Arc::new(fs));
            let mesh = Mesh::new(resc.clone(), fs, Some(resn.clone()), Some(resu.clone()), false);
            meshes.push((name, mesh, mtl))
        }
    }

    meshes
}
<|endoftext|>


<commit_before>// TODO: figure out how to use failure for error (or some other pattern)
#[derive(Debug)]
pub enum Error {
    NotAnInstruction,
    LongInstruction,
}

/// FlowMeta is metadata that describes code flow for an instruction.
///
/// The metadata is packed into a single byte, with the expectation that,
///  there may be one `FlowMeta` instance for each (code) offset in a program.
/// However, the you should not be worried about the representation, just the interface.
#[derive(Debug)]
pub struct FlowMeta(u8);

impl FlowMeta {
    /// Create a new FlowMeta.
    ///
    /// Args:
    ///   - insn_length: size of instruction, or 0 if not an instruction, or 0xF if 0xF or larger.
    ///   - does_fallthrough: is there a fallthrough code flow to the next instruction?
    ///   - has_xrefs_from: are there code xrefs from this instruction (non-fallthrough)?
    pub fn new(insn_length: u8, does_fallthrough: bool, has_xrefs_from: bool) -> FlowMeta {
        let len = if insn_length >= 0xF {
            // instrunction length too long
            0x0F
        } else if insn_length == 0x0 {
            // not an instruction
            0x00
        } else {
            // good flow
            insn_length & 0x0F
        };

        let fallthrough = if does_fallthrough {
            0b0001_0000
        } else {
            0b0000_0000
        };

        let xfrom = if has_xrefs_from {
            0b0010_0000
        } else {
            0b0000_0000
        };

        FlowMeta(len | fallthrough | xfrom)
    }

    /// Fetch the cached length of the instruction.
    ///
    /// If longer than 14 bytes, then will return `Error::LongInstruction` and you'll have to decode
    ///  the instruction yourself.
    ///
    /// Errors:
    ///   - NotAnInstruction
    ///   - LongInstruction
    ///
    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    /// assert_eq!(FlowMeta::new(0x01, true, true).get_insn_length().unwrap(),
    ///            0x01);
    /// assert!(matches!(FlowMeta::new(0x00, true, true).get_insn_length().err().unwrap(),
    ///                  Error::NotAnInstruction));
    /// assert!(matches!(FlowMeta::new(0x0F, true, true).get_insn_length().err().unwrap(),
    ///                  Error::LongInstruction));
    /// ```
    pub fn get_insn_length(&self) -> Result<u8, Error> {
        let v = self.0 & 0b0000_1111;
        match v {
            0x00 => Err(Error::NotAnInstruction),
            0x0F => Err(Error::LongInstruction),
            v @ _ => Ok(v)
        }
    }

    /// Does the instruction fallthrough?
    pub fn does_fallthrough(&self) -> bool {
        self.0 & 0b0001_0000 > 0
    }

    /// Does the instruction have flow xrefs from it?
    /// This does not include the fallthrough flow.
    pub fn has_xrefs_from(&self) -> bool {
        self.0 & 0b0010_0000 > 0
    }

    /// Does the instruction have flow xrefs to it?
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// assert_eq!(FlowMeta::new(0x01, false, false).has_xrefs_to(), false);
    /// ```
    pub fn has_xrefs_to(&self) -> bool {
        self.0 & 0b0100_0000 > 0
    }

    /// Set the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::new(0x01, false, false);
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    /// ```
    pub fn set_xrefs_to(&mut self) {
        self.0 = self.0 | 0b0100_0000;
    }

    /// Unset the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::new(0x01, false, false);
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    ///
    /// m.unset_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), false);
    /// ```
    pub fn unset_xrefs_to(&mut self) {
        self.0 = self.0 & 0b1011_1111
    }
}
<commit_msg>flowmeta: add display<commit_after>use std::fmt;

use super::arch;

// TODO: figure out how to use failure for error (or some other pattern)
#[derive(Debug)]
pub enum Error {
    NotAnInstruction,
    LongInstruction,
}

/// FlowMeta is metadata that describes code flow for an instruction.
///
/// The metadata is packed into a single byte, with the expectation that,
///  there may be one `FlowMeta` instance for each (code) offset in a program.
/// However, the you should not be worried about the representation, just the interface.
#[derive(Debug)]
pub struct FlowMeta(u8);

impl FlowMeta {
    /// Create a new FlowMeta.
    ///
    /// Args:
    ///   - insn_length: size of instruction, or 0 if not an instruction, or 0xF if 0xF or larger.
    ///   - does_fallthrough: is there a fallthrough code flow to the next instruction?
    ///   - has_xrefs_from: are there code xrefs from this instruction (non-fallthrough)?
    pub fn new(insn_length: u8, does_fallthrough: bool, has_xrefs_from: bool) -> FlowMeta {
        let len = if insn_length >= 0xF {
            // instrunction length too long
            0x0F
        } else if insn_length == 0x0 {
            // not an instruction
            0x00
        } else {
            // good flow
            insn_length & 0x0F
        };

        let fallthrough = if does_fallthrough {
            0b0001_0000
        } else {
            0b0000_0000
        };

        let xfrom = if has_xrefs_from {
            0b0010_0000
        } else {
            0b0000_0000
        };

        FlowMeta(len | fallthrough | xfrom)
    }

    /// Fetch the cached length of the instruction.
    ///
    /// If longer than 14 bytes, then will return `Error::LongInstruction` and you'll have to decode
    ///  the instruction yourself.
    ///
    /// Errors:
    ///   - NotAnInstruction
    ///   - LongInstruction
    ///
    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    /// assert_eq!(FlowMeta::new(0x01, true, true).get_insn_length().unwrap(),
    ///            0x01);
    /// assert!(matches!(FlowMeta::new(0x00, true, true).get_insn_length().err().unwrap(),
    ///                  Error::NotAnInstruction));
    /// assert!(matches!(FlowMeta::new(0x0F, true, true).get_insn_length().err().unwrap(),
    ///                  Error::LongInstruction));
    /// ```
    pub fn get_insn_length(&self) -> Result<u8, Error> {
        let v = self.0 & 0b0000_1111;
        match v {
            0x00 => Err(Error::NotAnInstruction),
            0x0F => Err(Error::LongInstruction),
            v @ _ => Ok(v)
        }
    }

    /// Does the instruction fallthrough?
    pub fn does_fallthrough(&self) -> bool {
        self.0 & 0b0001_0000 > 0
    }

    /// Does the instruction have flow xrefs from it?
    /// This does not include the fallthrough flow.
    pub fn has_xrefs_from(&self) -> bool {
        self.0 & 0b0010_0000 > 0
    }

    /// Does the instruction have flow xrefs to it?
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// assert_eq!(FlowMeta::new(0x01, false, false).has_xrefs_to(), false);
    /// ```
    pub fn has_xrefs_to(&self) -> bool {
        self.0 & 0b0100_0000 > 0
    }

    /// Set the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::new(0x01, false, false);
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    /// ```
    pub fn set_xrefs_to(&mut self) {
        self.0 = self.0 | 0b0100_0000;
    }

    /// Unset the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::new(0x01, false, false);
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    ///
    /// m.unset_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), false);
    /// ```
    pub fn unset_xrefs_to(&mut self) {
        self.0 = self.0 & 0b1011_1111
    }
}

impl fmt::Display for FlowMeta {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "FlowMeta{{length: {}, fallthrough: {}, xrefs to: {}, xrefs from: {}}}",
               match self.get_insn_length() {
                   Ok(v) => format!("0x{:x}", v),
                   Err(Error::LongInstruction) => "more than 0xE".to_string(),
                   Err(Error::NotAnInstruction) => "not an instruction".to_string(),
               },
               self.does_fallthrough(),
               self.has_xrefs_to(),
               self.has_xrefs_from())
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables)]

use std::thread::Thread;

use super::square::{Square,Rank,File};
use super::moves::Move;
use super::color::{White,Black};
use super::piece::Piece;
use super::piece::Type::{Pawn,King,Queen,Bishop,Knight,Rook};
use super::pos::{Position,Kingside,Queenside};

use self::Action::{Continue,Stop};

macro_rules! send {
    ($tx: expr, $x: expr) => ({
        match $tx.send_opt($x) {
            Ok(()) => (),
            Err(_) => return Stop,
        }
    })
}

enum Action {
    Continue,
    Stop,
}
impl Action {
    fn is_stop(&self) -> bool {
        match *self {
            Continue => false,
            Stop => true,
        }
    }
}

pub fn receive_psudo_legal(p: Position) -> Receiver<Move> {
    let (tx,rx) = sync_channel(0);
    Thread::spawn(move || gen_psudo_legal(&p, tx)).detach();
    rx
}

pub fn gen_psudo_legal(p: &Position, tx: SyncSender<Move>) {
    if gen_en_passant(p, &tx).is_stop() {
        return;
    }
    if gen_castle(p, &tx).is_stop() {
        return;
    }
    for file in range(0,8) {
        for rank in range(0,8) {
            if gen_move_from(p, Square::new(File(file),Rank(rank)), &tx).is_stop() {
                return;
            }
        }
    }
}

//Functions below return Err(()) if the receiver hung up.

fn gen_move_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let piece_id = match p.at(from) {
        None => return Continue,
        Some(val) => val,
    };
    if piece_id.color() != p.side_to_move() {
        return Continue;
    }
    match piece_id.piece_type() {
        Pawn => gen_pawn_from(p, from, tx),
        Queen|Bishop|Rook => gen_slider_from(p, from, tx),
        King|Knight => gen_fixed_from(p, from, tx),
    }
}

// (file, rank)
type Diff = (int, int);

fn shift(s: Square, dir: Diff) -> Option<Square> {
    let (dx, dy) = dir;
    let (File(file), Rank(rank)) = s.to_tuple();
    Square::from_int(file as int + dx, rank as int + dy)
}

fn gen_slider_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let rook_slide:   &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1)];
    let bishop_slide: &[Diff] = &[(1,1), (1,-1), (-1,-1), (-1,1)];
    let queen_slide:  &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1), (1,1), (1,-1), (-1,-1), (-1,1)];

    let piece_id = p.at(from).unwrap();
    let piece_type = piece_id.piece_type();
    let piece_color = piece_id.color();

    let slide = match piece_type {
        Rook => rook_slide,
        Bishop => bishop_slide,
        Queen => queen_slide,
        _ => panic!(),
    };

    for dir in slide.iter() {
        let mut curr_pos = shift(from, *dir);
        //while curr_pos is valid and there is no piece there.
        while curr_pos.is_some() && p.at(curr_pos.unwrap()).is_none() {
            let curr_move = Move::new(from, curr_pos.unwrap());
            send!(tx, curr_move);
            curr_pos = shift(curr_pos.unwrap(), *dir);
        }
        //if curr_pos is valid
        if curr_pos.is_some() {
            let to = curr_pos.unwrap();
            let dest: Option<Piece> = p.at(to);
            //that square is always occupied
            let dest_piece: Piece = dest.unwrap();
            let dest_color = dest_piece.color();
            //if there is an enemy at the destination
            if dest_color == piece_color.invert() {
                let curr_move = Move::new(from, to).set_capture(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_fixed_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let king_fixed:   &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1), (1,1), (1,-1), (-1,-1), (-1,1)];
    let knight_fixed: &[Diff] = &[(2,1), (2,-1), (-2,-1), (-2,1), (1,2), (1,-2), (-1,-2), (-1,2)];

    let piece_id = p.at(from).unwrap();
    let piece_type = piece_id.piece_type();
    let piece_color = piece_id.color();

    let fixed = match piece_type {
        King => king_fixed,
        Knight => knight_fixed,
        _ => panic!(),
    };

    for dir in fixed.iter() {
        let new_pos = shift(from, *dir);
        if new_pos.is_some() {
            let to = new_pos.unwrap();
            let dest: Option<Piece> = p.at(to);
            let (is_valid, is_capture) = match dest {
                Some(val) => {
                    if val.color() == piece_color { (false, false) }
                    else { (true, true) }
                },
                None => (true, false),
            };
            if is_valid {
                let curr_move = Move::new(from, to).set_capture(is_capture);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_pawn_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let piece_color = p.at(from).unwrap().color();
    let (File(_), Rank(from_rank)) = from.to_tuple();
    //rank_up is the 1-based rank from the piece-owner's side.
    let (dy, rank_up): (int, u8) = match piece_color {
        White => ( 1, 1u8 + from_rank ),
        Black => (-1, 8u8 - from_rank ),
    };
    let move_dir: Diff = (0, dy);
    let to: Square = shift(from, move_dir).unwrap();
    // if destination is empty
    if p.at(to).is_none() {
        match rank_up {
            7 => {
                for new_piece in [Queen, Knight, Rook, Bishop].iter() {
                    let curr_move = Move::new(from, to).set_promote(Some(*new_piece));
                    send!(tx, curr_move);
                }
            },
            2 => {
                let curr_move = Move::new(from, to);
                send!(tx, curr_move);
                let to2: Square = shift(to, move_dir).unwrap();
                let curr_move2 = Move::new(from, to2);
                send!(tx, curr_move2);
            },
            _ => {
                let curr_move = Move::new(from, to);
                send!(tx, curr_move);
            },
        }
    }

    for dx in [1,-1].iter() {
        let capture_dir: Diff = (*dx, dy);
        let capture_new_pos: Option<Square> = shift(from, capture_dir);
        let capture_to: Square = match capture_new_pos {
            Some(val) => val,
            None => continue,
        };
        let dest: Option<Piece> = p.at(capture_to);
        let dest_piece: Piece = match dest {
            Some(val) => val,
            None => continue,
        };
        let dest_color = dest_piece.color();
        if dest_color == piece_color.invert() {
            if rank_up == 7 {
                for new_piece in [Queen, Knight, Rook, Bishop].iter() {
                    let curr_move = Move::new(from, capture_to).set_capture(true);
                    let curr_move = curr_move.set_promote(Some(*new_piece));
                    send!(tx, curr_move);
                }
            } else {
                let curr_move = Move::new(from, to).set_capture(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_en_passant(p: &Position, tx: &SyncSender<Move>) -> Action {
    //TODO implement en passant generation
    unimplemented!()
}

fn gen_castle(p: &Position, tx: &SyncSender<Move>) -> Action {
    match p.side_to_move() {
        White => {
            if p.can_castle(Kingside, White) {
                let from = Square::new(File(4),Rank(0));
                let to   = Square::new(File(6),Rank(0));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
            if p.can_castle(Queenside, White) {
                let from = Square::new(File(4),Rank(0));
                let to   = Square::new(File(2),Rank(0));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
        }
        Black => {
            if p.can_castle(Kingside, Black) {
                let from = Square::new(File(4),Rank(7));
                let to   = Square::new(File(6),Rank(7));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
            if p.can_castle(Queenside, Black) {
                let from = Square::new(File(4),Rank(7));
                let to   = Square::new(File(2),Rank(7));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}
<commit_msg>Implement en passant generation.<commit_after>#![allow(unused_variables)]

use std::thread::Thread;

use super::square::{Square,Rank,File};
use super::moves::Move;
use super::color::{White,Black};
use super::piece::Piece;
use super::piece::Type::{Pawn,King,Queen,Bishop,Knight,Rook};
use super::pos::{Position,Kingside,Queenside};

use self::Action::{Continue,Stop};

macro_rules! send {
    ($tx: expr, $x: expr) => ({
        match $tx.send_opt($x) {
            Ok(()) => (),
            Err(_) => return Stop,
        }
    })
}

enum Action {
    Continue,
    Stop,
}
impl Action {
    fn is_stop(&self) -> bool {
        match *self {
            Continue => false,
            Stop => true,
        }
    }
}

pub fn receive_psudo_legal(p: Position) -> Receiver<Move> {
    let (tx,rx) = sync_channel(0);
    Thread::spawn(move || gen_psudo_legal(&p, tx)).detach();
    rx
}

pub fn gen_psudo_legal(p: &Position, tx: SyncSender<Move>) {
    if gen_en_passant(p, &tx).is_stop() {
        return;
    }
    if gen_castle(p, &tx).is_stop() {
        return;
    }
    for file in range(0,8) {
        for rank in range(0,8) {
            if gen_move_from(p, Square::new(File(file),Rank(rank)), &tx).is_stop() {
                return;
            }
        }
    }
}

//Functions below return Err(()) if the receiver hung up.

fn gen_move_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let piece_id = match p.at(from) {
        None => return Continue,
        Some(val) => val,
    };
    if piece_id.color() != p.side_to_move() {
        return Continue;
    }
    match piece_id.piece_type() {
        Pawn => gen_pawn_from(p, from, tx),
        Queen|Bishop|Rook => gen_slider_from(p, from, tx),
        King|Knight => gen_fixed_from(p, from, tx),
    }
}

// (file, rank)
type Diff = (int, int);

fn shift(s: Square, dir: Diff) -> Option<Square> {
    let (dx, dy) = dir;
    let (File(file), Rank(rank)) = s.to_tuple();
    Square::from_int(file as int + dx, rank as int + dy)
}

fn gen_slider_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let rook_slide:   &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1)];
    let bishop_slide: &[Diff] = &[(1,1), (1,-1), (-1,-1), (-1,1)];
    let queen_slide:  &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1), (1,1), (1,-1), (-1,-1), (-1,1)];

    let piece_id = p.at(from).unwrap();
    let piece_type = piece_id.piece_type();
    let piece_color = piece_id.color();

    let slide = match piece_type {
        Rook => rook_slide,
        Bishop => bishop_slide,
        Queen => queen_slide,
        _ => panic!(),
    };

    for dir in slide.iter() {
        let mut curr_pos = shift(from, *dir);
        //while curr_pos is valid and there is no piece there.
        while curr_pos.is_some() && p.at(curr_pos.unwrap()).is_none() {
            let curr_move = Move::new(from, curr_pos.unwrap());
            send!(tx, curr_move);
            curr_pos = shift(curr_pos.unwrap(), *dir);
        }
        //if curr_pos is valid
        if curr_pos.is_some() {
            let to = curr_pos.unwrap();
            let dest: Option<Piece> = p.at(to);
            //that square is always occupied
            let dest_piece: Piece = dest.unwrap();
            let dest_color = dest_piece.color();
            //if there is an enemy at the destination
            if dest_color == piece_color.invert() {
                let curr_move = Move::new(from, to).set_capture(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_fixed_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let king_fixed:   &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1), (1,1), (1,-1), (-1,-1), (-1,1)];
    let knight_fixed: &[Diff] = &[(2,1), (2,-1), (-2,-1), (-2,1), (1,2), (1,-2), (-1,-2), (-1,2)];

    let piece_id = p.at(from).unwrap();
    let piece_type = piece_id.piece_type();
    let piece_color = piece_id.color();

    let fixed = match piece_type {
        King => king_fixed,
        Knight => knight_fixed,
        _ => panic!(),
    };

    for dir in fixed.iter() {
        let new_pos = shift(from, *dir);
        if new_pos.is_some() {
            let to = new_pos.unwrap();
            let dest: Option<Piece> = p.at(to);
            let (is_valid, is_capture) = match dest {
                Some(val) => {
                    if val.color() == piece_color { (false, false) }
                    else { (true, true) }
                },
                None => (true, false),
            };
            if is_valid {
                let curr_move = Move::new(from, to).set_capture(is_capture);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_pawn_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let piece_color = p.at(from).unwrap().color();
    let (File(_), Rank(from_rank)) = from.to_tuple();
    //rank_up is the 1-based rank from the piece-owner's side.
    let (dy, rank_up): (int, u8) = match piece_color {
        White => ( 1, 1u8 + from_rank ),
        Black => (-1, 8u8 - from_rank ),
    };
    let move_dir: Diff = (0, dy);
    let to: Square = shift(from, move_dir).unwrap();
    // if destination is empty
    if p.at(to).is_none() {
        match rank_up {
            7 => {
                for new_piece in [Queen, Knight, Rook, Bishop].iter() {
                    let curr_move = Move::new(from, to).set_promote(Some(*new_piece));
                    send!(tx, curr_move);
                }
            },
            2 => {
                let curr_move = Move::new(from, to);
                send!(tx, curr_move);
                let to2: Square = shift(to, move_dir).unwrap();
                let curr_move2 = Move::new(from, to2);
                send!(tx, curr_move2);
            },
            _ => {
                let curr_move = Move::new(from, to);
                send!(tx, curr_move);
            },
        }
    }

    for dx in [1,-1].iter() {
        let capture_dir: Diff = (*dx, dy);
        let capture_new_pos: Option<Square> = shift(from, capture_dir);
        let capture_to: Square = match capture_new_pos {
            Some(val) => val,
            None => continue,
        };
        let dest: Option<Piece> = p.at(capture_to);
        let dest_piece: Piece = match dest {
            Some(val) => val,
            None => continue,
        };
        let dest_color = dest_piece.color();
        if dest_color == piece_color.invert() {
            if rank_up == 7 {
                for new_piece in [Queen, Knight, Rook, Bishop].iter() {
                    let curr_move = Move::new(from, capture_to).set_capture(true);
                    let curr_move = curr_move.set_promote(Some(*new_piece));
                    send!(tx, curr_move);
                }
            } else {
                let curr_move = Move::new(from, to).set_capture(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_en_passant(p: &Position, tx: &SyncSender<Move>) -> Action {
    let to_file = match p.en_passant() {
        Some(f) => f,
        None => return Continue,
    };
    let (from_rank, to_rank) = match p.side_to_move() {
        White => (Rank(4), Rank(5)),
        Black => (Rank(3), Rank(2)),
    };
    let (x, y, z);
    let from_file_all: &[File] = match to_file {
        File(0) => { x = [File(1)]; &x },
        File(7) => { y = [File(6)]; &y },
        File(f) => { z = [File(f-1), File(f+1)]; &z },
    };

    let expect_piece = Piece::new(p.side_to_move(), Pawn);
    let to = Square::new(to_file, to_rank);

    for &from_file in from_file_all.iter() {
        let from = Square::new(from_file, from_rank);
        if p.at(from) == Some(expect_piece) {
            let curr_move = Move::new(from, to).set_en_passant(true);
            send!(tx, curr_move);
        }
    }

    Continue
}

fn gen_castle(p: &Position, tx: &SyncSender<Move>) -> Action {
    match p.side_to_move() {
        White => {
            if p.can_castle(Kingside, White) {
                let from = Square::new(File(4),Rank(0));
                let to   = Square::new(File(6),Rank(0));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
            if p.can_castle(Queenside, White) {
                let from = Square::new(File(4),Rank(0));
                let to   = Square::new(File(2),Rank(0));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
        }
        Black => {
            if p.can_castle(Kingside, Black) {
                let from = Square::new(File(4),Rank(7));
                let to   = Square::new(File(6),Rank(7));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
            if p.can_castle(Queenside, Black) {
                let from = Square::new(File(4),Rank(7));
                let to   = Square::new(File(2),Rank(7));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}
<|endoftext|>