<commit_before>//! Trap codes describing the reason for a trap.

use core::fmt::{self, Display, Formatter};
use core::str::FromStr;

/// A trap code describing the reason for a trap.
///
/// All trap instructions have an explicit trap code.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum TrapCode {
    /// The current stack space was exhausted.
    ///
    /// On some platforms, a stack overflow may also be indicated by a segmentation fault from the
    /// stack guard page.
    StackOverflow,

    /// A `heap_addr` instruction detected an out-of-bounds error.
    ///
    /// Note that not all out-of-bounds heap accesses are reported this way;
    /// some are detected by a segmentation fault on the heap unmapped or
    /// offset-guard pages.
    HeapOutOfBounds,

    /// A `table_addr` instruction detected an out-of-bounds error.
    TableOutOfBounds,

    /// Other bounds checking error.
    OutOfBounds,

    /// Indirect call to a null table entry.
    IndirectCallToNull,

    /// Signature mismatch on indirect call.
    BadSignature,

    /// An integer arithmetic operation caused an overflow.
    IntegerOverflow,

    /// An integer division by zero.
    IntegerDivisionByZero,

    /// Failed float-to-int conversion.
    BadConversionToInteger,

    /// Code that was supposed to have been unreachable was reached.
    UnreachableCodeReached,

    /// Execution has potentially run too long and may be interrupted.
    /// This trap is resumable.
    Interrupt,

    /// A user-defined trap code.
    User(u16),
}

impl Display for TrapCode {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::TrapCode::*;
        let identifier = match *self {
            StackOverflow => "stk_ovf",
            HeapOutOfBounds => "heap_oob",
            TableOutOfBounds => "table_oob",
            OutOfBounds => "oob",
            IndirectCallToNull => "icall_null",
            BadSignature => "bad_sig",
            IntegerOverflow => "int_ovf",
            IntegerDivisionByZero => "int_divz",
            BadConversionToInteger => "bad_toint",
            UnreachableCodeReached => "unreachable",
            Interrupt => "interrupt",
            User(x) => return write!(f, "user{}", x),
        };
        f.write_str(identifier)
    }
}

impl FromStr for TrapCode {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::TrapCode::*;
        match s {
            "stk_ovf" => Ok(StackOverflow),
            "heap_oob" => Ok(HeapOutOfBounds),
            "table_oob" => Ok(TableOutOfBounds),
            "oob" => Ok(OutOfBounds),
            "icall_null" => Ok(IndirectCallToNull),
            "bad_sig" => Ok(BadSignature),
            "int_ovf" => Ok(IntegerOverflow),
            "int_divz" => Ok(IntegerDivisionByZero),
            "bad_toint" => Ok(BadConversionToInteger),
            "unreachable" => Ok(UnreachableCodeReached),
            "interrupt" => Ok(Interrupt),
            _ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()),
            _ => Err(()),
        }
    }
}

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

    // Everything but user-defined codes.
    const CODES: [TrapCode; 11] = [
        TrapCode::StackOverflow,
        TrapCode::HeapOutOfBounds,
        TrapCode::TableOutOfBounds,
        TrapCode::OutOfBounds,
        TrapCode::IndirectCallToNull,
        TrapCode::BadSignature,
        TrapCode::IntegerOverflow,
        TrapCode::IntegerDivisionByZero,
        TrapCode::BadConversionToInteger,
        TrapCode::UnreachableCodeReached,
        TrapCode::Interrupt,
    ];

    #[test]
    fn display() {
        for r in &CODES {
            let tc = *r;
            assert_eq!(tc.to_string().parse(), Ok(tc));
        }
        assert_eq!("bogus".parse::<TrapCode>(), Err(()));

        assert_eq!(TrapCode::User(17).to_string(), "user17");
        assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
        assert_eq!("user".parse::<TrapCode>(), Err(()));
        assert_eq!("user-1".parse::<TrapCode>(), Err(()));
        assert_eq!("users".parse::<TrapCode>(), Err(()));
    }
}
<commit_msg>Derive serde traits for TrapCode<commit_after>//! Trap codes describing the reason for a trap.

use core::fmt::{self, Display, Formatter};
use core::str::FromStr;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};

/// A trap code describing the reason for a trap.
///
/// All trap instructions have an explicit trap code.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum TrapCode {
    /// The current stack space was exhausted.
    ///
    /// On some platforms, a stack overflow may also be indicated by a segmentation fault from the
    /// stack guard page.
    StackOverflow,

    /// A `heap_addr` instruction detected an out-of-bounds error.
    ///
    /// Note that not all out-of-bounds heap accesses are reported this way;
    /// some are detected by a segmentation fault on the heap unmapped or
    /// offset-guard pages.
    HeapOutOfBounds,

    /// A `table_addr` instruction detected an out-of-bounds error.
    TableOutOfBounds,

    /// Other bounds checking error.
    OutOfBounds,

    /// Indirect call to a null table entry.
    IndirectCallToNull,

    /// Signature mismatch on indirect call.
    BadSignature,

    /// An integer arithmetic operation caused an overflow.
    IntegerOverflow,

    /// An integer division by zero.
    IntegerDivisionByZero,

    /// Failed float-to-int conversion.
    BadConversionToInteger,

    /// Code that was supposed to have been unreachable was reached.
    UnreachableCodeReached,

    /// Execution has potentially run too long and may be interrupted.
    /// This trap is resumable.
    Interrupt,

    /// A user-defined trap code.
    User(u16),
}

impl Display for TrapCode {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::TrapCode::*;
        let identifier = match *self {
            StackOverflow => "stk_ovf",
            HeapOutOfBounds => "heap_oob",
            TableOutOfBounds => "table_oob",
            OutOfBounds => "oob",
            IndirectCallToNull => "icall_null",
            BadSignature => "bad_sig",
            IntegerOverflow => "int_ovf",
            IntegerDivisionByZero => "int_divz",
            BadConversionToInteger => "bad_toint",
            UnreachableCodeReached => "unreachable",
            Interrupt => "interrupt",
            User(x) => return write!(f, "user{}", x),
        };
        f.write_str(identifier)
    }
}

impl FromStr for TrapCode {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::TrapCode::*;
        match s {
            "stk_ovf" => Ok(StackOverflow),
            "heap_oob" => Ok(HeapOutOfBounds),
            "table_oob" => Ok(TableOutOfBounds),
            "oob" => Ok(OutOfBounds),
            "icall_null" => Ok(IndirectCallToNull),
            "bad_sig" => Ok(BadSignature),
            "int_ovf" => Ok(IntegerOverflow),
            "int_divz" => Ok(IntegerDivisionByZero),
            "bad_toint" => Ok(BadConversionToInteger),
            "unreachable" => Ok(UnreachableCodeReached),
            "interrupt" => Ok(Interrupt),
            _ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()),
            _ => Err(()),
        }
    }
}

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

    // Everything but user-defined codes.
    const CODES: [TrapCode; 11] = [
        TrapCode::StackOverflow,
        TrapCode::HeapOutOfBounds,
        TrapCode::TableOutOfBounds,
        TrapCode::OutOfBounds,
        TrapCode::IndirectCallToNull,
        TrapCode::BadSignature,
        TrapCode::IntegerOverflow,
        TrapCode::IntegerDivisionByZero,
        TrapCode::BadConversionToInteger,
        TrapCode::UnreachableCodeReached,
        TrapCode::Interrupt,
    ];

    #[test]
    fn display() {
        for r in &CODES {
            let tc = *r;
            assert_eq!(tc.to_string().parse(), Ok(tc));
        }
        assert_eq!("bogus".parse::<TrapCode>(), Err(()));

        assert_eq!(TrapCode::User(17).to_string(), "user17");
        assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
        assert_eq!("user".parse::<TrapCode>(), Err(()));
        assert_eq!("user-1".parse::<TrapCode>(), Err(()));
        assert_eq!("users".parse::<TrapCode>(), Err(()));
    }
}
<|endoftext|>


<commit_before>use crate::utils::{iter_input_pats, snippet, span_lint, type_is_unsafe_function};
use matches::matches;
use rustc::hir;
use rustc::hir::def::Def;
use rustc::hir::intravisit;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_data_structures::fx::FxHashSet;
use rustc_target::spec::abi::Abi;
use syntax::ast;
use syntax::source_map::Span;

/// **What it does:** Checks for functions with too many parameters.
///
/// **Why is this bad?** Functions with lots of parameters are considered bad
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
/// grouping some parameters into a new type.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
///     ..
/// }
/// ```
declare_clippy_lint! {
    pub TOO_MANY_ARGUMENTS,
    complexity,
    "functions with too many arguments"
}

/// **What it does:** Checks for functions with a large amount of lines.
///
/// **Why is this bad?** Functions with a lot of lines are harder to understand
/// due to having to look at a larger amount of code to understand what the
/// function is doing. Consider splitting the body of the function into
/// multiple functions.
///
/// **Known problems:** None.
///
/// **Example:**
/// ``` rust
/// fn im_too_long() {
/// println!("");
/// // ... 100 more LoC
/// println!("");
/// }
/// ```
declare_clippy_lint! {
    pub TOO_MANY_LINES,
    pedantic,
    "functions with too many lines"
}

/// **What it does:** Checks for public functions that dereferences raw pointer
/// arguments but are not marked unsafe.
///
/// **Why is this bad?** The function should probably be marked `unsafe`, since
/// for an arbitrary raw pointer, there is no way of telling for sure if it is
/// valid.
///
/// **Known problems:**
///
/// * It does not check functions recursively so if the pointer is passed to a
/// private non-`unsafe` function which does the dereferencing, the lint won't
/// trigger.
/// * It only checks for arguments whose type are raw pointers, not raw pointers
/// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
/// `some_argument.get_raw_ptr()`).
///
/// **Example:**
/// ```rust
/// pub fn foo(x: *const u8) {
///     println!("{}", unsafe { *x });
/// }
/// ```
declare_clippy_lint! {
    pub NOT_UNSAFE_PTR_ARG_DEREF,
    correctness,
    "public functions dereferencing raw pointer arguments but not marked `unsafe`"
}

#[derive(Copy, Clone)]
pub struct Functions {
    threshold: u64,
    max_lines: u64,
}

impl Functions {
    pub fn new(threshold: u64, max_lines: u64) -> Self {
        Self { threshold, max_lines }
    }
}

impl LintPass for Functions {
    fn get_lints(&self) -> LintArray {
        lint_array!(TOO_MANY_ARGUMENTS, TOO_MANY_LINES, NOT_UNSAFE_PTR_ARG_DEREF)
    }

    fn name(&self) -> &'static str {
        "Functions"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: intravisit::FnKind<'tcx>,
        decl: &'tcx hir::FnDecl,
        body: &'tcx hir::Body,
        span: Span,
        nodeid: ast::NodeId,
    ) {
        let is_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(nodeid)) {
            matches!(item.node, hir::ItemKind::Impl(_, _, _, _, Some(_), _, _))
        } else {
            false
        };

        let unsafety = match kind {
            hir::intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _, _) => unsafety,
            hir::intravisit::FnKind::Method(_, sig, _, _) => sig.header.unsafety,
            hir::intravisit::FnKind::Closure(_) => return,
        };

        // don't warn for implementations, it's not their fault
        if !is_impl {
            // don't lint extern functions decls, it's not their fault either
            match kind {
                hir::intravisit::FnKind::Method(
                    _,
                    &hir::MethodSig {
                        header: hir::FnHeader { abi: Abi::Rust, .. },
                        ..
                    },
                    _,
                    _,
                )
                | hir::intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _, _) => {
                    self.check_arg_number(cx, decl, span)
                },
                _ => {},
            }
        }

        self.check_raw_ptr(cx, unsafety, decl, body, nodeid);
        self.check_line_number(cx, span);
    }

    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
        if let hir::TraitItemKind::Method(ref sig, ref eid) = item.node {
            // don't lint extern functions decls, it's not their fault
            if sig.header.abi == Abi::Rust {
                self.check_arg_number(cx, &sig.decl, item.span);
            }

            if let hir::TraitMethod::Provided(eid) = *eid {
                let body = cx.tcx.hir().body(eid);
                self.check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.id);
            }
        }
    }
}

impl<'a, 'tcx> Functions {
    fn check_arg_number(self, cx: &LateContext<'_, '_>, decl: &hir::FnDecl, span: Span) {
        let args = decl.inputs.len() as u64;
        if args > self.threshold {
            span_lint(
                cx,
                TOO_MANY_ARGUMENTS,
                span,
                &format!("this function has too many arguments ({}/{})", args, self.threshold),
            );
        }
    }

    fn check_line_number(self, cx: &LateContext<'_, '_>, span: Span) {
        let code_snippet = snippet(cx, span, "..");
        let mut line_count: u64 = 0;
        let mut in_comment = false;
        let mut code_in_line;

        // Skip the surrounding function decl.
        let start_brace_idx = match code_snippet.find("{") {
            Some(i) => i + 1,
            None => 0,
        };
        let end_brace_idx = match code_snippet.find("}") {
            Some(i) => i,
            None => code_snippet.len(),
        };
        let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines();

        for mut line in function_lines {
            code_in_line = false;
            loop {
                line = line.trim_start();
                if line.is_empty() {
                    break;
                }
                if in_comment {
                    match line.find("*/") {
                        Some(i) => {
                            line = &line[i + 2..];
                            in_comment = false;
                            continue;
                        },
                        None => break,
                    }
                } else {
                    let multi_idx = match line.find("/*") {
                        Some(i) => i,
                        None => line.len(),
                    };
                    let single_idx = match line.find("//") {
                        Some(i) => i,
                        None => line.len(),
                    };
                    code_in_line |= multi_idx > 0 && single_idx > 0;
                    // Implies multi_idx is below line.len()
                    if multi_idx < single_idx {
                        line = &line[multi_idx + 2..];
                        in_comment = true;
                        continue;
                    }
                    break;
                }
            }
            if code_in_line {
                line_count += 1;
            }
        }

        if line_count > self.max_lines {
            span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.")
        }
    }

    fn check_raw_ptr(
        self,
        cx: &LateContext<'a, 'tcx>,
        unsafety: hir::Unsafety,
        decl: &'tcx hir::FnDecl,
        body: &'tcx hir::Body,
        nodeid: ast::NodeId,
    ) {
        let expr = &body.value;
        if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(nodeid) {
            let raw_ptrs = iter_input_pats(decl, body)
                .zip(decl.inputs.iter())
                .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
                .collect::<FxHashSet<_>>();

            if !raw_ptrs.is_empty() {
                let tables = cx.tcx.body_tables(body.id());
                let mut v = DerefVisitor {
                    cx,
                    ptrs: raw_ptrs,
                    tables,
                };

                hir::intravisit::walk_expr(&mut v, expr);
            }
        }
    }
}

fn raw_ptr_arg(arg: &hir::Arg, ty: &hir::Ty) -> Option<ast::NodeId> {
    if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.node, &ty.node) {
        Some(id)
    } else {
        None
    }
}

struct DerefVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    ptrs: FxHashSet<ast::NodeId>,
    tables: &'a ty::TypeckTables<'tcx>,
}

impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        match expr.node {
            hir::ExprKind::Call(ref f, ref args) => {
                let ty = self.tables.expr_ty(f);

                if type_is_unsafe_function(self.cx, ty) {
                    for arg in args {
                        self.check_arg(arg);
                    }
                }
            },
            hir::ExprKind::MethodCall(_, _, ref args) => {
                let def_id = self.tables.type_dependent_defs()[expr.hir_id].def_id();
                let base_type = self.cx.tcx.type_of(def_id);

                if type_is_unsafe_function(self.cx, base_type) {
                    for arg in args {
                        self.check_arg(arg);
                    }
                }
            },
            hir::ExprKind::Unary(hir::UnDeref, ref ptr) => self.check_arg(ptr),
            _ => (),
        }

        hir::intravisit::walk_expr(self, expr);
    }
    fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
        intravisit::NestedVisitorMap::None
    }
}

impl<'a, 'tcx: 'a> DerefVisitor<'a, 'tcx> {
    fn check_arg(&self, ptr: &hir::Expr) {
        if let hir::ExprKind::Path(ref qpath) = ptr.node {
            if let Def::Local(id) = self.cx.tables.qpath_def(qpath, ptr.hir_id) {
                if self.ptrs.contains(&id) {
                    span_lint(
                        self.cx,
                        NOT_UNSAFE_PTR_ARG_DEREF,
                        ptr.span,
                        "this public function dereferences a raw pointer but is not marked `unsafe`",
                    );
                }
            }
        }
    }
}
<commit_msg>Skipping check if in external macro.<commit_after>use crate::utils::{iter_input_pats, snippet, span_lint, type_is_unsafe_function};
use matches::matches;
use rustc::hir;
use rustc::hir::def::Def;
use rustc::hir::intravisit;
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_data_structures::fx::FxHashSet;
use rustc_target::spec::abi::Abi;
use syntax::ast;
use syntax::source_map::Span;

/// **What it does:** Checks for functions with too many parameters.
///
/// **Why is this bad?** Functions with lots of parameters are considered bad
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
/// grouping some parameters into a new type.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
///     ..
/// }
/// ```
declare_clippy_lint! {
    pub TOO_MANY_ARGUMENTS,
    complexity,
    "functions with too many arguments"
}

/// **What it does:** Checks for functions with a large amount of lines.
///
/// **Why is this bad?** Functions with a lot of lines are harder to understand
/// due to having to look at a larger amount of code to understand what the
/// function is doing. Consider splitting the body of the function into
/// multiple functions.
///
/// **Known problems:** None.
///
/// **Example:**
/// ``` rust
/// fn im_too_long() {
/// println!("");
/// // ... 100 more LoC
/// println!("");
/// }
/// ```
declare_clippy_lint! {
    pub TOO_MANY_LINES,
    pedantic,
    "functions with too many lines"
}

/// **What it does:** Checks for public functions that dereferences raw pointer
/// arguments but are not marked unsafe.
///
/// **Why is this bad?** The function should probably be marked `unsafe`, since
/// for an arbitrary raw pointer, there is no way of telling for sure if it is
/// valid.
///
/// **Known problems:**
///
/// * It does not check functions recursively so if the pointer is passed to a
/// private non-`unsafe` function which does the dereferencing, the lint won't
/// trigger.
/// * It only checks for arguments whose type are raw pointers, not raw pointers
/// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
/// `some_argument.get_raw_ptr()`).
///
/// **Example:**
/// ```rust
/// pub fn foo(x: *const u8) {
///     println!("{}", unsafe { *x });
/// }
/// ```
declare_clippy_lint! {
    pub NOT_UNSAFE_PTR_ARG_DEREF,
    correctness,
    "public functions dereferencing raw pointer arguments but not marked `unsafe`"
}

#[derive(Copy, Clone)]
pub struct Functions {
    threshold: u64,
    max_lines: u64,
}

impl Functions {
    pub fn new(threshold: u64, max_lines: u64) -> Self {
        Self { threshold, max_lines }
    }
}

impl LintPass for Functions {
    fn get_lints(&self) -> LintArray {
        lint_array!(TOO_MANY_ARGUMENTS, TOO_MANY_LINES, NOT_UNSAFE_PTR_ARG_DEREF)
    }

    fn name(&self) -> &'static str {
        "Functions"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: intravisit::FnKind<'tcx>,
        decl: &'tcx hir::FnDecl,
        body: &'tcx hir::Body,
        span: Span,
        nodeid: ast::NodeId,
    ) {
        let is_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(nodeid)) {
            matches!(item.node, hir::ItemKind::Impl(_, _, _, _, Some(_), _, _))
        } else {
            false
        };

        let unsafety = match kind {
            hir::intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _, _) => unsafety,
            hir::intravisit::FnKind::Method(_, sig, _, _) => sig.header.unsafety,
            hir::intravisit::FnKind::Closure(_) => return,
        };

        // don't warn for implementations, it's not their fault
        if !is_impl {
            // don't lint extern functions decls, it's not their fault either
            match kind {
                hir::intravisit::FnKind::Method(
                    _,
                    &hir::MethodSig {
                        header: hir::FnHeader { abi: Abi::Rust, .. },
                        ..
                    },
                    _,
                    _,
                )
                | hir::intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _, _) => {
                    self.check_arg_number(cx, decl, span)
                },
                _ => {},
            }
        }

        self.check_raw_ptr(cx, unsafety, decl, body, nodeid);
        self.check_line_number(cx, span);
    }

    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
        if let hir::TraitItemKind::Method(ref sig, ref eid) = item.node {
            // don't lint extern functions decls, it's not their fault
            if sig.header.abi == Abi::Rust {
                self.check_arg_number(cx, &sig.decl, item.span);
            }

            if let hir::TraitMethod::Provided(eid) = *eid {
                let body = cx.tcx.hir().body(eid);
                self.check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.id);
            }
        }
    }
}

impl<'a, 'tcx> Functions {
    fn check_arg_number(self, cx: &LateContext<'_, '_>, decl: &hir::FnDecl, span: Span) {
        let args = decl.inputs.len() as u64;
        if args > self.threshold {
            span_lint(
                cx,
                TOO_MANY_ARGUMENTS,
                span,
                &format!("this function has too many arguments ({}/{})", args, self.threshold),
            );
        }
    }

    fn check_line_number(self, cx: &LateContext<'_, '_>, span: Span) {
        if in_external_macro(cx.sess(), span) {
            return;
        }

        let code_snippet = snippet(cx, span, "..");
        let mut line_count: u64 = 0;
        let mut in_comment = false;
        let mut code_in_line;

        // Skip the surrounding function decl.
        let start_brace_idx = match code_snippet.find("{") {
            Some(i) => i + 1,
            None => 0,
        };
        let end_brace_idx = match code_snippet.find("}") {
            Some(i) => i,
            None => code_snippet.len(),
        };
        let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines();

        for mut line in function_lines {
            code_in_line = false;
            loop {
                line = line.trim_start();
                if line.is_empty() {
                    break;
                }
                if in_comment {
                    match line.find("*/") {
                        Some(i) => {
                            line = &line[i + 2..];
                            in_comment = false;
                            continue;
                        },
                        None => break,
                    }
                } else {
                    let multi_idx = match line.find("/*") {
                        Some(i) => i,
                        None => line.len(),
                    };
                    let single_idx = match line.find("//") {
                        Some(i) => i,
                        None => line.len(),
                    };
                    code_in_line |= multi_idx > 0 && single_idx > 0;
                    // Implies multi_idx is below line.len()
                    if multi_idx < single_idx {
                        line = &line[multi_idx + 2..];
                        in_comment = true;
                        continue;
                    }
                    break;
                }
            }
            if code_in_line {
                line_count += 1;
            }
        }

        if line_count > self.max_lines {
            span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.")
        }
    }

    fn check_raw_ptr(
        self,
        cx: &LateContext<'a, 'tcx>,
        unsafety: hir::Unsafety,
        decl: &'tcx hir::FnDecl,
        body: &'tcx hir::Body,
        nodeid: ast::NodeId,
    ) {
        let expr = &body.value;
        if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(nodeid) {
            let raw_ptrs = iter_input_pats(decl, body)
                .zip(decl.inputs.iter())
                .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
                .collect::<FxHashSet<_>>();

            if !raw_ptrs.is_empty() {
                let tables = cx.tcx.body_tables(body.id());
                let mut v = DerefVisitor {
                    cx,
                    ptrs: raw_ptrs,
                    tables,
                };

                hir::intravisit::walk_expr(&mut v, expr);
            }
        }
    }
}

fn raw_ptr_arg(arg: &hir::Arg, ty: &hir::Ty) -> Option<ast::NodeId> {
    if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.node, &ty.node) {
        Some(id)
    } else {
        None
    }
}

struct DerefVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    ptrs: FxHashSet<ast::NodeId>,
    tables: &'a ty::TypeckTables<'tcx>,
}

impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        match expr.node {
            hir::ExprKind::Call(ref f, ref args) => {
                let ty = self.tables.expr_ty(f);

                if type_is_unsafe_function(self.cx, ty) {
                    for arg in args {
                        self.check_arg(arg);
                    }
                }
            },
            hir::ExprKind::MethodCall(_, _, ref args) => {
                let def_id = self.tables.type_dependent_defs()[expr.hir_id].def_id();
                let base_type = self.cx.tcx.type_of(def_id);

                if type_is_unsafe_function(self.cx, base_type) {
                    for arg in args {
                        self.check_arg(arg);
                    }
                }
            },
            hir::ExprKind::Unary(hir::UnDeref, ref ptr) => self.check_arg(ptr),
            _ => (),
        }

        hir::intravisit::walk_expr(self, expr);
    }
    fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
        intravisit::NestedVisitorMap::None
    }
}

impl<'a, 'tcx: 'a> DerefVisitor<'a, 'tcx> {
    fn check_arg(&self, ptr: &hir::Expr) {
        if let hir::ExprKind::Path(ref qpath) = ptr.node {
            if let Def::Local(id) = self.cx.tables.qpath_def(qpath, ptr.hir_id) {
                if self.ptrs.contains(&id) {
                    span_lint(
                        self.cx,
                        NOT_UNSAFE_PTR_ARG_DEREF,
                        ptr.span,
                        "this public function dereferences a raw pointer but is not marked `unsafe`",
                    );
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>#![crate_name = "sam4l"]
#![crate_type = "rlib"]
#![feature(asm,core_intrinsics,concat_idents,const_fn)]
#![no_std]

extern crate common;
extern crate hil;
extern crate process;

mod helpers;

pub mod chip;
pub mod ast;
pub mod dma;
pub mod i2c;
pub mod spi;
pub mod nvic;
pub mod pm;
pub mod gpio;
pub mod usart;
pub mod scif;
pub mod adc;

unsafe extern "C" fn unhandled_interrupt() {
    panic!("Unhandled Interrupt");
}

extern {
    // _estack is not really a function, but it makes the types work
    // You should never actually invoke it!!
    fn _estack();

    // Defined in src/main/main.rs
    fn main();

    // Defined in src/arch/cortex-m4/ctx_switch.S
    fn SVC_Handler();

    static mut _szero : u32;
    static mut _ezero : u32;
    static mut _etext : u32;
    static mut _srelocate : u32;
    static mut _erelocate : u32;
}

#[link_section=".vectors"]
pub static ISR_VECTOR: [Option<unsafe extern fn()>; 96] = [
    // First 16 are defined in the Cortex M4 user guide section 2.3.4

    /* Stack top */     Option::Some(_estack),
    /* Reset */         Option::Some(reset_handler),
    /* NMI */           Option::Some(unhandled_interrupt),
    /* Hard Fault */    Option::Some(hard_fault_handler),
    /* MemManage */     Option::Some(unhandled_interrupt),
    /* BusFault */      Option::Some(unhandled_interrupt),
    /* UsageFault*/     Option::Some(unhandled_interrupt),
    None, None, None, None,
    /* SVC */           Option::Some(SVC_Handler),
    /* DebugMon */      Option::Some(unhandled_interrupt),
    None,
    /* PendSV */        Option::Some(unhandled_interrupt),
    /* SysTick */       Option::Some(unhandled_interrupt),

    // Perhipheral vectors are defined by Atmel in the SAM4L datasheet section
    // 4.7.
    /* HFLASHC */       Option::Some(unhandled_interrupt),
    /* PDCA0 */         Option::Some(dma::PDCA_0_Handler),
    /* PDCA1 */         Option::Some(dma::PDCA_1_Handler),
    /* PDCA2 */         Option::Some(dma::PDCA_2_Handler),
    /* PDCA3..PDCA15 */ None, None, None, None, None, None, None, None, None,
                        None, None, None, None,
    /* CRCCU */         Option::Some(unhandled_interrupt),
    /* USBC */          Option::Some(unhandled_interrupt),
    /* PEVC_TR */       Option::Some(unhandled_interrupt),
    /* PEVC_OV */       Option::Some(unhandled_interrupt),
    /* AESA */          Option::Some(unhandled_interrupt),
    /* PM */            Option::Some(unhandled_interrupt),
    /* SCIF */          Option::Some(unhandled_interrupt),
    /* FREQM */         Option::Some(unhandled_interrupt),
    /* GPIO0 */         Option::Some(gpio::GPIO_0_Handler),
    /* GPIO1 */         Option::Some(gpio::GPIO_1_Handler),
    /* GPIO2 */         Option::Some(gpio::GPIO_2_Handler),
    /* GPIO3 */         Option::Some(gpio::GPIO_3_Handler),
    /* GPIO4 */         Option::Some(gpio::GPIO_4_Handler),
    /* GPIO5 */         Option::Some(gpio::GPIO_5_Handler),
    /* GPIO6 */         Option::Some(gpio::GPIO_6_Handler),
    /* GPIO7 */         Option::Some(gpio::GPIO_7_Handler),
    /* GPIO8 */         Option::Some(gpio::GPIO_8_Handler),
    /* GPIO9 */         Option::Some(gpio::GPIO_9_Handler),
    /* GPIO10 */        Option::Some(gpio::GPIO_10_Handler),
    /* GPIO11 */        Option::Some(gpio::GPIO_11_Handler),
    /* BPM */           Option::Some(unhandled_interrupt),
    /* BSCIF */         Option::Some(unhandled_interrupt),
    /* AST_ALARM */     Option::Some(ast::AST_ALARM_Handler),
    /* AST_PER */       Option::Some(unhandled_interrupt),
    /* AST_OVF */       Option::Some(unhandled_interrupt),
    /* AST_READY */     Option::Some(unhandled_interrupt),
    /* AST_CLKREADY */  Option::Some(unhandled_interrupt),
    /* WDT */           Option::Some(unhandled_interrupt),
    /* EIC1 */          Option::Some(unhandled_interrupt),
    /* EIC2 */          Option::Some(unhandled_interrupt),
    /* EIC3 */          Option::Some(unhandled_interrupt),
    /* EIC4 */          Option::Some(unhandled_interrupt),
    /* EIC5 */          Option::Some(unhandled_interrupt),
    /* EIC6 */          Option::Some(unhandled_interrupt),
    /* EIC7 */          Option::Some(unhandled_interrupt),
    /* EIC8 */          Option::Some(unhandled_interrupt),
    /* IISC */          Option::Some(unhandled_interrupt),
    /* SPI */           Option::Some(unhandled_interrupt),
    /* TC00 */          Option::Some(unhandled_interrupt),
    /* TC01 */          Option::Some(unhandled_interrupt),
    /* TC02 */          Option::Some(unhandled_interrupt),
    /* TC10 */          Option::Some(unhandled_interrupt),
    /* TC11 */          Option::Some(unhandled_interrupt),
    /* TC12 */          Option::Some(unhandled_interrupt),
    /* TWIM0 */         Option::Some(unhandled_interrupt),
    /* TWIS0 */         Option::Some(unhandled_interrupt),
    /* TWIM1 */         Option::Some(unhandled_interrupt),
    /* TWIS1 */         Option::Some(unhandled_interrupt),
    /* USART0 */        Option::Some(unhandled_interrupt),
    /* USART1 */        Option::Some(unhandled_interrupt),
    /* USART2 */        Option::Some(unhandled_interrupt),
    /* USART3 */        Option::Some(usart::USART3_Handler),
    /* ADCIFE */        Option::Some(adc::ADCIFE_Handler),
    /* DACC */          Option::Some(unhandled_interrupt),
    /* ACIFC */         Option::Some(unhandled_interrupt),
    /* ABDACB */        Option::Some(unhandled_interrupt),
    /* TRNG */          Option::Some(unhandled_interrupt),
    /* PARC */          Option::Some(unhandled_interrupt),
    /* CATB */          Option::Some(unhandled_interrupt),
    None,
    /* TWIM2 */         Option::Some(unhandled_interrupt),
    /* TWIM3 */         Option::Some(unhandled_interrupt),
    /* LCDCA */         Option::Some(unhandled_interrupt),
];

unsafe extern "C" fn reset_handler() {

    // Relocate data segment.
    // Assumes data starts right after text segment as specified by the linker
    // file.
    let mut pdest  = &mut _srelocate as *mut u32;
    let pend  = &mut _erelocate as *mut u32;
    let mut psrc = &_etext as *const u32;

    if psrc != pdest {
        while (pdest as *const u32) < pend {
            *pdest = *psrc;
            pdest = pdest.offset(1);
            psrc = psrc.offset(1);
        }
    }

    // Clear the zero segment (BSS)
    let pzero = &_ezero as *const u32;
    pdest = &mut _szero as *mut u32;

    while (pdest as *const u32) < pzero {
        *pdest = 0;
        pdest = pdest.offset(1);
    }

    main();
}

unsafe extern "C" fn hard_fault_handler() {
    use core::intrinsics::offset;

    let faulting_stack: *mut u32;

    asm!(
        "tst    lr, #4                      \n\
         ite    eq                          \n\
         mrseq  r0, msp                     \n\
         mrsne  r0, psp                     "
        : "={r0}"(faulting_stack)
        :
        : "r0"
        :
        );

    let stacked_r0  :u32 = *offset(faulting_stack, 0);
    let stacked_r1  :u32 = *offset(faulting_stack, 1);
    let stacked_r2  :u32 = *offset(faulting_stack, 2);
    let stacked_r3  :u32 = *offset(faulting_stack, 3);
    let stacked_r12 :u32 = *offset(faulting_stack, 4);
    let stacked_lr  :u32 = *offset(faulting_stack, 5);
    let stacked_pc  :u32 = *offset(faulting_stack, 6);
    let stacked_prs :u32 = *offset(faulting_stack, 7);

    panic!("HardFault.\n\
           \tr0  0x{:x}\n\
           \tr1  0x{:x}\n\
           \tr2  0x{:x}\n\
           \tr3  0x{:x}\n\
           \tr12 0x{:x}\n\
           \tlr  0x{:x}\n\
           \tpc  0x{:x}\n\
           \tprs 0x{:x}\n\
           ", stacked_r0, stacked_r1, stacked_r2, stacked_r3,
           stacked_r12, stacked_lr, stacked_pc, stacked_prs);
}

<commit_msg>Print which unhandled interrupt panic'd<commit_after>#![crate_name = "sam4l"]
#![crate_type = "rlib"]
#![feature(asm,core_intrinsics,concat_idents,const_fn)]
#![no_std]

extern crate common;
extern crate hil;
extern crate process;

mod helpers;

pub mod chip;
pub mod ast;
pub mod dma;
pub mod i2c;
pub mod spi;
pub mod nvic;
pub mod pm;
pub mod gpio;
pub mod usart;
pub mod scif;
pub mod adc;

unsafe extern "C" fn unhandled_interrupt() {
    let mut interrupt_number: u32;

    // IPSR[8:0] holds the currently active interrupt
    asm!(
        "mrs    r0, ipsr                    "
        : "={r0}"(interrupt_number)
        :
        : "r0"
        :
        );

    interrupt_number = interrupt_number & 0x1ff;

    panic!("Unhandled Interrupt. ISR {} is active.", interrupt_number);
}

extern {
    // _estack is not really a function, but it makes the types work
    // You should never actually invoke it!!
    fn _estack();

    // Defined in src/main/main.rs
    fn main();

    // Defined in src/arch/cortex-m4/ctx_switch.S
    fn SVC_Handler();

    static mut _szero : u32;
    static mut _ezero : u32;
    static mut _etext : u32;
    static mut _srelocate : u32;
    static mut _erelocate : u32;
}

#[link_section=".vectors"]
pub static ISR_VECTOR: [Option<unsafe extern fn()>; 96] = [
    // First 16 are defined in the Cortex M4 user guide section 2.3.4

    /* Stack top */     Option::Some(_estack),
    /* Reset */         Option::Some(reset_handler),
    /* NMI */           Option::Some(unhandled_interrupt),
    /* Hard Fault */    Option::Some(hard_fault_handler),
    /* MemManage */     Option::Some(unhandled_interrupt),
    /* BusFault */      Option::Some(unhandled_interrupt),
    /* UsageFault*/     Option::Some(unhandled_interrupt),
    None, None, None, None,
    /* SVC */           Option::Some(SVC_Handler),
    /* DebugMon */      Option::Some(unhandled_interrupt),
    None,
    /* PendSV */        Option::Some(unhandled_interrupt),
    /* SysTick */       Option::Some(unhandled_interrupt),

    // Perhipheral vectors are defined by Atmel in the SAM4L datasheet section
    // 4.7.
    /* HFLASHC */       Option::Some(unhandled_interrupt),
    /* PDCA0 */         Option::Some(dma::PDCA_0_Handler),
    /* PDCA1 */         Option::Some(dma::PDCA_1_Handler),
    /* PDCA2 */         Option::Some(dma::PDCA_2_Handler),
    /* PDCA3..PDCA15 */ None, None, None, None, None, None, None, None, None,
                        None, None, None, None,
    /* CRCCU */         Option::Some(unhandled_interrupt),
    /* USBC */          Option::Some(unhandled_interrupt),
    /* PEVC_TR */       Option::Some(unhandled_interrupt),
    /* PEVC_OV */       Option::Some(unhandled_interrupt),
    /* AESA */          Option::Some(unhandled_interrupt),
    /* PM */            Option::Some(unhandled_interrupt),
    /* SCIF */          Option::Some(unhandled_interrupt),
    /* FREQM */         Option::Some(unhandled_interrupt),
    /* GPIO0 */         Option::Some(gpio::GPIO_0_Handler),
    /* GPIO1 */         Option::Some(gpio::GPIO_1_Handler),
    /* GPIO2 */         Option::Some(gpio::GPIO_2_Handler),
    /* GPIO3 */         Option::Some(gpio::GPIO_3_Handler),
    /* GPIO4 */         Option::Some(gpio::GPIO_4_Handler),
    /* GPIO5 */         Option::Some(gpio::GPIO_5_Handler),
    /* GPIO6 */         Option::Some(gpio::GPIO_6_Handler),
    /* GPIO7 */         Option::Some(gpio::GPIO_7_Handler),
    /* GPIO8 */         Option::Some(gpio::GPIO_8_Handler),
    /* GPIO9 */         Option::Some(gpio::GPIO_9_Handler),
    /* GPIO10 */        Option::Some(gpio::GPIO_10_Handler),
    /* GPIO11 */        Option::Some(gpio::GPIO_11_Handler),
    /* BPM */           Option::Some(unhandled_interrupt),
    /* BSCIF */         Option::Some(unhandled_interrupt),
    /* AST_ALARM */     Option::Some(ast::AST_ALARM_Handler),
    /* AST_PER */       Option::Some(unhandled_interrupt),
    /* AST_OVF */       Option::Some(unhandled_interrupt),
    /* AST_READY */     Option::Some(unhandled_interrupt),
    /* AST_CLKREADY */  Option::Some(unhandled_interrupt),
    /* WDT */           Option::Some(unhandled_interrupt),
    /* EIC1 */          Option::Some(unhandled_interrupt),
    /* EIC2 */          Option::Some(unhandled_interrupt),
    /* EIC3 */          Option::Some(unhandled_interrupt),
    /* EIC4 */          Option::Some(unhandled_interrupt),
    /* EIC5 */          Option::Some(unhandled_interrupt),
    /* EIC6 */          Option::Some(unhandled_interrupt),
    /* EIC7 */          Option::Some(unhandled_interrupt),
    /* EIC8 */          Option::Some(unhandled_interrupt),
    /* IISC */          Option::Some(unhandled_interrupt),
    /* SPI */           Option::Some(unhandled_interrupt),
    /* TC00 */          Option::Some(unhandled_interrupt),
    /* TC01 */          Option::Some(unhandled_interrupt),
    /* TC02 */          Option::Some(unhandled_interrupt),
    /* TC10 */          Option::Some(unhandled_interrupt),
    /* TC11 */          Option::Some(unhandled_interrupt),
    /* TC12 */          Option::Some(unhandled_interrupt),
    /* TWIM0 */         Option::Some(unhandled_interrupt),
    /* TWIS0 */         Option::Some(unhandled_interrupt),
    /* TWIM1 */         Option::Some(unhandled_interrupt),
    /* TWIS1 */         Option::Some(unhandled_interrupt),
    /* USART0 */        Option::Some(unhandled_interrupt),
    /* USART1 */        Option::Some(unhandled_interrupt),
    /* USART2 */        Option::Some(unhandled_interrupt),
    /* USART3 */        Option::Some(usart::USART3_Handler),
    /* ADCIFE */        Option::Some(adc::ADCIFE_Handler),
    /* DACC */          Option::Some(unhandled_interrupt),
    /* ACIFC */         Option::Some(unhandled_interrupt),
    /* ABDACB */        Option::Some(unhandled_interrupt),
    /* TRNG */          Option::Some(unhandled_interrupt),
    /* PARC */          Option::Some(unhandled_interrupt),
    /* CATB */          Option::Some(unhandled_interrupt),
    None,
    /* TWIM2 */         Option::Some(unhandled_interrupt),
    /* TWIM3 */         Option::Some(unhandled_interrupt),
    /* LCDCA */         Option::Some(unhandled_interrupt),
];

unsafe extern "C" fn reset_handler() {

    // Relocate data segment.
    // Assumes data starts right after text segment as specified by the linker
    // file.
    let mut pdest  = &mut _srelocate as *mut u32;
    let pend  = &mut _erelocate as *mut u32;
    let mut psrc = &_etext as *const u32;

    if psrc != pdest {
        while (pdest as *const u32) < pend {
            *pdest = *psrc;
            pdest = pdest.offset(1);
            psrc = psrc.offset(1);
        }
    }

    // Clear the zero segment (BSS)
    let pzero = &_ezero as *const u32;
    pdest = &mut _szero as *mut u32;

    while (pdest as *const u32) < pzero {
        *pdest = 0;
        pdest = pdest.offset(1);
    }

    main();
}

unsafe extern "C" fn hard_fault_handler() {
    use core::intrinsics::offset;

    let faulting_stack: *mut u32;

    asm!(
        "tst    lr, #4                      \n\
         ite    eq                          \n\
         mrseq  r0, msp                     \n\
         mrsne  r0, psp                     "
        : "={r0}"(faulting_stack)
        :
        : "r0"
        :
        );

    let stacked_r0  :u32 = *offset(faulting_stack, 0);
    let stacked_r1  :u32 = *offset(faulting_stack, 1);
    let stacked_r2  :u32 = *offset(faulting_stack, 2);
    let stacked_r3  :u32 = *offset(faulting_stack, 3);
    let stacked_r12 :u32 = *offset(faulting_stack, 4);
    let stacked_lr  :u32 = *offset(faulting_stack, 5);
    let stacked_pc  :u32 = *offset(faulting_stack, 6);
    let stacked_prs :u32 = *offset(faulting_stack, 7);

    panic!("HardFault.\n\
           \tr0  0x{:x}\n\
           \tr1  0x{:x}\n\
           \tr2  0x{:x}\n\
           \tr3  0x{:x}\n\
           \tr12 0x{:x}\n\
           \tlr  0x{:x}\n\
           \tpc  0x{:x}\n\
           \tprs 0x{:x}\n\
           ", stacked_r0, stacked_r1, stacked_r2, stacked_r3,
           stacked_r12, stacked_lr, stacked_pc, stacked_prs);
}

<|endoftext|>


<commit_before>// Copyright © 2018 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

mod item;

use std::cell::{Cell, RefCell};

use failure::Error;

use self::item::{Enum, EnumItem, Item, Slider, TextField, Toggle};

#[derive(Clone)]
pub enum MenuState {
    /// Menu is inactive.
    Inactive,

    /// Menu is active. `index` indicates the currently selected element.
    Active { index: usize },

    /// A submenu of this menu is active. `index` indicates the active submenu.
    InSubMenu { index: usize },
}

pub struct Menu {
    items: Vec<NamedMenuItem>,
    state: RefCell<MenuState>,
}

impl Menu {
    /// Returns a reference to the active submenu of this menu and its parent.
    fn active_submenu_and_parent(&self) -> Result<(&Menu, Option<&Menu>), Error> {
        let mut m = self;
        let mut m_parent = None;

        while let MenuState::InSubMenu { index } = *m.state.borrow() {
            match m.items[index].item {
                Item::Submenu(ref s) => {
                    m_parent = Some(m);
                    m = s;
                }
                _ => bail!("Menu state points to invalid submenu"),
            }
        }

        Ok((m, m_parent))
    }

    /// Return a reference to the active submenu of this menu
    fn active_submenu(&self) -> Result<&Menu, Error> {
        let (m, _) = self.active_submenu_and_parent()?;
        Ok(m)
    }

    /// Return a reference to the parent of the active submenu of this menu.
    ///
    /// If this is the root menu, returns None.
    fn active_submenu_parent(&self) -> Result<Option<&Menu>, Error> {
        let (_, m_parent) = self.active_submenu_and_parent()?;
        Ok(m_parent)
    }

    /// Select the next element of this Menu.
    pub fn next(&self) -> Result<(), Error> {
        let m = self.active_submenu()?;

        let s = m.state.borrow().clone();
        if let MenuState::Active { index } = s {
            m.state.replace(MenuState::Active {
                index: (index + 1) % m.items.len(),
            });
        } else {
            bail!("Selected menu is inactive (invariant violation)");
        }

        Ok(())
    }

    /// Select the previous element of this Menu.
    pub fn prev(&self) -> Result<(), Error> {
        let m = self.active_submenu()?;

        let s = m.state.borrow().clone();
        if let MenuState::Active { index } = s {
            m.state.replace(MenuState::Active {
                index: (index - 1) % m.items.len(),
            });
        } else {
            bail!("Selected menu is inactive (invariant violation)");
        }

        Ok(())
    }

    /// Return a reference to the currently selected menu item.
    pub fn selected(&self) -> Result<&Item, Error> {
        let m = self.active_submenu()?;

        if let MenuState::Active { index } = *m.state.borrow() {
            return Ok(&m.items[index].item);
        } else {
            bail!("Active menu in invalid state (invariant violation)")
        }
    }

    /// Activate the currently selected menu item.
    ///
    /// If this item is a `Menu`, sets the active (sub)menu's state to
    /// `MenuState::InSubMenu` and the selected submenu's state to
    /// `MenuState::Active`.
    ///
    /// If this item is an `Action`, executes the function contained in the
    /// `Action`.
    pub fn activate(&self) -> Result<(), Error> {
        let m = self.active_submenu()?;

        let s = m.state.borrow().clone();
        if let MenuState::Active { index } = s {
            match m.items[index].item {
                Item::Submenu(ref submenu) => {
                    m.state.replace(MenuState::InSubMenu { index });
                    submenu.state.replace(MenuState::Active { index: 0 });
                }

                _ => unimplemented!(),
            }
        }

        Ok(())
    }

    /// Return `true` if the root menu is active, `false` otherwise.
    pub fn at_root(&self) -> bool {
        match *self.state.borrow() {
            MenuState::Active { .. } => true,
            _ => false,
        }
    }

    /// Deactivate the active menu and activate its parent
    pub fn back(&self) -> Result<(), Error> {
        if self.at_root() {
            bail!("Cannot back out of root menu!");
        }

        let (m, m_parent) = self.active_submenu_and_parent()?;
        m.state.replace(MenuState::Inactive);

        match m_parent {
            Some(mp) => {
                let s = mp.state.borrow().clone();
                match s {
                    MenuState::InSubMenu { index } => mp.state.replace(MenuState::Active { index }),
                    _ => unreachable!(),
                };
            }

            None => unreachable!(),
        }

        Ok(())
    }
}

pub struct MenuBuilder {
    items: Vec<NamedMenuItem>,
}

impl MenuBuilder {
    pub fn new() -> MenuBuilder {
        MenuBuilder { items: Vec::new() }
    }

    pub fn build(self) -> Menu {
        // deactivate all child menus
        for item in self.items.iter() {
            if let Item::Submenu(ref m) = item.item {
                m.state.replace(MenuState::Inactive);
            }
        }

        Menu {
            items: self.items,
            state: RefCell::new(MenuState::Active { index: 0 }),
        }
    }

    pub fn add_submenu<S>(mut self, name: S, submenu: Menu) -> MenuBuilder
    where
        S: AsRef<str>,
    {
        self.items
            .push(NamedMenuItem::new(name, Item::Submenu(submenu)));
        self
    }

    pub fn add_action<S>(mut self, name: S, action: Box<Fn()>) -> MenuBuilder
    where
        S: AsRef<str>,
    {
        self.items
            .push(NamedMenuItem::new(name, Item::Action(action)));
        self
    }

    pub fn add_toggle<S>(mut self, name: S, init: bool, on_toggle: Box<Fn(bool)>) -> MenuBuilder
    where
        S: AsRef<str>,
    {
        self.items.push(NamedMenuItem::new(
            name,
            Item::Toggle(Toggle::new(init, on_toggle)),
        ));
        self
    }

    pub fn add_enum<S, E>(mut self, name: S, items: E, init: usize) -> Result<MenuBuilder, Error>
    where
        S: AsRef<str>,
        E: Into<Vec<EnumItem>>,
    {
        self.items.push(NamedMenuItem::new(
            name,
            Item::Enum(Enum::new(init, items.into())?),
        ));
        Ok(self)
    }

    pub fn add_slider<S>(
        mut self,
        name: S,
        min: f32,
        max: f32,
        steps: usize,
        init: usize,
        on_select: Box<Fn(f32)>,
    ) -> Result<MenuBuilder, Error>
    where
        S: AsRef<str>,
    {
        self.items.push(NamedMenuItem::new(
            name,
            Item::Slider(Slider::new(min, max, steps, init, on_select)?),
        ));
        Ok(self)
    }

    pub fn add_text_field<S>(
        mut self,
        name: S,
        default: Option<S>,
        max_len: Option<usize>,
        on_update: Box<Fn(&str)>,
    ) -> Result<MenuBuilder, Error>
    where
        S: AsRef<str>,
    {
        self.items.push(NamedMenuItem::new(
            name,
            Item::TextField(TextField::new(default, max_len, on_update)?),
        ));
        Ok(self)
    }
}

struct NamedMenuItem {
    name: String,
    item: Item,
}

impl NamedMenuItem {
    fn new<S>(name: S, item: Item) -> NamedMenuItem
    where
        S: AsRef<str>,
    {
        NamedMenuItem {
            name: name.as_ref().to_string(),
            item,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::cell::RefCell;
    use std::rc::Rc;

    fn is_inactive(state: &MenuState) -> bool {
        match state {
            MenuState::Inactive => true,
            _ => false,
        }
    }

    fn is_active(state: &MenuState) -> bool {
        match state {
            MenuState::Active { .. } => true,
            _ => false,
        }
    }

    fn is_insubmenu(state: &MenuState) -> bool {
        match state {
            MenuState::InSubMenu { .. } => true,
            _ => false,
        }
    }

    #[test]
    fn test_menu_builder() {
        let action_target = Rc::new(Cell::new(false));
        let action_target_handle = action_target.clone();

        let m = MenuBuilder::new()
            .add_action("action", Box::new(move || action_target_handle.set(true)))
            .build();

        // TODO
    }

    #[test]
    fn test_menu_active_submenu() {
        let menu = MenuBuilder::new()
            .add_submenu(
                "menu_1",
                MenuBuilder::new()
                    .add_action("action_1", Box::new(|| ()))
                    .build(),
            )
            .add_submenu(
                "menu_2",
                MenuBuilder::new()
                    .add_action("action_2", Box::new(|| ()))
                    .build(),
            )
            .build();

        let m = &menu;
        let m1 = match m.items[0].item {
            Item::Submenu(ref m1i) => m1i,
            _ => unreachable!(),
        };
        let m2 = match m.items[1].item {
            Item::Submenu(ref m2i) => m2i,
            _ => unreachable!(),
        };

        assert!(is_active(&m.state.borrow()));
        assert!(is_inactive(&m1.state.borrow()));
        assert!(is_inactive(&m2.state.borrow()));

        // enter m1
        m.activate().unwrap();
        assert!(is_insubmenu(&m.state.borrow()));
        assert!(is_active(&m1.state.borrow()));
        assert!(is_inactive(&m2.state.borrow()));

        // exit m1
        m.back().unwrap();
        assert!(is_active(&m.state.borrow()));
        assert!(is_inactive(&m1.state.borrow()));
        assert!(is_inactive(&m2.state.borrow()));

        // enter m2
        m.next().unwrap();
        m.activate().unwrap();
        assert!(is_insubmenu(&m.state.borrow()));
        assert!(is_inactive(&m1.state.borrow()));
        assert!(is_active(&m2.state.borrow()));
    }
}
<commit_msg>Add Layout for predefined menu graphics<commit_after>// Copyright © 2018 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

mod item;

use std::cell::{Cell, RefCell};

use failure::Error;

use self::item::{Enum, EnumItem, Item, Slider, TextField, Toggle};

#[derive(Clone)]
pub enum MenuState {
    /// Menu is inactive.
    Inactive,

    /// Menu is active. `index` indicates the currently selected element.
    Active { index: usize },

    /// A submenu of this menu is active. `index` indicates the active submenu.
    InSubMenu { index: usize },
}

struct Layout {
    gfx_name: String,
    items: Vec<NamedMenuItem>,
    cursor_pos: Vec<(u32, u32)>,
}

enum Items {
    List(Vec<NamedMenuItem>),
    Layout(Layout),
}

impl Items {
    pub fn item(&self, i: usize) -> &Item {
        match self {
            Items::List(ref list) => &list[i].item,
            Items::Layout(ref layout) => &layout.items[i].item,
        }
    }

    pub fn len(&self) -> usize {
        match self {
            Items::List(ref list) => list.len(),
            Items::Layout(ref layout) => layout.items.len(),
        }
    }
}

pub struct Menu {
    items: Items,
    state: RefCell<MenuState>,
}

impl Menu {
    /// Returns a reference to the active submenu of this menu and its parent.
    fn active_submenu_and_parent(&self) -> Result<(&Menu, Option<&Menu>), Error> {
        let mut m = self;
        let mut m_parent = None;

        while let MenuState::InSubMenu { index } = *m.state.borrow() {
            match m.items.item(index) {
                Item::Submenu(ref s) => {
                    m_parent = Some(m);
                    m = s;
                }
                _ => bail!("Menu state points to invalid submenu"),
            }
        }

        Ok((m, m_parent))
    }

    /// Return a reference to the active submenu of this menu
    fn active_submenu(&self) -> Result<&Menu, Error> {
        let (m, _) = self.active_submenu_and_parent()?;
        Ok(m)
    }

    /// Return a reference to the parent of the active submenu of this menu.
    ///
    /// If this is the root menu, returns None.
    fn active_submenu_parent(&self) -> Result<Option<&Menu>, Error> {
        let (_, m_parent) = self.active_submenu_and_parent()?;
        Ok(m_parent)
    }

    /// Select the next element of this Menu.
    pub fn next(&self) -> Result<(), Error> {
        let m = self.active_submenu()?;

        let s = m.state.borrow().clone();
        if let MenuState::Active { index } = s {
            m.state.replace(MenuState::Active {
                index: (index + 1) % m.items.len(),
            });
        } else {
            bail!("Selected menu is inactive (invariant violation)");
        }

        Ok(())
    }

    /// Select the previous element of this Menu.
    pub fn prev(&self) -> Result<(), Error> {
        let m = self.active_submenu()?;

        let s = m.state.borrow().clone();
        if let MenuState::Active { index } = s {
            m.state.replace(MenuState::Active {
                index: (index - 1) % m.items.len(),
            });
        } else {
            bail!("Selected menu is inactive (invariant violation)");
        }

        Ok(())
    }

    /// Return a reference to the currently selected menu item.
    pub fn selected(&self) -> Result<&Item, Error> {
        let m = self.active_submenu()?;

        if let MenuState::Active { index } = *m.state.borrow() {
            return Ok(&m.items.item(index));
        } else {
            bail!("Active menu in invalid state (invariant violation)")
        }
    }

    /// Activate the currently selected menu item.
    ///
    /// If this item is a `Menu`, sets the active (sub)menu's state to
    /// `MenuState::InSubMenu` and the selected submenu's state to
    /// `MenuState::Active`.
    ///
    /// If this item is an `Action`, executes the function contained in the
    /// `Action`.
    pub fn activate(&self) -> Result<(), Error> {
        let m = self.active_submenu()?;

        let s = m.state.borrow().clone();
        if let MenuState::Active { index } = s {
            match m.items.item(index) {
                Item::Submenu(ref submenu) => {
                    m.state.replace(MenuState::InSubMenu { index });
                    submenu.state.replace(MenuState::Active { index: 0 });
                }

                _ => unimplemented!(),
            }
        }

        Ok(())
    }

    /// Return `true` if the root menu is active, `false` otherwise.
    pub fn at_root(&self) -> bool {
        match *self.state.borrow() {
            MenuState::Active { .. } => true,
            _ => false,
        }
    }

    /// Deactivate the active menu and activate its parent
    pub fn back(&self) -> Result<(), Error> {
        if self.at_root() {
            bail!("Cannot back out of root menu!");
        }

        let (m, m_parent) = self.active_submenu_and_parent()?;
        m.state.replace(MenuState::Inactive);

        match m_parent {
            Some(mp) => {
                let s = mp.state.borrow().clone();
                match s {
                    MenuState::InSubMenu { index } => mp.state.replace(MenuState::Active { index }),
                    _ => unreachable!(),
                };
            }

            None => unreachable!(),
        }

        Ok(())
    }
}

pub struct MenuBuilder {
    items: Vec<NamedMenuItem>,
}

impl MenuBuilder {
    pub fn new() -> MenuBuilder {
        MenuBuilder { items: Vec::new() }
    }

    pub fn build(self) -> Menu {
        // deactivate all child menus
        for item in self.items.iter() {
            if let Item::Submenu(ref m) = item.item {
                m.state.replace(MenuState::Inactive);
            }
        }

        Menu {
            items: Items::List(self.items),
            state: RefCell::new(MenuState::Active { index: 0 }),
        }
    }

    pub fn add_submenu<S>(mut self, name: S, submenu: Menu) -> MenuBuilder
    where
        S: AsRef<str>,
    {
        self.items
            .push(NamedMenuItem::new(name, Item::Submenu(submenu)));
        self
    }

    pub fn add_action<S>(mut self, name: S, action: Box<Fn()>) -> MenuBuilder
    where
        S: AsRef<str>,
    {
        self.items
            .push(NamedMenuItem::new(name, Item::Action(action)));
        self
    }

    pub fn add_toggle<S>(mut self, name: S, init: bool, on_toggle: Box<Fn(bool)>) -> MenuBuilder
    where
        S: AsRef<str>,
    {
        self.items.push(NamedMenuItem::new(
            name,
            Item::Toggle(Toggle::new(init, on_toggle)),
        ));
        self
    }

    pub fn add_enum<S, E>(mut self, name: S, items: E, init: usize) -> Result<MenuBuilder, Error>
    where
        S: AsRef<str>,
        E: Into<Vec<EnumItem>>,
    {
        self.items.push(NamedMenuItem::new(
            name,
            Item::Enum(Enum::new(init, items.into())?),
        ));
        Ok(self)
    }

    pub fn add_slider<S>(
        mut self,
        name: S,
        min: f32,
        max: f32,
        steps: usize,
        init: usize,
        on_select: Box<Fn(f32)>,
    ) -> Result<MenuBuilder, Error>
    where
        S: AsRef<str>,
    {
        self.items.push(NamedMenuItem::new(
            name,
            Item::Slider(Slider::new(min, max, steps, init, on_select)?),
        ));
        Ok(self)
    }

    pub fn add_text_field<S>(
        mut self,
        name: S,
        default: Option<S>,
        max_len: Option<usize>,
        on_update: Box<Fn(&str)>,
    ) -> Result<MenuBuilder, Error>
    where
        S: AsRef<str>,
    {
        self.items.push(NamedMenuItem::new(
            name,
            Item::TextField(TextField::new(default, max_len, on_update)?),
        ));
        Ok(self)
    }
}

struct NamedMenuItem {
    name: String,
    item: Item,
}

impl NamedMenuItem {
    fn new<S>(name: S, item: Item) -> NamedMenuItem
    where
        S: AsRef<str>,
    {
        NamedMenuItem {
            name: name.as_ref().to_string(),
            item,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::cell::RefCell;
    use std::rc::Rc;

    fn is_inactive(state: &MenuState) -> bool {
        match state {
            MenuState::Inactive => true,
            _ => false,
        }
    }

    fn is_active(state: &MenuState) -> bool {
        match state {
            MenuState::Active { .. } => true,
            _ => false,
        }
    }

    fn is_insubmenu(state: &MenuState) -> bool {
        match state {
            MenuState::InSubMenu { .. } => true,
            _ => false,
        }
    }

    #[test]
    fn test_menu_builder() {
        let action_target = Rc::new(Cell::new(false));
        let action_target_handle = action_target.clone();

        let m = MenuBuilder::new()
            .add_action("action", Box::new(move || action_target_handle.set(true)))
            .build();

        // TODO
    }

    #[test]
    fn test_menu_active_submenu() {
        let menu = MenuBuilder::new()
            .add_submenu(
                "menu_1",
                MenuBuilder::new()
                    .add_action("action_1", Box::new(|| ()))
                    .build(),
            )
            .add_submenu(
                "menu_2",
                MenuBuilder::new()
                    .add_action("action_2", Box::new(|| ()))
                    .build(),
            )
            .build();

        let m = &menu;
        let m1 = match m.items.item(0) {
            Item::Submenu(ref m1i) => m1i,
            _ => unreachable!(),
        };
        let m2 = match m.items.item(1) {
            Item::Submenu(ref m2i) => m2i,
            _ => unreachable!(),
        };

        assert!(is_active(&m.state.borrow()));
        assert!(is_inactive(&m1.state.borrow()));
        assert!(is_inactive(&m2.state.borrow()));

        // enter m1
        m.activate().unwrap();
        assert!(is_insubmenu(&m.state.borrow()));
        assert!(is_active(&m1.state.borrow()));
        assert!(is_inactive(&m2.state.borrow()));

        // exit m1
        m.back().unwrap();
        assert!(is_active(&m.state.borrow()));
        assert!(is_inactive(&m1.state.borrow()));
        assert!(is_inactive(&m2.state.borrow()));

        // enter m2
        m.next().unwrap();
        m.activate().unwrap();
        assert!(is_insubmenu(&m.state.borrow()));
        assert!(is_inactive(&m1.state.borrow()));
        assert!(is_active(&m2.state.borrow()));
    }
}
<|endoftext|>


<commit_before>
//use ast::Ast;
use ast::ExprAst;
use ast::StrNode;
use ast::IntNode;
use ast::SymbolNode;
use ast::BoolNode;
use ast::ProcNode;

#[deriving(Clone, PartialEq)]
pub struct Env {
    pub vars: Vec<ExprAst>,
    pub vals: Vec<ExprAst>,
    pub next: Option<Box<Env>>
}

// macro_rules! def_proc {
//     ($type_str:expr, $func_name:ident) => (

#[allow(dead_code)]
impl Env {
    pub fn new() -> Env {
        let mut res = Env {
            vars: vec![],
            vals: vec![],
            next: None
        };
        res.setup();
        res
    }

    pub fn def_var(&mut self, var: ExprAst, val: ExprAst) {
        self.add_bingding(var, val);
    }

    pub fn add_bingding(&mut self, var: ExprAst, val: ExprAst) {
        assert!(self.vars.len() == self.vals.len());
        self.vars.push(var);
        self.vals.push(val);
    }

    pub fn lookup(&self, var: ExprAst) -> Option<ExprAst> {
        for i in range(0u, self.vars.len()).rev() {
            if self.vars[i] == var {
                return Some(self.vals[i].clone());
            }
        }
        match self.next {
            Some(ref sub) => return sub.lookup(var),
            _ => return None
        };
    }

    fn setup(&mut self) {
        fn is_null(args: ExprAst) -> ExprAst {
            ExprAst::Bool(BoolNode::new(args.car().is_empty()))
        }

        fn is_boolean(args: ExprAst) -> ExprAst {
            ExprAst::Bool(BoolNode::new(args.car().is_bool()))
        }
        fn is_symbol(args: ExprAst) -> ExprAst {
            ExprAst::Bool(BoolNode::new(args.car().is_symbol()))
        }

        fn is_string(args: ExprAst) -> ExprAst {
            ExprAst::Bool(BoolNode::new(args.car().is_string()))
        }

        fn is_pair(args: ExprAst) -> ExprAst {
            ExprAst::Bool(BoolNode::new(args.car().is_pair()))
        }

        fn is_char(args: ExprAst) -> ExprAst {
            ExprAst::Bool(BoolNode::new(args.car().is_char()))
        }

        fn is_int(args: ExprAst) -> ExprAst {
            ExprAst::Bool(BoolNode::new(args.car().is_int()))
        }

        fn add(args: ExprAst) -> ExprAst {
            let mut res = 0i;
            let mut exps = args;
            loop {
                if exps.is_empty() { break; }
                res += exps.car().as_int();
                exps = exps.cdr();
            }
            return ExprAst::Int(IntNode::new(res));
        }

        fn sub(args: ExprAst) -> ExprAst {
            let mut res = 0i;
            let mut exps = args;
            loop {
                if exps.is_empty() { break; }
                res -= exps.car().as_int();
                exps = exps.cdr();
            }
            return ExprAst::Int(IntNode::new(res));
        }

        fn mul(args: ExprAst) -> ExprAst {
            let mut res = 1i;
            let mut exps = args;
            loop {
                if exps.is_empty() { break; }
                res *= exps.car().as_int();
                exps = exps.cdr();
            }
            return ExprAst::Int(IntNode::new(res));
        }

        fn div(args: ExprAst) -> ExprAst {
            let mut exps = args;
            let mut res = exps.car().as_int();
            exps = exps.cdr();
            loop {
                if exps.is_empty() { break; }
                let nxt = exps.car().as_int();
                if nxt == 0 {
                    return ExprAst::Symbol(SymbolNode::new("Fail"));
                }
                res /= nxt;
                exps = exps.cdr();
            }
            return ExprAst::Int(IntNode::new(res));
        }

        fn eq(args: ExprAst) -> ExprAst {
            let obj1 = args.car();
            let obj2 = args.cdr().car();
            return ExprAst::Bool(BoolNode::new(obj1 == obj2));
        }

        fn less(args: ExprAst) -> ExprAst {
            let val = args.car().as_int();
            let mut exps = args.cdr();
            loop {
                if exps.is_empty() { break; }
                if val >= exps.car().as_int() {
                    return ExprAst::Bool(BoolNode::new(false));
                }
                exps = exps.cdr();
            }
            return ExprAst::Bool(BoolNode::new(true));
        }

        fn large(args: ExprAst) -> ExprAst {
            let val = args.car().as_int();
            let mut exps = args.cdr();
            loop {
                if exps.is_empty() { break; }
                if val <= exps.car().as_int() {
                    return ExprAst::Bool(BoolNode::new(false));
                }
                exps = exps.cdr();
            }
            return ExprAst::Bool(BoolNode::new(true));
        }

        self.def_var(ExprAst::Symbol(SymbolNode::new("null?")),
                     ExprAst::Proc(ProcNode::new(is_null)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("boolean?")),
                     ExprAst::Proc(ProcNode::new(is_boolean)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("symbol?")),
                     ExprAst::Proc(ProcNode::new(is_symbol)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("string?")),
                     ExprAst::Proc(ProcNode::new(is_string)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("char?")),
                     ExprAst::Proc(ProcNode::new(is_char)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("integer?")),
                     ExprAst::Proc(ProcNode::new(is_int)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("+")),
                     ExprAst::Proc(ProcNode::new(add)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("-")),
                     ExprAst::Proc(ProcNode::new(sub)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("<")),
                     ExprAst::Proc(ProcNode::new(less)));
        self.def_var(ExprAst::Symbol(SymbolNode::new(">")),
                     ExprAst::Proc(ProcNode::new(large)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("*")),
                     ExprAst::Proc(ProcNode::new(mul)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("/")),
                     ExprAst::Proc(ProcNode::new(div)));
        self.def_var(ExprAst::Symbol(SymbolNode::new("eq?")),
                     ExprAst::Proc(ProcNode::new(eq)));
    }
}

#[test]
fn test_env() {
    let mut env = Env::new();
    env.def_var(ExprAst::Str(StrNode::new("hello")),
                ExprAst::Str(StrNode::new("world")));

    let val = env.lookup(ExprAst::Str(StrNode::new("hello")));
    assert!(val.unwrap().as_str() == "world");

    env.def_var(ExprAst::Str(StrNode::new("1")),
                ExprAst::Int(IntNode::new(1)));
    let val = env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_int() == 1);

    env.def_var(ExprAst::Str(StrNode::new("1")),
                ExprAst::Int(IntNode::new(2)));
    let val = env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_int() == 2);

    env.def_var(ExprAst::Symbol(SymbolNode::new("sym")),
                ExprAst::Int(IntNode::new(2)));
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("sym")));
    assert!(val.unwrap().as_int() == 2);

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("null?")));
    assert!(val.unwrap().is_proc());
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("char?")));
    assert!(val.unwrap().is_proc());
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("integer?")));
    assert!(val.unwrap().is_proc());

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new(">")));
    assert!(val.unwrap().is_proc());

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("<")));
    assert!(val.unwrap().is_proc());
}
<commit_msg>refactor env with macro_rule<commit_after>use ast::*;

#[deriving(Clone, PartialEq)]
pub struct Env {
    pub vars: Vec<ExprAst>,
    pub vals: Vec<ExprAst>,
    pub next: Option<Box<Env>>
}

#[allow(dead_code)]
impl Env {
    pub fn new() -> Env {
        let mut res = Env {
            vars: vec![],
            vals: vec![],
            next: None
        };
        res.setup();
        res
    }

    pub fn def_var(&mut self, var: ExprAst, val: ExprAst) {
        self.add_bingding(var, val);
    }

    pub fn add_bingding(&mut self, var: ExprAst, val: ExprAst) {
        assert!(self.vars.len() == self.vals.len());
        self.vars.push(var);
        self.vals.push(val);
    }

    pub fn lookup(&self, var: ExprAst) -> Option<ExprAst> {
        for i in range(0u, self.vars.len()).rev() {
            if self.vars[i] == var {
                return Some(self.vals[i].clone());
            }
        }
        match self.next {
            Some(ref sub) => return sub.lookup(var),
            _ => return None
        };
    }

    fn setup(&mut self) {
        macro_rules! def_proc {
            ($func_name:ident, $raw_func_name:ident) => (
                fn $func_name(args: ExprAst) -> ExprAst {
                    ExprAst::Bool(BoolNode::new(args.car().$raw_func_name()))
                }
                )
        }

        macro_rules! add_proc {
            ($type_str:expr, $func_name:ident) => (
                self.def_var(ExprAst::Symbol(SymbolNode::new($type_str)),
                             ExprAst::Proc(ProcNode::new($func_name)))
                    )
        }

        def_proc!(is_null, is_empty);
        def_proc!(is_boolean, is_bool);
        def_proc!(is_symbol, is_symbol);
        def_proc!(is_string, is_string);
        def_proc!(is_pair, is_pair);
        def_proc!(is_char, is_char);
        def_proc!(is_int, is_int);

        add_proc!("null?", is_null);
        add_proc!("boolean?", is_boolean);
        add_proc!("symbol?", is_symbol);
        add_proc!("string?", is_string);
        add_proc!("char?", is_char);
        add_proc!("integer?", is_int);
        add_proc!("+", add);
        add_proc!("-", sub);
        add_proc!("*", mul);
        add_proc!("/", div);
        add_proc!("eq?", eq);
        add_proc!("<", less);
        add_proc!(">", large);
    }
}

fn add(args: ExprAst) -> ExprAst {
    let mut res = 0i;
    let mut exps = args;
    loop {
        if exps.is_empty() { break; }
        res += exps.car().as_int();
        exps = exps.cdr();
    }
    return ExprAst::Int(IntNode::new(res));
}

fn sub(args: ExprAst) -> ExprAst {
    let mut res = 0i;
    let mut exps = args;
    loop {
        if exps.is_empty() { break; }
        res -= exps.car().as_int();
        exps = exps.cdr();
    }
    return ExprAst::Int(IntNode::new(res));
}

fn mul(args: ExprAst) -> ExprAst {
    let mut res = 1i;
    let mut exps = args;
    loop {
        if exps.is_empty() { break; }
        res *= exps.car().as_int();
        exps = exps.cdr();
    }
    return ExprAst::Int(IntNode::new(res));
}

fn div(args: ExprAst) -> ExprAst {
    let mut exps = args;
    let mut res = exps.car().as_int();
    exps = exps.cdr();
    loop {
        if exps.is_empty() { break; }
        let nxt = exps.car().as_int();
        if nxt == 0 {
            return ExprAst::Symbol(SymbolNode::new("Fail"));
        }
        res /= nxt;
        exps = exps.cdr();
    }
    return ExprAst::Int(IntNode::new(res));
}

fn eq(args: ExprAst) -> ExprAst {
    let obj1 = args.car();
    let obj2 = args.cdr().car();
    return ExprAst::Bool(BoolNode::new(obj1 == obj2));
}

fn less(args: ExprAst) -> ExprAst {
    let val = args.car().as_int();
    let mut exps = args.cdr();
    loop {
        if exps.is_empty() { break; }
        if val >= exps.car().as_int() {
            return ExprAst::Bool(BoolNode::new(false));
        }
        exps = exps.cdr();
    }
    return ExprAst::Bool(BoolNode::new(true));
}

fn large(args: ExprAst) -> ExprAst {
    let val = args.car().as_int();
    let mut exps = args.cdr();
    loop {
        if exps.is_empty() { break; }
        if val <= exps.car().as_int() {
            return ExprAst::Bool(BoolNode::new(false));
        }
        exps = exps.cdr();
    }
    return ExprAst::Bool(BoolNode::new(true));
}

#[test]
fn test_env() {
    let mut env = Env::new();
    env.def_var(ExprAst::Str(StrNode::new("hello")),
                ExprAst::Str(StrNode::new("world")));

    let val = env.lookup(ExprAst::Str(StrNode::new("hello")));
    assert!(val.unwrap().as_str() == "world");

    env.def_var(ExprAst::Str(StrNode::new("1")),
                ExprAst::Int(IntNode::new(1)));
    let val = env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_int() == 1);

    env.def_var(ExprAst::Str(StrNode::new("1")),
                ExprAst::Int(IntNode::new(2)));
    let val = env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_int() == 2);

    env.def_var(ExprAst::Symbol(SymbolNode::new("sym")),
                ExprAst::Int(IntNode::new(2)));
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("sym")));
    assert!(val.unwrap().as_int() == 2);

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("null?")));
    assert!(val.unwrap().is_proc());
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("char?")));
    assert!(val.unwrap().is_proc());
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("integer?")));
    assert!(val.unwrap().is_proc());

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new(">")));
    assert!(val.unwrap().is_proc());

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("<")));
    assert!(val.unwrap().is_proc());
}
<|endoftext|>