<commit_before>use std::str::FromStr;

#[derive(Debug, PartialEq)]
enum Atom {
    Number(f64),
    Symbol(String)
}

#[derive(Debug, PartialEq)]
enum Sexp {
    Atom(Atom),
    List(Vec<Sexp>)
}

#[derive(Debug, PartialEq)]
enum ParseAtomError {
    IncorrectSymbolName
}

impl FromStr for Atom {
    type Err = ParseAtomError;

    fn from_str(s: &str) -> Result<Atom, ParseAtomError> {
        match s.parse::<f64>() {
            Ok(f) => {
                Ok(Atom::Number(f))
            },
            _ => {
                match s.chars().next() {
                    Some(c) if !c.is_numeric() => {
                        Ok(Atom::Symbol(s.to_string()))
                    },
                    _ => {
                        Err(ParseAtomError::IncorrectSymbolName)
                    }
                }
            }
        }
    }
}

fn tokenize(s: &str) -> Vec<String> {
    s.replace("("," ( ")
        .replace(")", " ) ")
        .replace("\n", " ")
        .split(' ')
        .filter(|s| { !s.is_empty() })
        .map(|s| { s.to_string() })
        .collect()
}

#[derive(Debug, PartialEq)]
enum ReadState {
    StartRead,
    OpenList,
    AtomRead(Atom),
    CloseList,
    EndRead,
}

#[derive(Debug, PartialEq)]
enum ReadError {
}

fn read(tokens: Vec<String>) -> Result<Vec<Sexp>, ReadError> {
    let mut state = ReadState::StartRead;
    let mut iter = tokens.iter();
    let mut result = vec![];
    loop {
        match state {
            ReadState::StartRead => {
                match iter.next() {
                    Some(s) if *s == "(" => {
                        state = ReadState::OpenList;
                        result.push(Sexp::List(vec![]));
                    },
                    None => {
                        state = ReadState::EndRead;
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::OpenList => {
                match iter.next() {
                    Some(s) if *s == ")" => {
                        state = ReadState::CloseList;
                    },
                    Some(s) => {
                        state = ReadState::AtomRead(s.parse::<Atom>().ok().unwrap());
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::CloseList => {
                match iter.next() {
                    Some(s) if *s == "(" => {
                        state = ReadState::OpenList;
                        result.push(Sexp::List(vec![]));
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::AtomRead(atom) => {
                if let Some(Sexp::List(mut current_list)) = result.pop() {
                    current_list.push(Sexp::Atom(atom));
                    result.push(Sexp::List(current_list));
                }
                match iter.next() {
                    Some(s) if *s == ")" => {
                        state = ReadState::CloseList;
                    },
                    Some(s) => {
                        state = ReadState::AtomRead(s.parse::<Atom>().ok().unwrap());
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::EndRead => {
                return Ok(result)
            }
        }
    }
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
}

#[cfg(test)]
mod tests {
    use super::Atom::{self, Number, Symbol};
    use super::Sexp;
    use super::ParseAtomError::IncorrectSymbolName;
    use super::{tokenize, read};

    #[test]
    fn test_parse_integer() {
        assert_eq!(Number(64f64), "64".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_float() {
        assert_eq!(Number(64.5), "64.5".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_symbol() {
        assert_eq!(Symbol("name".to_string()), "name".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_incorrect_symbol_starting_with_digit() {
        assert_eq!(IncorrectSymbolName, "6name".parse::<Atom>().err().unwrap())
    }

    #[test]
    fn test_tokenize_dense_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize("(def a 1)"))
    }

    #[test]
    fn test_tokenize_sparse_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize(" ( \n def a\n1)   \n"))
    }

    #[test]
    fn test_read_empty() {
        let expected_result = vec![];
        assert_eq!(expected_result, read(tokenize("")).ok().unwrap())
    }

    #[test]
    fn test_read_single_expression() {
        let expected_result =  vec![Sexp::List(vec![Sexp::Atom(Symbol("def".to_string())),
                                                    Sexp::Atom(Symbol("a".to_string())),
                                                    Sexp::Atom(Number(1f64))])];
        assert_eq!(expected_result, read(tokenize("(def a 1)")).ok().unwrap())
    }

    #[test]
    fn test_read_multiple_expression() {
        let expected_result =  vec![Sexp::List(vec![Sexp::Atom(Symbol("def".to_string())),
                                                    Sexp::Atom(Symbol("a".to_string())),
                                                    Sexp::Atom(Number(1f64))]),
                                    Sexp::List(vec![Sexp::Atom(Symbol("def".to_string())),
                                                    Sexp::Atom(Symbol("b".to_string())),
                                                    Sexp::Atom(Number(2f64))]),
                                    Sexp::List(vec![Sexp::Atom(Symbol("+".to_string())),
                                                    Sexp::Atom(Symbol("a".to_string())),
                                                    Sexp::Atom(Symbol("b".to_string()))])];
        assert_eq!(expected_result, read(tokenize("(def a 1)(def b 2)(+ a b)")).ok().unwrap())
    }
}
<commit_msg>Remove unused pattern matching case<commit_after>use std::str::FromStr;

#[derive(Debug, PartialEq)]
enum Atom {
    Number(f64),
    Symbol(String)
}

#[derive(Debug, PartialEq)]
enum Sexp {
    Atom(Atom),
    List(Vec<Sexp>)
}

#[derive(Debug, PartialEq)]
enum ParseAtomError {
    IncorrectSymbolName
}

impl FromStr for Atom {
    type Err = ParseAtomError;

    fn from_str(s: &str) -> Result<Atom, ParseAtomError> {
        match s.parse::<f64>() {
            Ok(f) => {
                Ok(Atom::Number(f))
            },
            _ => {
                match s.chars().next() {
                    Some(c) if !c.is_numeric() => {
                        Ok(Atom::Symbol(s.to_string()))
                    },
                    _ => {
                        Err(ParseAtomError::IncorrectSymbolName)
                    }
                }
            }
        }
    }
}

fn tokenize(s: &str) -> Vec<String> {
    s.replace("("," ( ")
        .replace(")", " ) ")
        .replace("\n", " ")
        .split(' ')
        .filter(|s| { !s.is_empty() })
        .map(|s| { s.to_string() })
        .collect()
}

#[derive(Debug, PartialEq)]
enum ReadState {
    StartRead,
    OpenList,
    AtomRead(Atom),
    CloseList,
    EndRead,
}

#[derive(Debug, PartialEq)]
enum ReadError {
}

fn read(tokens: Vec<String>) -> Result<Vec<Sexp>, ReadError> {
    let mut state = ReadState::StartRead;
    let mut iter = tokens.iter();
    let mut result = vec![];
    loop {
        match state {
            ReadState::StartRead => {
                match iter.next() {
                    Some(s) if *s == "(" => {
                        state = ReadState::OpenList;
                        result.push(Sexp::List(vec![]));
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::OpenList => {
                match iter.next() {
                    Some(s) if *s == ")" => {
                        state = ReadState::CloseList;
                    },
                    Some(s) => {
                        state = ReadState::AtomRead(s.parse::<Atom>().ok().unwrap());
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::CloseList => {
                match iter.next() {
                    Some(s) if *s == "(" => {
                        state = ReadState::OpenList;
                        result.push(Sexp::List(vec![]));
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::AtomRead(atom) => {
                if let Some(Sexp::List(mut current_list)) = result.pop() {
                    current_list.push(Sexp::Atom(atom));
                    result.push(Sexp::List(current_list));
                }
                match iter.next() {
                    Some(s) if *s == ")" => {
                        state = ReadState::CloseList;
                    },
                    Some(s) => {
                        state = ReadState::AtomRead(s.parse::<Atom>().ok().unwrap());
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::EndRead => {
                return Ok(result)
            }
        }
    }
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
}

#[cfg(test)]
mod tests {
    use super::Atom::{self, Number, Symbol};
    use super::Sexp;
    use super::ParseAtomError::IncorrectSymbolName;
    use super::{tokenize, read};

    #[test]
    fn test_parse_integer() {
        assert_eq!(Number(64f64), "64".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_float() {
        assert_eq!(Number(64.5), "64.5".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_symbol() {
        assert_eq!(Symbol("name".to_string()), "name".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_incorrect_symbol_starting_with_digit() {
        assert_eq!(IncorrectSymbolName, "6name".parse::<Atom>().err().unwrap())
    }

    #[test]
    fn test_tokenize_dense_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize("(def a 1)"))
    }

    #[test]
    fn test_tokenize_sparse_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize(" ( \n def a\n1)   \n"))
    }

    #[test]
    fn test_read_empty() {
        let expected_result = vec![];
        assert_eq!(expected_result, read(tokenize("")).ok().unwrap())
    }

    #[test]
    fn test_read_single_expression() {
        let expected_result =  vec![Sexp::List(vec![Sexp::Atom(Symbol("def".to_string())),
                                                    Sexp::Atom(Symbol("a".to_string())),
                                                    Sexp::Atom(Number(1f64))])];
        assert_eq!(expected_result, read(tokenize("(def a 1)")).ok().unwrap())
    }

    #[test]
    fn test_read_multiple_expression() {
        let expected_result =  vec![Sexp::List(vec![Sexp::Atom(Symbol("def".to_string())),
                                                    Sexp::Atom(Symbol("a".to_string())),
                                                    Sexp::Atom(Number(1f64))]),
                                    Sexp::List(vec![Sexp::Atom(Symbol("def".to_string())),
                                                    Sexp::Atom(Symbol("b".to_string())),
                                                    Sexp::Atom(Number(2f64))]),
                                    Sexp::List(vec![Sexp::Atom(Symbol("+".to_string())),
                                                    Sexp::Atom(Symbol("a".to_string())),
                                                    Sexp::Atom(Symbol("b".to_string()))])];
        assert_eq!(expected_result, read(tokenize("(def a 1)(def b 2)(+ a b)")).ok().unwrap())
    }
}
<|endoftext|>


<commit_before>#[allow(dead_code)]
#[path = "../../libm/src/math/mod.rs"]
mod libm;

macro_rules! no_mangle {
    ($(fn $fun:ident($($iid:ident : $ity:ty),+) -> $oty:ty;)+) => {
        $(
            #[no_mangle]
            pub fn $fun($($iid: $ity),+) -> $oty {
                self::libm::$fun($($iid),+)
            }
        )+
    }
}

no_mangle! {
    fn acos(x: f64) -> f64;
    fn asin(x: f64) -> f64;
    fn atan(x: f64) -> f64;
    fn atan2(x: f64, y: f64) -> f64;
    fn cbrt(x: f64) -> f64;
    fn cosh(x: f64) -> f64;
    fn expm1(x: f64) -> f64;
    fn hypot(x: f64, y: f64) -> f64;
    fn log1p(x: f64) -> f64;
    fn sinh(x: f64) -> f64;
    fn tan(x: f64) -> f64;
    fn tanh(x: f64) -> f64;
    fn cos(x: f64) -> f64;
    fn cosf(x: f32) -> f32;
    fn exp(x: f64) -> f64;
    fn expf(x: f32) -> f32;
    fn log2(x: f64) -> f64;
    fn log2f(x: f32) -> f32;
    fn log10(x: f64) -> f64;
    fn log10f(x: f32) -> f32;
    fn log(x: f64) -> f64;
    fn logf(x: f32) -> f32;
    fn round(x: f64) -> f64;
    fn roundf(x: f32) -> f32;
    fn sin(x: f64) -> f64;
    fn sinf(x: f32) -> f32;
    fn pow(x: f64, y: f64) -> f64;
    fn powf(x: f32, y: f32) -> f32;
    fn exp2(x: f64) -> f64;
    fn exp2f(x: f32) -> f32;
    fn fmod(x: f64, y: f64) -> f64;
    fn fmodf(x: f32, y: f32) -> f32;
    fn fma(x: f64, y: f64, z: f64) -> f64;
    fn fmaf(x: f32, y: f32, z: f32) -> f32;
}
<commit_msg>fix path<commit_after>#[allow(dead_code)]
#[path = "../libm/src/math/mod.rs"]
mod libm;

macro_rules! no_mangle {
    ($(fn $fun:ident($($iid:ident : $ity:ty),+) -> $oty:ty;)+) => {
        $(
            #[no_mangle]
            pub fn $fun($($iid: $ity),+) -> $oty {
                self::libm::$fun($($iid),+)
            }
        )+
    }
}

no_mangle! {
    fn acos(x: f64) -> f64;
    fn asin(x: f64) -> f64;
    fn atan(x: f64) -> f64;
    fn atan2(x: f64, y: f64) -> f64;
    fn cbrt(x: f64) -> f64;
    fn cosh(x: f64) -> f64;
    fn expm1(x: f64) -> f64;
    fn hypot(x: f64, y: f64) -> f64;
    fn log1p(x: f64) -> f64;
    fn sinh(x: f64) -> f64;
    fn tan(x: f64) -> f64;
    fn tanh(x: f64) -> f64;
    fn cos(x: f64) -> f64;
    fn cosf(x: f32) -> f32;
    fn exp(x: f64) -> f64;
    fn expf(x: f32) -> f32;
    fn log2(x: f64) -> f64;
    fn log2f(x: f32) -> f32;
    fn log10(x: f64) -> f64;
    fn log10f(x: f32) -> f32;
    fn log(x: f64) -> f64;
    fn logf(x: f32) -> f32;
    fn round(x: f64) -> f64;
    fn roundf(x: f32) -> f32;
    fn sin(x: f64) -> f64;
    fn sinf(x: f32) -> f32;
    fn pow(x: f64, y: f64) -> f64;
    fn powf(x: f32, y: f32) -> f32;
    fn exp2(x: f64) -> f64;
    fn exp2f(x: f32) -> f32;
    fn fmod(x: f64, y: f64) -> f64;
    fn fmodf(x: f32, y: f32) -> f32;
    fn fma(x: f64, y: f64, z: f64) -> f64;
    fn fmaf(x: f32, y: f32, z: f32) -> f32;
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use syntax::ptr::P;
use rustc_front::hir::*;
use reexport::*;
use rustc_front::util::{is_comparison_binop, binop_to_string};
use syntax::codemap::{Span, Spanned};
use rustc_front::intravisit::FnKind;
use rustc::middle::ty;
use rustc::middle::const_eval::ConstVal::Float;
use rustc::middle::const_eval::eval_const_expr_partial;
use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
use rustc::middle::def::Def;

use utils::{get_item_name, match_path, snippet, span_lint, walk_ptrs_ty, is_integer_literal};
use utils::span_help_and_lint;

/// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`. It is `Warn` by default.
///
/// **Why is this bad?** The `ref` declaration makes the function take an owned value, but turns the argument into a reference (which means that the value is destroyed when exiting the function). This adds not much value: either take a reference type, or take an owned value and create references in the body.
///
/// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The type of `x` is more obvious with the former.
///
/// **Known problems:** If the argument is dereferenced within the function, removing the `ref` will lead to errors. This can be fixed by removing the dereferences, e.g. changing `*x` to `x` within the function.
///
/// **Example:** `fn foo(ref x: u8) -> bool { .. }`
declare_lint!(pub TOPLEVEL_REF_ARG, Warn,
              "An entire binding was declared as `ref`, in a function argument (`fn foo(ref x: Bar)`), \
               or a `let` statement (`let ref x = foo()`). In such cases, it is preferred to take \
               references with `&`.");

#[allow(missing_copy_implementations)]
pub struct TopLevelRefPass;

impl LintPass for TopLevelRefPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(TOPLEVEL_REF_ARG)
    }
}

impl LateLintPass for TopLevelRefPass {
    fn check_fn(&mut self, cx: &LateContext, k: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
        if let FnKind::Closure = k {
            // Does not apply to closures
            return
        }
        for ref arg in &decl.inputs {
            if let PatIdent(BindByRef(_), _, _) = arg.pat.node {
                span_lint(cx,
                    TOPLEVEL_REF_ARG,
                    arg.pat.span,
                    "`ref` directly on a function argument is ignored. Consider using a reference type instead."
                );
            }
        }
    }
    fn check_stmt(&mut self, cx: &LateContext, s: &Stmt) {
        if_let_chain! {
            [
            let StmtDecl(ref d, _) = s.node,
            let DeclLocal(ref l) = d.node,
            let PatIdent(BindByRef(_), i, None) = l.pat.node,
            let Some(ref init) = l.init
            ], {
                let tyopt = if let Some(ref ty) = l.ty {
                    format!(": {:?} ", ty)
                } else {
                    "".to_owned()
                };
                span_help_and_lint(cx,
                    TOPLEVEL_REF_ARG,
                    l.pat.span,
                    "`ref` on an entire `let` pattern is discouraged, take a reference with & instead",
                    &format!("try `let {} {}= &{};`", snippet(cx, i.span, "_"),
                             tyopt, snippet(cx, init.span, "_"))
                );
            }
        };
    }
}

/// **What it does:** This lint checks for comparisons to NAN. It is `Deny` by default.
///
/// **Why is this bad?** NAN does not compare meaningfully to anything – not even itself – so those comparisons are simply wrong.
///
/// **Known problems:** None
///
/// **Example:** `x == NAN`
declare_lint!(pub CMP_NAN, Deny,
              "comparisons to NAN (which will always return false, which is probably not intended)");

#[derive(Copy,Clone)]
pub struct CmpNan;

impl LintPass for CmpNan {
    fn get_lints(&self) -> LintArray {
        lint_array!(CMP_NAN)
    }
}

impl LateLintPass for CmpNan {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
            if is_comparison_binop(cmp.node) {
                if let ExprPath(_, ref path) = left.node {
                    check_nan(cx, path, expr.span);
                }
                if let ExprPath(_, ref path) = right.node {
                    check_nan(cx, path, expr.span);
                }
            }
        }
    }
}

fn check_nan(cx: &LateContext, path: &Path, span: Span) {
    path.segments.last().map(|seg| if seg.identifier.name.as_str() == "NAN" {
        span_lint(cx, CMP_NAN, span,
            "doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
    });
}

/// **What it does:** This lint checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called `*eq*` (which probably implement equality for a type involving floats). It is `Warn` by default.
///
/// **Why is this bad?** Floating point calculations are usually imprecise, so asking if two values are *exactly* equal is asking for trouble. For a good guide on what to do, see [the floating point guide](http://www.floating-point-gui.de/errors/comparison).
///
/// **Known problems:** None
///
/// **Example:** `y == 1.23f64`
declare_lint!(pub FLOAT_CMP, Warn,
              "using `==` or `!=` on float values (as floating-point operations \
               usually involve rounding errors, it is always better to check for approximate \
               equality within small bounds)");

#[derive(Copy,Clone)]
pub struct FloatCmp;

impl LintPass for FloatCmp {
    fn get_lints(&self) -> LintArray {
        lint_array!(FLOAT_CMP)
    }
}

impl LateLintPass for FloatCmp {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
            let op = cmp.node;
            if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
                if is_allowed(cx, left) || is_allowed(cx, right) { return; }
                if let Some(name) = get_item_name(cx, expr) {
                    let name = name.as_str();
                    if name == "eq" || name == "ne" || name == "is_nan" ||
                            name.starts_with("eq_") ||
                            name.ends_with("_eq") {
                        return;
                    }
                }
                span_lint(cx, FLOAT_CMP, expr.span, &format!(
                    "{}-comparison of f32 or f64 detected. Consider changing this to \
                     `abs({} - {}) < epsilon` for some suitable value of epsilon",
                    binop_to_string(op), snippet(cx, left.span, ".."),
                    snippet(cx, right.span, "..")));
            }
        }
    }
}

fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
    let res = eval_const_expr_partial(cx.tcx, expr, ExprTypeChecked, None);
    if let Ok(Float(val)) = res {
        val == 0.0 || val == ::std::f64::INFINITY || val == ::std::f64::NEG_INFINITY
    } else { false }
}

fn is_float(cx: &LateContext, expr: &Expr) -> bool {
    if let ty::TyFloat(_) = walk_ptrs_ty(cx.tcx.expr_ty(expr)).sty {
        true
    } else {
        false
    }
}

/// **What it does:** This lint checks for conversions to owned values just for the sake of a comparison. It is `Warn` by default.
///
/// **Why is this bad?** The comparison can operate on a reference, so creating an owned value effectively throws it away directly afterwards, which is needlessly consuming code and heap space.
///
/// **Known problems:** None
///
/// **Example:** `x.to_owned() == y`
declare_lint!(pub CMP_OWNED, Warn,
              "creating owned instances for comparing with others, e.g. `x == \"foo\".to_string()`");

#[derive(Copy,Clone)]
pub struct CmpOwned;

impl LintPass for CmpOwned {
    fn get_lints(&self) -> LintArray {
        lint_array!(CMP_OWNED)
    }
}

impl LateLintPass for CmpOwned {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
            if is_comparison_binop(cmp.node) {
                check_to_owned(cx, left, right.span, true, cmp.span);
                check_to_owned(cx, right, left.span, false, cmp.span)
            }
        }
    }
}

fn check_to_owned(cx: &LateContext, expr: &Expr, other_span: Span, left: bool, op: Span) {
    let snip = match expr.node {
        ExprMethodCall(Spanned{node: ref name, ..}, _, ref args) if args.len() == 1 => {
            if name.as_str() == "to_string" ||
                name.as_str() == "to_owned" && is_str_arg(cx, args) {
                    snippet(cx, args[0].span, "..")
                } else {
                    return
                }
        }
        ExprCall(ref path, ref v) if v.len() == 1 => {
            if let ExprPath(None, ref path) = path.node {
                if match_path(path, &["String", "from_str"]) ||
                    match_path(path, &["String", "from"]) {
                            snippet(cx, v[0].span, "..")
                    } else {
                        return
                    }
            } else {
                return
            }
        }
        _ => return
    };
    if left {
        span_lint(cx, CMP_OWNED, expr.span, &format!(
        "this creates an owned instance just for comparison. Consider using \
        `{} {} {}` to compare without allocation", snip,
        snippet(cx, op, "=="), snippet(cx, other_span, "..")));
    } else {
        span_lint(cx, CMP_OWNED, expr.span, &format!(
        "this creates an owned instance just for comparison. Consider using \
        `{} {} {}` to compare without allocation",
        snippet(cx, other_span, ".."), snippet(cx, op, "=="),  snip));
    }

}

fn is_str_arg(cx: &LateContext, args: &[P<Expr>]) -> bool {
    args.len() == 1 && if let ty::TyStr =
        walk_ptrs_ty(cx.tcx.expr_ty(&args[0])).sty { true } else { false }
}

/// **What it does:** This lint checks for getting the remainder of a division by one. It is `Warn` by default.
///
/// **Why is this bad?** The result can only ever be zero. No one will write such code deliberately, unless trying to win an Underhanded Rust Contest. Even for that contest, it's probably a bad idea. Use something more underhanded.
///
/// **Known problems:** None
///
/// **Example:** `x % 1`
declare_lint!(pub MODULO_ONE, Warn, "taking a number modulo 1, which always returns 0");

#[derive(Copy,Clone)]
pub struct ModuloOne;

impl LintPass for ModuloOne {
    fn get_lints(&self) -> LintArray {
        lint_array!(MODULO_ONE)
    }
}

impl LateLintPass for ModuloOne {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if let ExprBinary(ref cmp, _, ref right) = expr.node {
            if let Spanned {node: BinOp_::BiRem, ..} = *cmp {
                if is_integer_literal(right, 1) {
                    cx.span_lint(MODULO_ONE, expr.span, "any number modulo 1 will be 0");
                }
            }
        }
    }
}

/// **What it does:** This lint checks for patterns in the form `name @ _`.
///
/// **Why is this bad?** It's almost always more readable to just use direct bindings.
///
/// **Known problems:** None
///
/// **Example**:
/// ```
/// match v {
///     Some(x) => (),
///     y @ _   => (), // easier written as `y`,
/// }
/// ```
declare_lint!(pub REDUNDANT_PATTERN, Warn, "using `name @ _` in a pattern");

#[derive(Copy,Clone)]
pub struct PatternPass;

impl LintPass for PatternPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(REDUNDANT_PATTERN)
    }
}

impl LateLintPass for PatternPass {
    fn check_pat(&mut self, cx: &LateContext, pat: &Pat) {
        if let PatIdent(_, ref ident, Some(ref right)) = pat.node {
            if right.node == PatWild {
                cx.span_lint(REDUNDANT_PATTERN, pat.span, &format!(
                    "the `{} @ _` pattern can be written as just `{}`",
                    ident.node.name, ident.node.name));
            }
        }
    }
}

declare_lint!(pub USED_UNDERSCORE_BINDING, Warn,
              "using a binding which is prefixed with an underscore");

#[derive(Copy, Clone)]
pub struct UsedUnderscoreBinding;

impl LintPass for UsedUnderscoreBinding {
    fn get_lints(&self) -> LintArray {
        lint_array!(USED_UNDERSCORE_BINDING)
    }
}

impl LateLintPass for UsedUnderscoreBinding {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        let needs_lint = match expr.node {
            ExprPath(_, ref path) => {
                path.segments.last().unwrap().identifier.name.as_str().chars().next() == Some('_') &&
                (cx.tcx.def_map.borrow()).values().any(|res| match res.base_def {
                                                  Def::DefLocal(_, _) => true,
                                                  _ => false
                                            })
            },
            ExprField(_, spanned) => spanned.node.as_str().chars().next() == Some('_'),
            _ => false
        };
        if needs_lint {
            cx.span_lint(USED_UNDERSCORE_BINDING, expr.span, &format!(
                "used binding which is prefixed with an underscore. A leading underscore signals\
                 that a binding will not be used."));
        }
    }
}
<commit_msg>Reduce false positives<commit_after>use rustc::lint::*;
use syntax::ptr::P;
use rustc_front::hir::*;
use reexport::*;
use rustc_front::util::{is_comparison_binop, binop_to_string};
use syntax::codemap::{Span, Spanned};
use rustc_front::intravisit::FnKind;
use rustc::middle::ty;
use rustc::middle::const_eval::ConstVal::Float;
use rustc::middle::const_eval::eval_const_expr_partial;
use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
use rustc::middle::def::Def;

use utils::{get_item_name, match_path, snippet, span_lint, walk_ptrs_ty, is_integer_literal};
use utils::span_help_and_lint;

/// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`. It is `Warn` by default.
///
/// **Why is this bad?** The `ref` declaration makes the function take an owned value, but turns the argument into a reference (which means that the value is destroyed when exiting the function). This adds not much value: either take a reference type, or take an owned value and create references in the body.
///
/// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The type of `x` is more obvious with the former.
///
/// **Known problems:** If the argument is dereferenced within the function, removing the `ref` will lead to errors. This can be fixed by removing the dereferences, e.g. changing `*x` to `x` within the function.
///
/// **Example:** `fn foo(ref x: u8) -> bool { .. }`
declare_lint!(pub TOPLEVEL_REF_ARG, Warn,
              "An entire binding was declared as `ref`, in a function argument (`fn foo(ref x: Bar)`), \
               or a `let` statement (`let ref x = foo()`). In such cases, it is preferred to take \
               references with `&`.");

#[allow(missing_copy_implementations)]
pub struct TopLevelRefPass;

impl LintPass for TopLevelRefPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(TOPLEVEL_REF_ARG)
    }
}

impl LateLintPass for TopLevelRefPass {
    fn check_fn(&mut self, cx: &LateContext, k: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
        if let FnKind::Closure = k {
            // Does not apply to closures
            return
        }
        for ref arg in &decl.inputs {
            if let PatIdent(BindByRef(_), _, _) = arg.pat.node {
                span_lint(cx,
                    TOPLEVEL_REF_ARG,
                    arg.pat.span,
                    "`ref` directly on a function argument is ignored. Consider using a reference type instead."
                );
            }
        }
    }
    fn check_stmt(&mut self, cx: &LateContext, s: &Stmt) {
        if_let_chain! {
            [
            let StmtDecl(ref d, _) = s.node,
            let DeclLocal(ref l) = d.node,
            let PatIdent(BindByRef(_), i, None) = l.pat.node,
            let Some(ref init) = l.init
            ], {
                let tyopt = if let Some(ref ty) = l.ty {
                    format!(": {:?} ", ty)
                } else {
                    "".to_owned()
                };
                span_help_and_lint(cx,
                    TOPLEVEL_REF_ARG,
                    l.pat.span,
                    "`ref` on an entire `let` pattern is discouraged, take a reference with & instead",
                    &format!("try `let {} {}= &{};`", snippet(cx, i.span, "_"),
                             tyopt, snippet(cx, init.span, "_"))
                );
            }
        };
    }
}

/// **What it does:** This lint checks for comparisons to NAN. It is `Deny` by default.
///
/// **Why is this bad?** NAN does not compare meaningfully to anything – not even itself – so those comparisons are simply wrong.
///
/// **Known problems:** None
///
/// **Example:** `x == NAN`
declare_lint!(pub CMP_NAN, Deny,
              "comparisons to NAN (which will always return false, which is probably not intended)");

#[derive(Copy,Clone)]
pub struct CmpNan;

impl LintPass for CmpNan {
    fn get_lints(&self) -> LintArray {
        lint_array!(CMP_NAN)
    }
}

impl LateLintPass for CmpNan {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
            if is_comparison_binop(cmp.node) {
                if let ExprPath(_, ref path) = left.node {
                    check_nan(cx, path, expr.span);
                }
                if let ExprPath(_, ref path) = right.node {
                    check_nan(cx, path, expr.span);
                }
            }
        }
    }
}

fn check_nan(cx: &LateContext, path: &Path, span: Span) {
    path.segments.last().map(|seg| if seg.identifier.name.as_str() == "NAN" {
        span_lint(cx, CMP_NAN, span,
            "doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
    });
}

/// **What it does:** This lint checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called `*eq*` (which probably implement equality for a type involving floats). It is `Warn` by default.
///
/// **Why is this bad?** Floating point calculations are usually imprecise, so asking if two values are *exactly* equal is asking for trouble. For a good guide on what to do, see [the floating point guide](http://www.floating-point-gui.de/errors/comparison).
///
/// **Known problems:** None
///
/// **Example:** `y == 1.23f64`
declare_lint!(pub FLOAT_CMP, Warn,
              "using `==` or `!=` on float values (as floating-point operations \
               usually involve rounding errors, it is always better to check for approximate \
               equality within small bounds)");

#[derive(Copy,Clone)]
pub struct FloatCmp;

impl LintPass for FloatCmp {
    fn get_lints(&self) -> LintArray {
        lint_array!(FLOAT_CMP)
    }
}

impl LateLintPass for FloatCmp {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
            let op = cmp.node;
            if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
                if is_allowed(cx, left) || is_allowed(cx, right) { return; }
                if let Some(name) = get_item_name(cx, expr) {
                    let name = name.as_str();
                    if name == "eq" || name == "ne" || name == "is_nan" ||
                            name.starts_with("eq_") ||
                            name.ends_with("_eq") {
                        return;
                    }
                }
                span_lint(cx, FLOAT_CMP, expr.span, &format!(
                    "{}-comparison of f32 or f64 detected. Consider changing this to \
                     `abs({} - {}) < epsilon` for some suitable value of epsilon",
                    binop_to_string(op), snippet(cx, left.span, ".."),
                    snippet(cx, right.span, "..")));
            }
        }
    }
}

fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
    let res = eval_const_expr_partial(cx.tcx, expr, ExprTypeChecked, None);
    if let Ok(Float(val)) = res {
        val == 0.0 || val == ::std::f64::INFINITY || val == ::std::f64::NEG_INFINITY
    } else { false }
}

fn is_float(cx: &LateContext, expr: &Expr) -> bool {
    if let ty::TyFloat(_) = walk_ptrs_ty(cx.tcx.expr_ty(expr)).sty {
        true
    } else {
        false
    }
}

/// **What it does:** This lint checks for conversions to owned values just for the sake of a comparison. It is `Warn` by default.
///
/// **Why is this bad?** The comparison can operate on a reference, so creating an owned value effectively throws it away directly afterwards, which is needlessly consuming code and heap space.
///
/// **Known problems:** None
///
/// **Example:** `x.to_owned() == y`
declare_lint!(pub CMP_OWNED, Warn,
              "creating owned instances for comparing with others, e.g. `x == \"foo\".to_string()`");

#[derive(Copy,Clone)]
pub struct CmpOwned;

impl LintPass for CmpOwned {
    fn get_lints(&self) -> LintArray {
        lint_array!(CMP_OWNED)
    }
}

impl LateLintPass for CmpOwned {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
            if is_comparison_binop(cmp.node) {
                check_to_owned(cx, left, right.span, true, cmp.span);
                check_to_owned(cx, right, left.span, false, cmp.span)
            }
        }
    }
}

fn check_to_owned(cx: &LateContext, expr: &Expr, other_span: Span, left: bool, op: Span) {
    let snip = match expr.node {
        ExprMethodCall(Spanned{node: ref name, ..}, _, ref args) if args.len() == 1 => {
            if name.as_str() == "to_string" ||
                name.as_str() == "to_owned" && is_str_arg(cx, args) {
                    snippet(cx, args[0].span, "..")
                } else {
                    return
                }
        }
        ExprCall(ref path, ref v) if v.len() == 1 => {
            if let ExprPath(None, ref path) = path.node {
                if match_path(path, &["String", "from_str"]) ||
                    match_path(path, &["String", "from"]) {
                            snippet(cx, v[0].span, "..")
                    } else {
                        return
                    }
            } else {
                return
            }
        }
        _ => return
    };
    if left {
        span_lint(cx, CMP_OWNED, expr.span, &format!(
        "this creates an owned instance just for comparison. Consider using \
        `{} {} {}` to compare without allocation", snip,
        snippet(cx, op, "=="), snippet(cx, other_span, "..")));
    } else {
        span_lint(cx, CMP_OWNED, expr.span, &format!(
        "this creates an owned instance just for comparison. Consider using \
        `{} {} {}` to compare without allocation",
        snippet(cx, other_span, ".."), snippet(cx, op, "=="),  snip));
    }

}

fn is_str_arg(cx: &LateContext, args: &[P<Expr>]) -> bool {
    args.len() == 1 && if let ty::TyStr =
        walk_ptrs_ty(cx.tcx.expr_ty(&args[0])).sty { true } else { false }
}

/// **What it does:** This lint checks for getting the remainder of a division by one. It is `Warn` by default.
///
/// **Why is this bad?** The result can only ever be zero. No one will write such code deliberately, unless trying to win an Underhanded Rust Contest. Even for that contest, it's probably a bad idea. Use something more underhanded.
///
/// **Known problems:** None
///
/// **Example:** `x % 1`
declare_lint!(pub MODULO_ONE, Warn, "taking a number modulo 1, which always returns 0");

#[derive(Copy,Clone)]
pub struct ModuloOne;

impl LintPass for ModuloOne {
    fn get_lints(&self) -> LintArray {
        lint_array!(MODULO_ONE)
    }
}

impl LateLintPass for ModuloOne {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if let ExprBinary(ref cmp, _, ref right) = expr.node {
            if let Spanned {node: BinOp_::BiRem, ..} = *cmp {
                if is_integer_literal(right, 1) {
                    cx.span_lint(MODULO_ONE, expr.span, "any number modulo 1 will be 0");
                }
            }
        }
    }
}

/// **What it does:** This lint checks for patterns in the form `name @ _`.
///
/// **Why is this bad?** It's almost always more readable to just use direct bindings.
///
/// **Known problems:** None
///
/// **Example**:
/// ```
/// match v {
///     Some(x) => (),
///     y @ _   => (), // easier written as `y`,
/// }
/// ```
declare_lint!(pub REDUNDANT_PATTERN, Warn, "using `name @ _` in a pattern");

#[derive(Copy,Clone)]
pub struct PatternPass;

impl LintPass for PatternPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(REDUNDANT_PATTERN)
    }
}

impl LateLintPass for PatternPass {
    fn check_pat(&mut self, cx: &LateContext, pat: &Pat) {
        if let PatIdent(_, ref ident, Some(ref right)) = pat.node {
            if right.node == PatWild {
                cx.span_lint(REDUNDANT_PATTERN, pat.span, &format!(
                    "the `{} @ _` pattern can be written as just `{}`",
                    ident.node.name, ident.node.name));
            }
        }
    }
}

declare_lint!(pub USED_UNDERSCORE_BINDING, Warn,
              "using a binding which is prefixed with an underscore");

#[derive(Copy, Clone)]
pub struct UsedUnderscoreBinding;

impl LintPass for UsedUnderscoreBinding {
    fn get_lints(&self) -> LintArray {
        lint_array!(USED_UNDERSCORE_BINDING)
    }
}

impl LateLintPass for UsedUnderscoreBinding {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        let needs_lint = match expr.node {
            ExprPath(_, ref path) => {
                let ident = path.segments.last()
                                .expect("path should always have at least one segment")
                                .identifier;
                ident.name.as_str().chars().next() == Some('_') //starts with '_'
                && ident.name.as_str().chars().skip(1).next() != Some('_') //doesn't start with "__"
                && ident.name != ident.unhygienic_name //not in macro
                && cx.tcx.def_map.borrow().values().any(|res| match res.base_def {
                                                  Def::DefLocal(_, _) => true,
                                                  _ => false
                                            }) //local variable
            },
            ExprField(_, spanned) => spanned.node.as_str().chars().next() == Some('_'),
            _ => false
        };
        if needs_lint {
            cx.span_lint(USED_UNDERSCORE_BINDING, expr.span,
                         "used binding which is prefixed with an underscore. A leading underscore\
                          signals that a binding will not be used.");
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::Cell;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::*;
use super::len::*;

// The consumer for find_first/find_last has fake indexes representing the lower
// and upper bounds of the "range" of data it consumes. This range does not
// correspond to indexes from the consumed iterator but rather indicate the
// consumer's position relative to other consumers. The purpose is to allow a
// consumer to know it should stop consuming items when another consumer finds a
// better match.

// An indexed consumer could specialize to use the real indexes instead, but we
// don't implement that for now. The only downside of the current approach is
// that in some cases, iterators very close to each other will have the same
// range and therefore not be able to stop processing if one of them finds a
// better match than the others.

#[derive(Copy, Clone)]
enum MatchPosition {
    Leftmost,
    Rightmost,
}

pub fn find_first<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(usize::max_value());
    let consumer = FindConsumer::new(&find_op, MatchPosition::Leftmost, &best_found);
    pi.drive_unindexed(consumer)
}

pub fn find_last<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(0);
    let consumer = FindConsumer::new(&find_op, MatchPosition::Rightmost, &best_found);
    pi.drive_unindexed(consumer)
}

struct FindConsumer<'f, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    lower_bound: Cell<usize>,
    upper_bound: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
}

impl<'f, FIND_OP> FindConsumer<'f, FIND_OP> {
    fn new(find_op: &'f FIND_OP,
           match_position: MatchPosition,
           best_found: &'f AtomicUsize) -> Self {
        FindConsumer {
            find_op: find_op,
            lower_bound: Cell::new(0),
            upper_bound: usize::max_value(),
            match_position: match_position,
            best_found: best_found,
        }
    }
}

impl<'f, ITEM, FIND_OP> Consumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    type Folder = FindFolder<'f, ITEM, FIND_OP>;
    type Reducer = FindReducer;
    type Result = Option<ITEM>;

    fn cost(&mut self, cost: f64) -> f64 {
        cost * FUNC_ADJUSTMENT
    }

    fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) {
        let dir = self.match_position;
        (self.split_off(),
         self,
         FindReducer { match_position: dir })
    }

    fn into_folder(self) -> Self::Folder {
        FindFolder {
            find_op: self.find_op,
            boundary: match self.match_position {
                MatchPosition::Leftmost => self.lower_bound.get(),
                MatchPosition::Rightmost => self.upper_bound
            },
            match_position: self.match_position,
            best_found: self.best_found,
            item: None,
        }
    }

    fn full(&self) -> bool {
        let best = self.best_found.load(Ordering::Relaxed);
        match self.match_position {
            // can stop consuming if the best found index so far is *strictly*
            // better than anything this consumer will find
            MatchPosition::Leftmost => best < self.lower_bound.get(),
            MatchPosition::Rightmost => best > self.upper_bound
        }
    }
}

impl<'f, ITEM, FIND_OP> UnindexedConsumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    fn split_off(&self) -> Self {
        // Upper bound for one consumer will be lower bound for the other. This
        // overlap is okay, because only one of the bounds will be used for
        // comparing against best_found; the other is kept only to be able to
        // divide the range in half
        let old_lower_bound = self.lower_bound.get();
        let median = old_lower_bound + ((self.upper_bound - old_lower_bound) / 2);
        self.lower_bound.set(median);

        FindConsumer {
            find_op: self.find_op,
            lower_bound: Cell::new(old_lower_bound),
            upper_bound: median,
            match_position: self.match_position,
            best_found: self.best_found,
        }
    }

    fn to_reducer(&self) -> Self::Reducer {
        FindReducer { match_position: self.match_position }
    }
}

struct FindFolder<'f, ITEM, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    boundary: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
    item: Option<ITEM>,
}

impl<'f, FIND_OP: 'f + Fn(&ITEM) -> bool, ITEM> Folder<ITEM> for FindFolder<'f, ITEM, FIND_OP> {
    type Result = Option<ITEM>;

    fn consume(mut self, item: ITEM) -> Self {
        if (self.find_op)(&item) {
            // This may sometimes set best_found to a worse index than it was
            // before, depending on timing. This means more consumers will
            // continue to run than necessary, but the reducer will still ensure
            // the correct value is returned.
            self.best_found.swap(self.boundary, Ordering::Relaxed);
            self.item = Some(item);
        }
        self
    }

    fn complete(self) -> Self::Result {
        self.item
    }

    fn full(&self) -> bool {
        let best_found = self.best_found.load(Ordering::Relaxed);
        match self.match_position {
            MatchPosition::Leftmost => best_found < self.boundary,
            MatchPosition::Rightmost => best_found > self.boundary,
        }
    }
}

struct FindReducer {
    match_position: MatchPosition
}

impl<ITEM> Reducer<Option<ITEM>> for FindReducer {
    fn reduce(self, left: Option<ITEM>, right: Option<ITEM>) -> Option<ITEM> {
        match self.match_position {
            MatchPosition::Leftmost => left.or(right),
            MatchPosition::Rightmost => right.or(left)
        }
    }
}
<commit_msg>Add extra import (related to #173)<commit_after>use std::cell::Cell;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::internal::*;
use super::*;
use super::len::*;

// The consumer for find_first/find_last has fake indexes representing the lower
// and upper bounds of the "range" of data it consumes. This range does not
// correspond to indexes from the consumed iterator but rather indicate the
// consumer's position relative to other consumers. The purpose is to allow a
// consumer to know it should stop consuming items when another consumer finds a
// better match.

// An indexed consumer could specialize to use the real indexes instead, but we
// don't implement that for now. The only downside of the current approach is
// that in some cases, iterators very close to each other will have the same
// range and therefore not be able to stop processing if one of them finds a
// better match than the others.

#[derive(Copy, Clone)]
enum MatchPosition {
    Leftmost,
    Rightmost,
}

pub fn find_first<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(usize::max_value());
    let consumer = FindConsumer::new(&find_op, MatchPosition::Leftmost, &best_found);
    pi.drive_unindexed(consumer)
}

pub fn find_last<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(0);
    let consumer = FindConsumer::new(&find_op, MatchPosition::Rightmost, &best_found);
    pi.drive_unindexed(consumer)
}

struct FindConsumer<'f, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    lower_bound: Cell<usize>,
    upper_bound: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
}

impl<'f, FIND_OP> FindConsumer<'f, FIND_OP> {
    fn new(find_op: &'f FIND_OP,
           match_position: MatchPosition,
           best_found: &'f AtomicUsize) -> Self {
        FindConsumer {
            find_op: find_op,
            lower_bound: Cell::new(0),
            upper_bound: usize::max_value(),
            match_position: match_position,
            best_found: best_found,
        }
    }
}

impl<'f, ITEM, FIND_OP> Consumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    type Folder = FindFolder<'f, ITEM, FIND_OP>;
    type Reducer = FindReducer;
    type Result = Option<ITEM>;

    fn cost(&mut self, cost: f64) -> f64 {
        cost * FUNC_ADJUSTMENT
    }

    fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) {
        let dir = self.match_position;
        (self.split_off(),
         self,
         FindReducer { match_position: dir })
    }

    fn into_folder(self) -> Self::Folder {
        FindFolder {
            find_op: self.find_op,
            boundary: match self.match_position {
                MatchPosition::Leftmost => self.lower_bound.get(),
                MatchPosition::Rightmost => self.upper_bound
            },
            match_position: self.match_position,
            best_found: self.best_found,
            item: None,
        }
    }

    fn full(&self) -> bool {
        let best = self.best_found.load(Ordering::Relaxed);
        match self.match_position {
            // can stop consuming if the best found index so far is *strictly*
            // better than anything this consumer will find
            MatchPosition::Leftmost => best < self.lower_bound.get(),
            MatchPosition::Rightmost => best > self.upper_bound
        }
    }
}

impl<'f, ITEM, FIND_OP> UnindexedConsumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    fn split_off(&self) -> Self {
        // Upper bound for one consumer will be lower bound for the other. This
        // overlap is okay, because only one of the bounds will be used for
        // comparing against best_found; the other is kept only to be able to
        // divide the range in half
        let old_lower_bound = self.lower_bound.get();
        let median = old_lower_bound + ((self.upper_bound - old_lower_bound) / 2);
        self.lower_bound.set(median);

        FindConsumer {
            find_op: self.find_op,
            lower_bound: Cell::new(old_lower_bound),
            upper_bound: median,
            match_position: self.match_position,
            best_found: self.best_found,
        }
    }

    fn to_reducer(&self) -> Self::Reducer {
        FindReducer { match_position: self.match_position }
    }
}

struct FindFolder<'f, ITEM, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    boundary: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
    item: Option<ITEM>,
}

impl<'f, FIND_OP: 'f + Fn(&ITEM) -> bool, ITEM> Folder<ITEM> for FindFolder<'f, ITEM, FIND_OP> {
    type Result = Option<ITEM>;

    fn consume(mut self, item: ITEM) -> Self {
        if (self.find_op)(&item) {
            // This may sometimes set best_found to a worse index than it was
            // before, depending on timing. This means more consumers will
            // continue to run than necessary, but the reducer will still ensure
            // the correct value is returned.
            self.best_found.swap(self.boundary, Ordering::Relaxed);
            self.item = Some(item);
        }
        self
    }

    fn complete(self) -> Self::Result {
        self.item
    }

    fn full(&self) -> bool {
        let best_found = self.best_found.load(Ordering::Relaxed);
        match self.match_position {
            MatchPosition::Leftmost => best_found < self.boundary,
            MatchPosition::Rightmost => best_found > self.boundary,
        }
    }
}

struct FindReducer {
    match_position: MatchPosition
}

impl<ITEM> Reducer<Option<ITEM>> for FindReducer {
    fn reduce(self, left: Option<ITEM>, right: Option<ITEM>) -> Option<ITEM> {
        match self.match_position {
            MatchPosition::Leftmost => left.or(right),
            MatchPosition::Rightmost => right.or(left)
        }
    }
}
<|endoftext|>


<commit_before>//! nock implements a nock interpreter.
// Copyright (2017) Jeremy A. Wall.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use parser::{Noun, ParseError, atom};
use std::error;
use std::fmt;
use std::fmt::Display;

make_error!(NockError, "NockError: {}\n");

impl From<ParseError> for NockError {
    fn from(err: ParseError) -> Self {
        Self::new_with_cause("AST Parse Error", Box::new(err))
    }
}

fn slice_to_noun(nouns: &[Noun]) -> Result<Noun, NockError> {
    if nouns.len() > 1 {
        Ok(Noun::Cell(nouns.iter().cloned().collect()))
    } else if nouns.len() == 1 {
        Ok(nouns[0].clone())
    } else {
        Err(NockError::new("!! Nock Empty Cell"))
    }
}

// true means take the head.
// false means take the tail
fn make_tree_path(addr: u64) -> Vec<bool> {
    // 6 is the head of 3
    // 3 is the tail of 1
    // 1 is the whole tree.

    // 13 is the tail of 6
    // 6 is the head of 3
    // 3 is the tail of 1
    // 1 is the whole tree.
    let mut ret = Vec::new();
    let mut next = addr;
    loop {
        ret.push(next % 2 == 0);
        next = next / 2;
        if next <= 1 {
            break;
        }
    }
    // TODO(jwall): if we could not have to reverse that would be nice.
    ret.iter().rev().cloned().collect()
}

fn fas(subj: &Noun, addr: u64) -> Result<Noun, NockError> {
    if addr == 0 {
        return Err(NockError::new("!! Invalid slot address 0"));
    }
    if addr == 1 {
        return Ok(subj.clone());
    }
    if addr == 2 {
        return Ok(try!(subj.head()).clone());
    }
    if addr == 3 {
        return slice_to_noun(try!(subj.tail()));
    }
    let path = make_tree_path(addr);
    let mut subject = subj.clone();
    for take_head in path {
        subject = if take_head {
            try!(subject.head()).clone()
        } else {
            try!(slice_to_noun(try!(subject.tail())))
        }
    }
    Ok(subject)
}

#[cfg(test)]
#[test]
fn test_simple_fas() {
    // /[1 [531 25 99]] is [531 25 99];
    let cases = vec![(cell!(atom(531), atom(25), atom(99)), 1,
                      cell!(atom(531), atom(25), atom(99))),
                     // /[2 [531 25 99]] is 531;
                     (cell!(atom(531), atom(25), atom(99)), 2,
                      atom(531)),
                     // /[3 [531 25 99]] is [25 99];
                     (cell!(atom(531), atom(25), atom(99)), 3,
                      cell!(atom(25), atom(99))),
                     // /[6 [531 25 99]] is 25;
                     (cell!(atom(531), atom(25), atom(99)), 6,
                      atom(25)),
                     (cell!(atom(531), cell!(atom(25), atom(26)), atom(99)), 6,
                      cell!(atom(25), atom(26))),
                     (cell!(atom(531), cell!(atom(25), atom(26)), atom(99)), 12,
                      atom(25)),
                     (cell!(atom(531), cell!(atom(25), atom(26)), atom(99)), 13,
                      atom(26)),
                     ];
    for (subj, addr, expected) in cases {
        assert_eq!(expected, fas(&subj, addr).unwrap());
    }
    // [12 [531 25 99]] crashes
}

#[cfg(test)]
#[test]
#[should_panic]
fn test_fas_crash() {
    let cell = cell!(atom(531), atom(25), atom(99));
    // We expect an error here so we crash.
    fas(&cell, 12).unwrap();
}

// Returns 1 false for an Noun::Atom and 0 true for a Noun::Cell.
fn wut(noun: Noun) -> Noun {
    match noun {
        Noun::Atom(_) => atom(1),
        Noun::Cell(_) => atom(0),
    }
}

// lus increments a Noun::Atom but crashes for a Noun::Cell.
fn lus(noun: Noun) -> Result<Noun, NockError> {
    match noun {
        Noun::Atom(a) => Ok(atom(a + 1)),
        Noun::Cell(_) => Err(NockError::new("!! Can't increment a cell")),
    }
}

#[cfg(test)]
#[test]
fn test_lus() {
    assert_eq!(lus(atom(1)).expect("Should be able to increment an atom"), atom(2));
}

#[cfg(test)]
#[test]
#[should_panic]
fn test_lus_crash() {
    lus(cell!(atom(1), atom(2))).unwrap();
}

fn cmp_noun(a: &Noun, b: &[Noun]) -> Noun {
    let truthy = atom(0);
    let falsy = atom(1);
    match a {
        &Noun::Cell(ref list) => {
            if b.len() == 1 {
                if let Noun::Cell(ref list) = b[0] {
                    return cmp_noun(a, &list);
                }
            }
            if list.len() != b.len() {
                return falsy;
            }
            for (i, n) in list.iter().enumerate() {
                if cmp_noun(n, &b[i..i+1]) == falsy {
                    return falsy;
                }
            }
            return truthy;
        }
        &Noun::Atom(a) => {
            if b.len() == 1 {
                if let Noun::Atom(b) = b[0] {
                    if a == b {
                        return truthy;
                    }
                }
            }
            return falsy;
        }
    }
}

// tis compares a Noun::Cell's head and tail Nouns for equality.
fn tis(noun: Noun) -> Result<Noun, NockError> {
    match noun {
        Noun::Atom(_) => Err(NockError::new("!! Can't compaire Atom like a cell")),
        Noun::Cell(list) => {
            if list.len() >= 2 {
                Ok(cmp_noun(&list[0], &list[1..]))
            } else {
                Err(NockError::new("!! Can't compare a cell of only one Noun"))
            }
        }
    }
}

#[cfg(test)]
#[test]
fn test_tis() {
    //assert_eq!(
    //    tis(cell!(atom(1), atom(1))).expect("Should be able to compare a Noun::Cell"),
    //        atom(0));
    //assert_eq!(
    //    tis(cell!(atom(0), atom(1))).expect("Should be able to compare a Noun::Cell"),
    //        atom(1));
    //assert_eq!(
    //    tis(cell!(atom(0), cell!(atom(1), atom(2)))).expect("Should be able to compare a Noun::Cell"),
    //        atom(1));
    //assert_eq!(
    //    tis(cell!(cell!(atom(1), atom(2)), cell!(atom(1), atom(2))))
    //        .expect("Should be able to compare a Noun::Cell"),
    //        atom(0));
    assert_eq!(
        tis(cell!(cell!(atom(1), cell!(atom(2), atom(3)), atom(4)),
                  cell!(atom(1), cell!(atom(2), atom(3)), atom(4))))
            .expect("Should be able to compare a Noun::Cell"),
            atom(0));
}

#[cfg(test)]
#[test]
#[should_panic]
fn test_tis_crash_atom() {
    tis(atom(1)).unwrap();
}

#[cfg(test)]
#[test]
#[should_panic]
fn test_tis_crash_cell() {
    tis(cell!(atom(1))).unwrap();
}

// evaluates a nock expression of type [subj formula] or [formula] or atom
pub fn compute(noun: Noun) -> Result<Noun, NockError> {
    match &noun {
        &Noun::Atom(_) => nock_internal(&Noun::Atom(0), noun.clone()),
        &Noun::Cell(ref list) => {
            if list.len() >= 2 {
                nock_internal(try!(noun.head()), try!(slice_to_noun(try!(noun.tail()))))
            } else {
                Err(NockError::new("!! Invalid Nock Expression"))
            }
        }
    }
}

// Evaluates a nock formula against a subj.
fn nock_internal(subj: &Noun, formula: Noun) -> Result<Noun, NockError> {
    match formula {
        Noun::Atom(_) => return Err(NockError::new(format!("!! Nock Infinite Loop"))),
        cell => {
            match try!(cell.head()) {
                &Noun::Atom(a) => {
                    // We expect an instruction from 0 to 10
                    match a {
                        0 => {
                            let tail = try!(slice_to_noun(try!(cell.tail())));
                            if let Noun::Atom(b) = tail {
                                return fas(subj, b);
                            } else {
                                return Err(NockError::new(format!("!! not a slot index {}", tail)));
                            }
                        }
                        1 => {
                            return Ok(try!(slice_to_noun(try!(cell.tail()))));
                        }
                        2 => {
                            return Ok(try!(nock_internal(subj,
                                                         try!(slice_to_noun(try!(cell.tail()))))));
                        }
                        3 => {
                            return Ok(wut(try!(nock_internal(subj,
                                                             try!(slice_to_noun(
                                                    try!(cell.tail())))))));
                        }
                        4 => {
                            let tail_noun = try!(slice_to_noun(try!(cell.tail())));
                            if let Noun::Cell(_) = tail_noun {
                                return Ok(try!(lus(try!(nock_internal(subj, tail_noun)))));
                            }
                            return Ok(try!(lus(tail_noun)));
                        }
                        5 => {
                            return Ok(try!(tis(try!(slice_to_noun(try!(cell.tail()))))));
                        }
                        // macros
                        6 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 3 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            let d = try!(slice_to_noun(&tail[2..]));
                            // *[a 6 b c d]     *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
                            let formula = cell!(atom(2),
                                                // [0 1]
                                                cell!(atom(0), atom(1)),
                                                // 2
                                                atom(2),
                                                // [1 c d]
                                                cell!(atom(1), c, d),
                                                // [1 0]
                                                cell!(atom(1), atom(0)),
                                                // 2
                                                atom(2),
                                                // [1 2 3]
                                                cell!(atom(1), atom(2), atom(3)),
                                                // [1 0]
                                                cell!(atom(1), atom(0)),
                                                // 4 4 b]
                                                atom(4),
                                                atom(4),
                                                b);
                            return nock_internal(subj, formula);
                        }
                        7 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 2 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            // *[a 7 b c] -> *[a 2 b 1 c]
                            let formula = cell!(atom(2), b, atom(1), c);
                            return nock_internal(subj, formula);
                        }
                        8 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 2 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            // *[a 8 b c]       *[a 7 [[7 [0 1] b] 0 1] c]
                            let formula = cell!(atom(7),
                                                cell!(cell!(atom(7), cell!(atom(0), atom(1)), b),
                                                      atom(0),
                                                      atom(1)),
                                                c);
                            return nock_internal(subj, formula);
                        }
                        9 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 2 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            // *[a 9 b c]       *[a 7 c 2 [0 1] 0 b]
                            let formula =
                                cell!(atom(7), c, atom(2), cell!(atom(0), atom(1)), atom(0), b);
                            return nock_internal(subj, formula);
                        }
                        10 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 2 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            match b {
                                Noun::Atom(_) => {
                                    // *[a 10 b c]      *[a c]
                                    // b is discarded.
                                    return nock_internal(subj, c);
                                }
                                Noun::Cell(list) => {
                                    let d = c;
                                    // b is discarded.
                                    let c = try!(slice_to_noun(&list[1..]));
                                    // *[a 10 [b c] d]  *[a 8 c 7 [0 3] d]
                                    let formula =
                                        cell!(atom(8), c, atom(7), cell!(atom(0), atom(3)), d);
                                    return nock_internal(subj, formula);
                                }
                            }
                        }
                        _ => {
                            return Err(NockError::new(format!("!! Unknown Nock instruction {}",
                                                              a)));
                        }
                    }
                }
                head_formula => {
                    let head = try!(nock_internal(subj, head_formula.clone()));
                    let new_formula = try!(slice_to_noun(try!(cell.tail())));
                    let tail_noun = try!(nock_internal(subj, new_formula));
                    return Ok(cell!(head, tail_noun));
                }
            }
        }
    }
}
<commit_msg>Use a VecDeque in our tree path calculation so we can avoid a reverse.<commit_after>//! nock implements a nock interpreter.
// Copyright (2017) Jeremy A. Wall.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use parser::{Noun, ParseError, atom};
use std::error;
use std::fmt;
use std::fmt::Display;
use std::collections::VecDeque;

make_error!(NockError, "NockError: {}\n");

impl From<ParseError> for NockError {
    fn from(err: ParseError) -> Self {
        Self::new_with_cause("AST Parse Error", Box::new(err))
    }
}

fn slice_to_noun(nouns: &[Noun]) -> Result<Noun, NockError> {
    if nouns.len() > 1 {
        Ok(Noun::Cell(nouns.iter().cloned().collect()))
    } else if nouns.len() == 1 {
        Ok(nouns[0].clone())
    } else {
        Err(NockError::new("!! Nock Empty Cell"))
    }
}

// true means take the head.
// false means take the tail
fn make_tree_path(addr: u64) -> Vec<bool> {
    // 6 is the head of 3
    // 3 is the tail of 1
    // 1 is the whole tree.

    // 13 is the tail of 6
    // 6 is the head of 3
    // 3 is the tail of 1
    // 1 is the whole tree.
    let mut ret = VecDeque::new();
    let mut next = addr;
    loop {
        ret.push_front(next % 2 == 0);
        next = next / 2;
        if next <= 1 {
            break;
        }
    }
    let range = 0..ret.len();
    let path = ret.drain(range).collect();
    return path;
}

fn fas(subj: &Noun, addr: u64) -> Result<Noun, NockError> {
    if addr == 0 {
        return Err(NockError::new("!! Invalid slot address 0"));
    }
    if addr == 1 {
        return Ok(subj.clone());
    }
    if addr == 2 {
        return Ok(try!(subj.head()).clone());
    }
    if addr == 3 {
        return slice_to_noun(try!(subj.tail()));
    }
    let path = make_tree_path(addr);
    let mut subject = subj.clone();
    for take_head in path {
        subject = if take_head {
            try!(subject.head()).clone()
        } else {
            try!(slice_to_noun(try!(subject.tail())))
        }
    }
    Ok(subject)
}

#[cfg(test)]
#[test]
fn test_simple_fas() {
    // /[1 [531 25 99]] is [531 25 99];
    let cases = vec![(cell!(atom(531), atom(25), atom(99)), 1,
                      cell!(atom(531), atom(25), atom(99))),
                     // /[2 [531 25 99]] is 531;
                     (cell!(atom(531), atom(25), atom(99)), 2,
                      atom(531)),
                     // /[3 [531 25 99]] is [25 99];
                     (cell!(atom(531), atom(25), atom(99)), 3,
                      cell!(atom(25), atom(99))),
                     // /[6 [531 25 99]] is 25;
                     (cell!(atom(531), atom(25), atom(99)), 6,
                      atom(25)),
                     (cell!(atom(531), cell!(atom(25), atom(26)), atom(99)), 6,
                      cell!(atom(25), atom(26))),
                     (cell!(atom(531), cell!(atom(25), atom(26)), atom(99)), 12,
                      atom(25)),
                     (cell!(atom(531), cell!(atom(25), atom(26)), atom(99)), 13,
                      atom(26)),
                     ];
    for (subj, addr, expected) in cases {
        assert_eq!(expected, fas(&subj, addr).unwrap());
    }
    // [12 [531 25 99]] crashes
}

#[cfg(test)]
#[test]
#[should_panic]
fn test_fas_crash() {
    let cell = cell!(atom(531), atom(25), atom(99));
    // We expect an error here so we crash.
    fas(&cell, 12).unwrap();
}

// Returns 1 false for an Noun::Atom and 0 true for a Noun::Cell.
fn wut(noun: Noun) -> Noun {
    match noun {
        Noun::Atom(_) => atom(1),
        Noun::Cell(_) => atom(0),
    }
}

// lus increments a Noun::Atom but crashes for a Noun::Cell.
fn lus(noun: Noun) -> Result<Noun, NockError> {
    match noun {
        Noun::Atom(a) => Ok(atom(a + 1)),
        Noun::Cell(_) => Err(NockError::new("!! Can't increment a cell")),
    }
}

#[cfg(test)]
#[test]
fn test_lus() {
    assert_eq!(lus(atom(1)).expect("Should be able to increment an atom"), atom(2));
}

#[cfg(test)]
#[test]
#[should_panic]
fn test_lus_crash() {
    lus(cell!(atom(1), atom(2))).unwrap();
}

fn cmp_noun(a: &Noun, b: &[Noun]) -> Noun {
    let truthy = atom(0);
    let falsy = atom(1);
    match a {
        &Noun::Cell(ref list) => {
            if b.len() == 1 {
                if let Noun::Cell(ref list) = b[0] {
                    return cmp_noun(a, &list);
                }
            }
            if list.len() != b.len() {
                return falsy;
            }
            for (i, n) in list.iter().enumerate() {
                if cmp_noun(n, &b[i..i+1]) == falsy {
                    return falsy;
                }
            }
            return truthy;
        }
        &Noun::Atom(a) => {
            if b.len() == 1 {
                if let Noun::Atom(b) = b[0] {
                    if a == b {
                        return truthy;
                    }
                }
            }
            return falsy;
        }
    }
}

// tis compares a Noun::Cell's head and tail Nouns for equality.
fn tis(noun: Noun) -> Result<Noun, NockError> {
    match noun {
        Noun::Atom(_) => Err(NockError::new("!! Can't compaire Atom like a cell")),
        Noun::Cell(list) => {
            if list.len() >= 2 {
                Ok(cmp_noun(&list[0], &list[1..]))
            } else {
                Err(NockError::new("!! Can't compare a cell of only one Noun"))
            }
        }
    }
}

#[cfg(test)]
#[test]
fn test_tis() {
    //assert_eq!(
    //    tis(cell!(atom(1), atom(1))).expect("Should be able to compare a Noun::Cell"),
    //        atom(0));
    //assert_eq!(
    //    tis(cell!(atom(0), atom(1))).expect("Should be able to compare a Noun::Cell"),
    //        atom(1));
    //assert_eq!(
    //    tis(cell!(atom(0), cell!(atom(1), atom(2)))).expect("Should be able to compare a Noun::Cell"),
    //        atom(1));
    //assert_eq!(
    //    tis(cell!(cell!(atom(1), atom(2)), cell!(atom(1), atom(2))))
    //        .expect("Should be able to compare a Noun::Cell"),
    //        atom(0));
    assert_eq!(
        tis(cell!(cell!(atom(1), cell!(atom(2), atom(3)), atom(4)),
                  cell!(atom(1), cell!(atom(2), atom(3)), atom(4))))
            .expect("Should be able to compare a Noun::Cell"),
            atom(0));
}

#[cfg(test)]
#[test]
#[should_panic]
fn test_tis_crash_atom() {
    tis(atom(1)).unwrap();
}

#[cfg(test)]
#[test]
#[should_panic]
fn test_tis_crash_cell() {
    tis(cell!(atom(1))).unwrap();
}

// evaluates a nock expression of type [subj formula] or [formula] or atom
pub fn compute(noun: Noun) -> Result<Noun, NockError> {
    match &noun {
        &Noun::Atom(_) => nock_internal(&Noun::Atom(0), noun.clone()),
        &Noun::Cell(ref list) => {
            if list.len() >= 2 {
                nock_internal(try!(noun.head()), try!(slice_to_noun(try!(noun.tail()))))
            } else {
                Err(NockError::new("!! Invalid Nock Expression"))
            }
        }
    }
}

// Evaluates a nock formula against a subj.
fn nock_internal(subj: &Noun, formula: Noun) -> Result<Noun, NockError> {
    match formula {
        Noun::Atom(_) => return Err(NockError::new(format!("!! Nock Infinite Loop"))),
        cell => {
            match try!(cell.head()) {
                &Noun::Atom(a) => {
                    // We expect an instruction from 0 to 10
                    match a {
                        0 => {
                            let tail = try!(slice_to_noun(try!(cell.tail())));
                            if let Noun::Atom(b) = tail {
                                return fas(subj, b);
                            } else {
                                return Err(NockError::new(format!("!! not a slot index {}", tail)));
                            }
                        }
                        1 => {
                            return Ok(try!(slice_to_noun(try!(cell.tail()))));
                        }
                        2 => {
                            return Ok(try!(nock_internal(subj,
                                                         try!(slice_to_noun(try!(cell.tail()))))));
                        }
                        3 => {
                            return Ok(wut(try!(nock_internal(subj,
                                                             try!(slice_to_noun(
                                                    try!(cell.tail())))))));
                        }
                        4 => {
                            let tail_noun = try!(slice_to_noun(try!(cell.tail())));
                            if let Noun::Cell(_) = tail_noun {
                                return Ok(try!(lus(try!(nock_internal(subj, tail_noun)))));
                            }
                            return Ok(try!(lus(tail_noun)));
                        }
                        5 => {
                            return Ok(try!(tis(try!(slice_to_noun(try!(cell.tail()))))));
                        }
                        // macros
                        6 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 3 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            let d = try!(slice_to_noun(&tail[2..]));
                            // *[a 6 b c d]     *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
                            let formula = cell!(atom(2),
                                                // [0 1]
                                                cell!(atom(0), atom(1)),
                                                // 2
                                                atom(2),
                                                // [1 c d]
                                                cell!(atom(1), c, d),
                                                // [1 0]
                                                cell!(atom(1), atom(0)),
                                                // 2
                                                atom(2),
                                                // [1 2 3]
                                                cell!(atom(1), atom(2), atom(3)),
                                                // [1 0]
                                                cell!(atom(1), atom(0)),
                                                // 4 4 b]
                                                atom(4),
                                                atom(4),
                                                b);
                            return nock_internal(subj, formula);
                        }
                        7 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 2 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            // *[a 7 b c] -> *[a 2 b 1 c]
                            let formula = cell!(atom(2), b, atom(1), c);
                            return nock_internal(subj, formula);
                        }
                        8 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 2 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            // *[a 8 b c]       *[a 7 [[7 [0 1] b] 0 1] c]
                            let formula = cell!(atom(7),
                                                cell!(cell!(atom(7), cell!(atom(0), atom(1)), b),
                                                      atom(0),
                                                      atom(1)),
                                                c);
                            return nock_internal(subj, formula);
                        }
                        9 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 2 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            // *[a 9 b c]       *[a 7 c 2 [0 1] 0 b]
                            let formula =
                                cell!(atom(7), c, atom(2), cell!(atom(0), atom(1)), atom(0), b);
                            return nock_internal(subj, formula);
                        }
                        10 => {
                            let tail = try!(cell.tail());
                            if tail.len() < 2 {
                                return Err(NockError::new("!! Need 3 Nouns for macro 6"));
                            }
                            let b = tail[0].clone();
                            let c = tail[1].clone();
                            match b {
                                Noun::Atom(_) => {
                                    // *[a 10 b c]      *[a c]
                                    // b is discarded.
                                    return nock_internal(subj, c);
                                }
                                Noun::Cell(list) => {
                                    let d = c;
                                    // b is discarded.
                                    let c = try!(slice_to_noun(&list[1..]));
                                    // *[a 10 [b c] d]  *[a 8 c 7 [0 3] d]
                                    let formula =
                                        cell!(atom(8), c, atom(7), cell!(atom(0), atom(3)), d);
                                    return nock_internal(subj, formula);
                                }
                            }
                        }
                        _ => {
                            return Err(NockError::new(format!("!! Unknown Nock instruction {}",
                                                              a)));
                        }
                    }
                }
                head_formula => {
                    let head = try!(nock_internal(subj, head_formula.clone()));
                    let new_formula = try!(slice_to_noun(try!(cell.tail())));
                    let tail_noun = try!(nock_internal(subj, new_formula));
                    return Ok(cell!(head, tail_noun));
                }
            }
        }
    }
}
<|endoftext|>