<commit_before>use crate::base::{
    ast::{self, MutVisitor, SpannedExpr, SpannedIdent},
    fnv::{FnvMap, FnvSet},
    pos::{BytePos, Span},
    symbol::Symbol,
    types::{self, AppVec, ArcType, BuiltinType, Generic, Type, TypeInterner},
};

use crate::{substitution::Substitution, typ::RcType, typecheck::Typecheck};

pub(crate) struct TypeGeneralizer<'a, 'b: 'a> {
    level: u32,
    unbound_variables: FnvMap<u32, RcType>,
    /// We delay updating the substitution until after all recursive bindings have been typechecked
    /// to ensure that they get can figure out which variable got generalized for each binding
    delayed_generalizations: Vec<(u32, RcType)>,
    variable_generator: TypeVariableGenerator,
    pub tc: &'a mut Typecheck<'b>,
    span: Span<BytePos>,
}

impl<'a, 'b> Drop for TypeGeneralizer<'a, 'b> {
    fn drop(&mut self) {
        for (id, gen) in self.delayed_generalizations.drain(..) {
            self.tc.subs.replace(id, gen);
        }
    }
}

impl<'a, 'b> ::std::ops::Deref for TypeGeneralizer<'a, 'b> {
    type Target = Typecheck<'b>;
    fn deref(&self) -> &Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> ::std::ops::DerefMut for TypeGeneralizer<'a, 'b> {
    fn deref_mut(&mut self) -> &mut Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> TypeInterner<Symbol, RcType> for TypeGeneralizer<'a, 'b> {
    fn intern(&mut self, typ: Type<Symbol, RcType>) -> RcType {
        (&self.tc.subs).intern(typ)
    }
}

impl<'a, 'b> TypeGeneralizer<'a, 'b> {
    pub(crate) fn new(
        level: u32,
        tc: &'a mut Typecheck<'b>,
        typ: &RcType,
        span: Span<BytePos>,
    ) -> TypeGeneralizer<'a, 'b> {
        TypeGeneralizer {
            level,
            unbound_variables: Default::default(),
            delayed_generalizations: Vec::new(),
            variable_generator: TypeVariableGenerator::new(&tc.subs, typ),
            tc,
            span,
        }
    }

    /// Generalizing updates all variables which are above `level` into "generic variables". A
    /// generic variable is instantiated with a fresh type variable each time it is refered to.
    ///
    /// Generalzing is a crucial part when inferring types as types will otherwise be less general
    /// than they need to. Consider the following expression.
    /// ```f#
    /// let id x = x
    /// in id 2
    /// ```
    /// If the variable in `id` was not generalized before the `id 2` call the variable would be
    /// unified to an `Int` which is not what we want. Generalazing before checking the body of the
    /// `let` basically infers that the variables in `id` does not refer to anything outside the
    /// `let` scope and can thus be "generalized" into `a -> a` which is instantiated with a fresh
    /// type variable in the `id 2` call.
    pub(crate) fn generalize_variables<'i>(
        &mut self,
        args: &mut Iterator<Item = &'i mut SpannedIdent<Symbol>>,
        expr: &mut SpannedExpr<Symbol>,
    ) {
        self.tc.type_variables.enter_scope();

        // Replaces all type variables with their inferred types
        struct ReplaceVisitor<'a: 'c, 'b: 'a, 'c> {
            generalizer: &'c mut TypeGeneralizer<'a, 'b>,
        }

        impl<'a, 'b, 'c, 'd> MutVisitor<'d> for ReplaceVisitor<'a, 'b, 'c> {
            type Ident = Symbol;

            fn visit_expr(&mut self, e: &'d mut SpannedExpr<Self::Ident>) {
                self.generalizer.span = e.span;
                ast::walk_mut_expr(self, e);
            }

            fn visit_spanned_typed_ident(&mut self, id: &mut SpannedIdent<Symbol>) {
                self.generalizer.span = id.span;
                self.visit_ident(&mut id.value)
            }
            fn visit_typ(&mut self, typ: &mut ArcType) {
                debug!("Variable generalize {}", typ);
                if let Some(new_type) = self.generalizer.generalize_type(typ) {
                    *typ = new_type;
                    debug!("End generalize {}", typ);
                } else {
                    debug!("End variable generalize");
                }
            }
        }

        {
            let mut visitor = ReplaceVisitor { generalizer: self };
            visitor.visit_expr(expr);
            for arg in &mut *args {
                visitor.visit_typ(&mut arg.value.typ)
            }
        }

        self.tc.type_variables.exit_scope();
    }

    pub(crate) fn generalize_type_top(&mut self, typ: &mut RcType) {
        self.tc.type_variables.enter_scope();

        let mut result_type = self.generalize_type(typ);

        self.tc.type_variables.exit_scope();

        if result_type.is_none() && !self.unbound_variables.is_empty() {
            result_type = Some(typ.clone());
        }

        result_type = result_type.map(|typ| {
            let delayed_generalizations = &mut self.delayed_generalizations;
            let mut params = self
                .unbound_variables
                .drain()
                .map(|(id, generic)| {
                    let t = match &*generic {
                        Type::Generic(g) => g.clone(),
                        _ => unreachable!(),
                    };
                    delayed_generalizations.push((id, generic));
                    t
                })
                .collect::<Vec<_>>();
            params.sort_unstable_by(|l, r| l.id.declared_name().cmp(r.id.declared_name()));

            self.tc.forall(params, typ)
        });
        if let Some(finished) = result_type {
            *typ = finished;
        }
        debug!("End generalize {}", typ);
    }

    pub(crate) fn generalize_type(&mut self, typ: &RcType) -> Option<RcType> {
        let replacement = self.subs.replace_variable(typ);
        let mut typ = typ;
        if let Some(ref t) = replacement {
            typ = t;
        }
        trace!("GEN: {}", typ);

        if !typ.needs_generalize() {
            trace!("No need to generalize: {}", typ);
            return replacement;
        }

        let new_type = match **typ {
            Type::Variable(ref var) if self.subs.get_level(var.id) >= self.level => {
                let Self {
                    tc,
                    variable_generator,
                    ..
                } = self;
                let gen = self.unbound_variables.entry(var.id).or_insert_with(|| {
                    // Create a prefix if none exists
                    let id = variable_generator.next_variable(tc);

                    let gen: RcType = tc.generic(Generic::new(id.clone(), var.kind.clone()));
                    debug!("Gen {} to {}", var.id, gen);

                    gen
                });

                Some(gen.clone())
            }

            Type::Skolem(ref skolem) => {
                let in_scope = self.type_variables.contains_key(&skolem.name);
                if self.subs.get_level(skolem.id) >= self.level {
                    let generic = self.generic(Generic {
                        id: skolem.name.clone(),
                        kind: skolem.kind.clone(),
                    });

                    if !in_scope {
                        self.unbound_variables
                            .insert(skolem.id.clone(), generic.clone());
                    }

                    Some(generic)
                } else {
                    replacement.clone()
                }
            }

            _ => {
                // Ensure that the forall's variables don't look unbound
                if let Type::Forall(ref params, _) = **typ {
                    self.type_variables.enter_scope();
                    let mut type_cache = &self.tc.subs;
                    self.tc.type_variables.extend(
                        params
                            .iter()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }

                let new_type = types::walk_move_type_opt(
                    typ,
                    &mut types::InternerVisitor::control(self, |self_, typ: &RcType| {
                        self_.generalize_type(typ)
                    }),
                )
                .map(|t| unroll_typ(self.tc, &t).unwrap_or(t))
                .or_else(|| replacement.clone());

                if let Type::Forall(..) = **typ {
                    self.type_variables.exit_scope();
                }

                new_type
            }
        };

        new_type
    }
}

struct TypeVariableGenerator {
    map: FnvSet<Symbol>,
    name: String,
    i: u32,
}

impl TypeVariableGenerator {
    fn new(subs: &Substitution<RcType>, typ: &RcType) -> TypeVariableGenerator {
        fn gather_foralls(map: &mut FnvSet<Symbol>, subs: &Substitution<RcType>, typ: &RcType) {
            let typ = subs.real(typ);
            if let Type::Forall(ref params, _) = **typ {
                map.extend(params.iter().map(|param| param.id.clone()));
            }
            types::walk_type_(
                typ,
                &mut types::ControlVisitation(|typ: &RcType| {
                    gather_foralls(map, subs, typ);
                }),
            );
        }
        let mut map = FnvSet::default();
        gather_foralls(&mut map, subs, typ);
        TypeVariableGenerator {
            map,
            name: "".to_string(),
            i: 0,
        }
    }
    /// Generate a generic variable name which is not used in the current scope
    fn next_variable(&mut self, tc: &mut Typecheck) -> Symbol {
        let symbol = if self.name.is_empty() {
            self.next_variable_(tc)
        } else {
            let name = format!("{}{}", self.name, self.i);
            self.i += 1;
            tc.symbols.symbol(&name[..])
        };
        self.map.insert(symbol.clone());
        let hole = tc.hole();
        tc.type_variables.insert(symbol.clone(), hole);
        symbol
    }

    fn next_variable_(&mut self, tc: &mut Typecheck) -> Symbol {
        for c in b'a'..(b'z' + 1) {
            self.name.push(c as char);
            let symbol = tc.symbols.symbol(&self.name[..]);
            if !self.map.contains(&symbol) && tc.type_variables.get(&symbol).is_none() {
                return symbol;
            }
            self.name.pop();
        }
        self.name.push('a');
        self.next_variable_(tc)
    }
}

fn unroll_typ(interner: &mut impl TypeInterner<Symbol, RcType>, typ: &RcType) -> Option<RcType> {
    let mut args = AppVec::new();
    let mut current = match **typ {
        Type::App(ref l, ref rest) => {
            // No need to unroll if `l` is not an application as that will just result in returning
            // an application that is identical to `typ`
            match **l {
                Type::App(..) => (),
                _ => return None,
            }
            args.extend(rest.iter().rev().cloned());
            l
        }
        _ => return unroll_record(interner, typ),
    };
    while let Type::App(ref l, ref rest) = **current {
        args.extend(rest.iter().rev().cloned());
        current = l;
    }
    if args.is_empty() {
        None
    } else {
        args.reverse();
        match **current {
            Type::Builtin(BuiltinType::Function) if args.len() == 2 => {
                let ret = args.pop().unwrap();
                Some(interner.function(args, ret))
            }
            _ => Some(interner.app(current.clone(), args)),
        }
    }
}

fn unroll_record(
    interner: &mut impl TypeInterner<Symbol, RcType>,
    typ: &Type<Symbol, RcType>,
) -> Option<RcType> {
    let mut new_types = Vec::new();
    let mut new_fields = Vec::new();
    let mut current = match *typ {
        Type::ExtendRow {
            ref types,
            ref fields,
            ref rest,
        } => match **rest {
            Type::ExtendRow { .. } => {
                new_types.extend_from_slice(types);
                new_fields.extend_from_slice(fields);
                rest
            }
            _ => return None,
        },
        _ => return None,
    };
    while let Type::ExtendRow {
        ref types,
        ref fields,
        ref rest,
    } = **current
    {
        new_types.extend_from_slice(types);
        new_fields.extend_from_slice(fields);
        current = rest;
    }
    if new_types.is_empty() && new_fields.is_empty() {
        None
    } else {
        Some(interner.extend_row(new_types, new_fields, current.clone()))
    }
}

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

    use crate::base::types::SharedInterner;

    #[test]
    fn unroll_typ_test() {
        let mut interner = SharedInterner::default();

        let i: RcType = interner.int();
        let s: RcType = interner.string();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![s.clone()]),
                    collect![i.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![s.clone(), i.clone()]))
        );
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![i.clone(), s.clone()]))
        );
        let f: RcType = interner.function_builtin();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(f.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.function(vec![i.clone()], s.clone()))
        );
    }
}
<commit_msg>perf: Avoid recursing in gather_foralls<commit_after>use crate::base::{
    ast::{self, MutVisitor, SpannedExpr, SpannedIdent},
    fnv::{FnvMap, FnvSet},
    pos::{BytePos, Span},
    symbol::Symbol,
    types::{self, AppVec, ArcType, BuiltinType, Flags, Generic, Type, TypeExt, TypeInterner},
};

use crate::{substitution::Substitution, typ::RcType, typecheck::Typecheck};

pub(crate) struct TypeGeneralizer<'a, 'b: 'a> {
    level: u32,
    unbound_variables: FnvMap<u32, RcType>,
    /// We delay updating the substitution until after all recursive bindings have been typechecked
    /// to ensure that they get can figure out which variable got generalized for each binding
    delayed_generalizations: Vec<(u32, RcType)>,
    variable_generator: TypeVariableGenerator,
    pub tc: &'a mut Typecheck<'b>,
    span: Span<BytePos>,
}

impl<'a, 'b> Drop for TypeGeneralizer<'a, 'b> {
    fn drop(&mut self) {
        for (id, gen) in self.delayed_generalizations.drain(..) {
            self.tc.subs.replace(id, gen);
        }
    }
}

impl<'a, 'b> ::std::ops::Deref for TypeGeneralizer<'a, 'b> {
    type Target = Typecheck<'b>;
    fn deref(&self) -> &Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> ::std::ops::DerefMut for TypeGeneralizer<'a, 'b> {
    fn deref_mut(&mut self) -> &mut Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> TypeInterner<Symbol, RcType> for TypeGeneralizer<'a, 'b> {
    fn intern(&mut self, typ: Type<Symbol, RcType>) -> RcType {
        (&self.tc.subs).intern(typ)
    }
}

impl<'a, 'b> TypeGeneralizer<'a, 'b> {
    pub(crate) fn new(
        level: u32,
        tc: &'a mut Typecheck<'b>,
        typ: &RcType,
        span: Span<BytePos>,
    ) -> TypeGeneralizer<'a, 'b> {
        TypeGeneralizer {
            level,
            unbound_variables: Default::default(),
            delayed_generalizations: Vec::new(),
            variable_generator: TypeVariableGenerator::new(&tc.subs, typ),
            tc,
            span,
        }
    }

    /// Generalizing updates all variables which are above `level` into "generic variables". A
    /// generic variable is instantiated with a fresh type variable each time it is refered to.
    ///
    /// Generalzing is a crucial part when inferring types as types will otherwise be less general
    /// than they need to. Consider the following expression.
    /// ```f#
    /// let id x = x
    /// in id 2
    /// ```
    /// If the variable in `id` was not generalized before the `id 2` call the variable would be
    /// unified to an `Int` which is not what we want. Generalazing before checking the body of the
    /// `let` basically infers that the variables in `id` does not refer to anything outside the
    /// `let` scope and can thus be "generalized" into `a -> a` which is instantiated with a fresh
    /// type variable in the `id 2` call.
    pub(crate) fn generalize_variables<'i>(
        &mut self,
        args: &mut Iterator<Item = &'i mut SpannedIdent<Symbol>>,
        expr: &mut SpannedExpr<Symbol>,
    ) {
        self.tc.type_variables.enter_scope();

        // Replaces all type variables with their inferred types
        struct ReplaceVisitor<'a: 'c, 'b: 'a, 'c> {
            generalizer: &'c mut TypeGeneralizer<'a, 'b>,
        }

        impl<'a, 'b, 'c, 'd> MutVisitor<'d> for ReplaceVisitor<'a, 'b, 'c> {
            type Ident = Symbol;

            fn visit_expr(&mut self, e: &'d mut SpannedExpr<Self::Ident>) {
                self.generalizer.span = e.span;
                ast::walk_mut_expr(self, e);
            }

            fn visit_spanned_typed_ident(&mut self, id: &mut SpannedIdent<Symbol>) {
                self.generalizer.span = id.span;
                self.visit_ident(&mut id.value)
            }
            fn visit_typ(&mut self, typ: &mut ArcType) {
                debug!("Variable generalize {}", typ);
                if let Some(new_type) = self.generalizer.generalize_type(typ) {
                    *typ = new_type;
                    debug!("End generalize {}", typ);
                } else {
                    debug!("End variable generalize");
                }
            }
        }

        {
            let mut visitor = ReplaceVisitor { generalizer: self };
            visitor.visit_expr(expr);
            for arg in &mut *args {
                visitor.visit_typ(&mut arg.value.typ)
            }
        }

        self.tc.type_variables.exit_scope();
    }

    pub(crate) fn generalize_type_top(&mut self, typ: &mut RcType) {
        self.tc.type_variables.enter_scope();

        let mut result_type = self.generalize_type(typ);

        self.tc.type_variables.exit_scope();

        if result_type.is_none() && !self.unbound_variables.is_empty() {
            result_type = Some(typ.clone());
        }

        result_type = result_type.map(|typ| {
            let delayed_generalizations = &mut self.delayed_generalizations;
            let mut params = self
                .unbound_variables
                .drain()
                .map(|(id, generic)| {
                    let t = match &*generic {
                        Type::Generic(g) => g.clone(),
                        _ => unreachable!(),
                    };
                    delayed_generalizations.push((id, generic));
                    t
                })
                .collect::<Vec<_>>();
            params.sort_unstable_by(|l, r| l.id.declared_name().cmp(r.id.declared_name()));

            self.tc.forall(params, typ)
        });
        if let Some(finished) = result_type {
            *typ = finished;
        }
        debug!("End generalize {}", typ);
    }

    pub(crate) fn generalize_type(&mut self, typ: &RcType) -> Option<RcType> {
        let replacement = self.subs.replace_variable(typ);
        let mut typ = typ;
        if let Some(ref t) = replacement {
            typ = t;
        }
        trace!("GEN: {}", typ);

        if !typ.needs_generalize() {
            trace!("No need to generalize: {}", typ);
            return replacement;
        }

        let new_type = match **typ {
            Type::Variable(ref var) if self.subs.get_level(var.id) >= self.level => {
                let Self {
                    tc,
                    variable_generator,
                    ..
                } = self;
                let gen = self.unbound_variables.entry(var.id).or_insert_with(|| {
                    // Create a prefix if none exists
                    let id = variable_generator.next_variable(tc);

                    let gen: RcType = tc.generic(Generic::new(id.clone(), var.kind.clone()));
                    debug!("Gen {} to {}", var.id, gen);

                    gen
                });

                Some(gen.clone())
            }

            Type::Skolem(ref skolem) => {
                let in_scope = self.type_variables.contains_key(&skolem.name);
                if self.subs.get_level(skolem.id) >= self.level {
                    let generic = self.generic(Generic {
                        id: skolem.name.clone(),
                        kind: skolem.kind.clone(),
                    });

                    if !in_scope {
                        self.unbound_variables
                            .insert(skolem.id.clone(), generic.clone());
                    }

                    Some(generic)
                } else {
                    replacement.clone()
                }
            }

            _ => {
                // Ensure that the forall's variables don't look unbound
                if let Type::Forall(ref params, _) = **typ {
                    self.type_variables.enter_scope();
                    let mut type_cache = &self.tc.subs;
                    self.tc.type_variables.extend(
                        params
                            .iter()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }

                let new_type = types::walk_move_type_opt(
                    typ,
                    &mut types::InternerVisitor::control(self, |self_, typ: &RcType| {
                        self_.generalize_type(typ)
                    }),
                )
                .map(|t| unroll_typ(self.tc, &t).unwrap_or(t))
                .or_else(|| replacement.clone());

                if let Type::Forall(..) = **typ {
                    self.type_variables.exit_scope();
                }

                new_type
            }
        };

        new_type
    }
}

struct TypeVariableGenerator {
    map: FnvSet<Symbol>,
    name: String,
    i: u32,
}

impl TypeVariableGenerator {
    fn new(subs: &Substitution<RcType>, typ: &RcType) -> TypeVariableGenerator {
        fn gather_foralls(map: &mut FnvSet<Symbol>, subs: &Substitution<RcType>, typ: &RcType) {
            if !typ
                .flags()
                .intersects(Flags::HAS_VARIABLES | Flags::HAS_FORALL)
            {
                return;
            }
            let typ = subs.real(typ);
            if let Type::Forall(ref params, _) = **typ {
                map.extend(params.iter().map(|param| param.id.clone()));
            }
            types::walk_type_(
                typ,
                &mut types::ControlVisitation(|typ: &RcType| {
                    gather_foralls(map, subs, typ);
                }),
            );
        }
        let mut map = FnvSet::default();
        gather_foralls(&mut map, subs, typ);
        TypeVariableGenerator {
            map,
            name: "".to_string(),
            i: 0,
        }
    }
    /// Generate a generic variable name which is not used in the current scope
    fn next_variable(&mut self, tc: &mut Typecheck) -> Symbol {
        let symbol = if self.name.is_empty() {
            self.next_variable_(tc)
        } else {
            let name = format!("{}{}", self.name, self.i);
            self.i += 1;
            tc.symbols.symbol(&name[..])
        };
        self.map.insert(symbol.clone());
        let hole = tc.hole();
        tc.type_variables.insert(symbol.clone(), hole);
        symbol
    }

    fn next_variable_(&mut self, tc: &mut Typecheck) -> Symbol {
        for c in b'a'..(b'z' + 1) {
            self.name.push(c as char);
            let symbol = tc.symbols.symbol(&self.name[..]);
            if !self.map.contains(&symbol) && tc.type_variables.get(&symbol).is_none() {
                return symbol;
            }
            self.name.pop();
        }
        self.name.push('a');
        self.next_variable_(tc)
    }
}

fn unroll_typ(interner: &mut impl TypeInterner<Symbol, RcType>, typ: &RcType) -> Option<RcType> {
    let mut args = AppVec::new();
    let mut current = match **typ {
        Type::App(ref l, ref rest) => {
            // No need to unroll if `l` is not an application as that will just result in returning
            // an application that is identical to `typ`
            match **l {
                Type::App(..) => (),
                _ => return None,
            }
            args.extend(rest.iter().rev().cloned());
            l
        }
        _ => return unroll_record(interner, typ),
    };
    while let Type::App(ref l, ref rest) = **current {
        args.extend(rest.iter().rev().cloned());
        current = l;
    }
    if args.is_empty() {
        None
    } else {
        args.reverse();
        match **current {
            Type::Builtin(BuiltinType::Function) if args.len() == 2 => {
                let ret = args.pop().unwrap();
                Some(interner.function(args, ret))
            }
            _ => Some(interner.app(current.clone(), args)),
        }
    }
}

fn unroll_record(
    interner: &mut impl TypeInterner<Symbol, RcType>,
    typ: &Type<Symbol, RcType>,
) -> Option<RcType> {
    let mut new_types = Vec::new();
    let mut new_fields = Vec::new();
    let mut current = match *typ {
        Type::ExtendRow {
            ref types,
            ref fields,
            ref rest,
        } => match **rest {
            Type::ExtendRow { .. } => {
                new_types.extend_from_slice(types);
                new_fields.extend_from_slice(fields);
                rest
            }
            _ => return None,
        },
        _ => return None,
    };
    while let Type::ExtendRow {
        ref types,
        ref fields,
        ref rest,
    } = **current
    {
        new_types.extend_from_slice(types);
        new_fields.extend_from_slice(fields);
        current = rest;
    }
    if new_types.is_empty() && new_fields.is_empty() {
        None
    } else {
        Some(interner.extend_row(new_types, new_fields, current.clone()))
    }
}

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

    use crate::base::types::SharedInterner;

    #[test]
    fn unroll_typ_test() {
        let mut interner = SharedInterner::default();

        let i: RcType = interner.int();
        let s: RcType = interner.string();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![s.clone()]),
                    collect![i.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![s.clone(), i.clone()]))
        );
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![i.clone(), s.clone()]))
        );
        let f: RcType = interner.function_builtin();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(f.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.function(vec![i.clone()], s.clone()))
        );
    }
}
<|endoftext|>


<commit_before>//! Probability distributions.

use random::Source;

/// A probability distribution.
pub trait Distribution {
    type Value;

    /// Compute the expected value.
    fn mean(&self) -> f64;

    /// Compute the variance.
    fn var(&self) -> f64;

    /// Compute the standard deviation.
    #[inline]
    fn sd(&self) -> f64 {
        self.var().sqrt()
    }

    /// Compute the skewness.
    fn skewness(&self) -> f64;

    /// Compute the excess kurtosis.
    fn kurtosis(&self) -> f64;

    /// Compute the median.
    fn median(&self) -> f64;

    /// Compute the modes.
    fn modes(&self) -> Vec<Self::Value>;

    /// Compute the differential entropy in nats.
    fn entropy(&self) -> f64;

    /// Compute the cumulative distribution function.
    fn cdf(&self, f64) -> f64;

    /// Compute the inverse of the cumulative distribution function.
    fn inv_cdf(&self, f64) -> Self::Value;

    /// Compute the probability density function.
    fn pdf(&self, Self::Value) -> f64 where Self: Continuous {
        unimplemented!();
    }

    /// Compute the probability mass function.
    fn pmf(&self, Self::Value) -> f64 where Self: Discrete {
        unimplemented!();
    }

    /// Draw a sample.
    fn sample<S>(&self, &mut S) -> Self::Value where S: Source;
}

/// A continuous probability distribution.
pub trait Continuous {
}

/// A discrete probability distribution.
pub trait Discrete {
}

pub use self::bernoulli::Bernoulli;
pub use self::beta::Beta;
pub use self::binomial::Binomial;
pub use self::categorical::Categorical;
pub use self::exponential::Exponential;
pub use self::gamma::Gamma;
pub use self::gaussian::Gaussian;
pub use self::uniform::Uniform;

mod bernoulli;
mod beta;
mod binomial;
mod categorical;
mod exponential;
mod gamma;
mod gaussian;
mod uniform;
<commit_msg>Change the signature of PDFs<commit_after>//! Probability distributions.

use random::Source;

/// A probability distribution.
pub trait Distribution {
    type Value;

    /// Compute the expected value.
    fn mean(&self) -> f64;

    /// Compute the variance.
    fn var(&self) -> f64;

    /// Compute the standard deviation.
    #[inline]
    fn sd(&self) -> f64 {
        self.var().sqrt()
    }

    /// Compute the skewness.
    fn skewness(&self) -> f64;

    /// Compute the excess kurtosis.
    fn kurtosis(&self) -> f64;

    /// Compute the median.
    fn median(&self) -> f64;

    /// Compute the modes.
    fn modes(&self) -> Vec<Self::Value>;

    /// Compute the differential entropy in nats.
    fn entropy(&self) -> f64;

    /// Compute the cumulative distribution function.
    fn cdf(&self, f64) -> f64;

    /// Compute the inverse of the cumulative distribution function.
    fn inv_cdf(&self, f64) -> Self::Value;

    /// Compute the probability density function.
    fn pdf(&self, f64) -> f64 where Self: Continuous {
        unimplemented!();
    }

    /// Compute the probability mass function.
    fn pmf(&self, Self::Value) -> f64 where Self: Discrete {
        unimplemented!();
    }

    /// Draw a sample.
    fn sample<S>(&self, &mut S) -> Self::Value where S: Source;
}

/// A continuous probability distribution.
pub trait Continuous {
}

/// A discrete probability distribution.
pub trait Discrete {
}

pub use self::bernoulli::Bernoulli;
pub use self::beta::Beta;
pub use self::binomial::Binomial;
pub use self::categorical::Categorical;
pub use self::exponential::Exponential;
pub use self::gamma::Gamma;
pub use self::gaussian::Gaussian;
pub use self::uniform::Uniform;

mod bernoulli;
mod beta;
mod binomial;
mod categorical;
mod exponential;
mod gamma;
mod gaussian;
mod uniform;
<|endoftext|>


<commit_before>use llvm_sys::execution_engine::*;
use super::*;
use std::mem;

pub struct ExecutionEngine {
    pub ptr: LLVMExecutionEngineRef,
}

impl ExecutionEngine {
    pub fn create_for_module(module: Module) -> Result<ExecutionEngine, &'static str> {
        unsafe {
            let mut ee = mem::uninitialized();
            let mut out = mem::zeroed();

            let res = LLVMCreateExecutionEngineForModule(&mut ee, module.ptr, &mut out);

            if res == 0 { // no error message was set
                Ok(ExecutionEngine{
                    ptr: ee
                })
            } else {
                Err(c_str_to_str!(out as *const i8))
            }
        }
    }

    pub fn get_function_address(&self, fname: &str) -> Option<extern "C" fn()> {
        let fname_s = CString::new(fname).unwrap();
        unsafe {
            let addr = LLVMGetFunctionAddress(self.ptr, fname_s.as_ptr());

            if addr == 0 {
                None
            } else {
                Some(mem::transmute(addr))
            }
        }
    }
}

impl Drop for ExecutionEngine {
    fn drop(&mut self) {
        unsafe {
            LLVMDisposeExecutionEngine(self.ptr);
        }
    }
}

<commit_msg>Add link_in_mcjit<commit_after>use llvm_sys::execution_engine::*;
use super::*;
use std::mem;

pub struct ExecutionEngine {
    pub ptr: LLVMExecutionEngineRef,
}

impl ExecutionEngine {
    pub fn create_for_module(module: Module) -> Result<ExecutionEngine, &'static str> {
        unsafe {
            let mut ee = mem::uninitialized();
            let mut out = mem::zeroed();

            let res = LLVMCreateExecutionEngineForModule(&mut ee, module.ptr, &mut out);

            if res == 0 { // no error message was set
                Ok(ExecutionEngine{
                    ptr: ee
                })
            } else {
                Err(c_str_to_str!(out as *const i8))
            }
        }
    }

    pub fn get_function_address(&self, fname: &str) -> Option<extern "C" fn()> {
        let fname_s = CString::new(fname).unwrap();
        unsafe {
            let addr = LLVMGetFunctionAddress(self.ptr, fname_s.as_ptr());

            if addr == 0 {
                None
            } else {
                Some(mem::transmute(addr))
            }
        }
    }
}

impl Drop for ExecutionEngine {
    fn drop(&mut self) {
        unsafe {
            LLVMDisposeExecutionEngine(self.ptr);
        }
    }
}

pub fn link_in_mcjit() {
    unsafe {
        LLVMLinkInMCJIT();
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// 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.

extern crate cmake;

use std::env;
use std::path::{Path, PathBuf};

fn build_shaderc(shaderc_dir: &PathBuf) -> PathBuf {
        cmake::Config::new(shaderc_dir)
            .profile("Release")
            .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
            .define("SPIRV_SKIP_EXECUTABLES", "ON")
            .define("SHADERC_SKIP_TESTS", "ON")
            .build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    cmake::Config::new(shaderc_dir)
            .profile("Release")
            .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
            .define("SPIRV_SKIP_EXECUTABLES", "ON")
            .define("SHADERC_SKIP_TESTS", "ON")
            // cmake-rs tries to be clever on Windows by injecting several
            // C/C++ flags, which causes problems. So I have to manually
            // define CMAKE_*_FLAGS_* here to suppress that.
            .define("CMAKE_C_FLAGS", " /nologo /EHsc")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc")
            .build()
}

fn main() {
    if env::var("CARGO_FEATURE_BUILD_NATIVE_SHADERC").is_err() {
        println!("cargo:warning=requested to skip building native C++ shaderc");
        return
    }

    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        build_shaderc_msvc(&shaderc_dir)
    } else {
        build_shaderc(&shaderc_dir)
    };

    lib_path.push("lib");

    println!("cargo:rustc-link-search=native={}", lib_path.display());
    if target_os == "windows" {
        println!("cargo:rustc-link-lib=static=shaderc_combined");
    } else {
        println!("cargo:rustc-link-lib=dylib=shaderc_shared");
    }
}
<commit_msg>Forced install directory to lib instead of lib64<commit_after>// Copyright 2017 Google Inc.
//
// 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.

extern crate cmake;

use std::env;
use std::path::{Path, PathBuf};

fn build_shaderc(shaderc_dir: &PathBuf) -> PathBuf {
        cmake::Config::new(shaderc_dir)
            .profile("Release")
            .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
            .define("SPIRV_SKIP_EXECUTABLES", "ON")
            .define("SHADERC_SKIP_TESTS", "ON")
            .define("CMAKE_INSTALL_LIBDIR", "lib")
            .build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    cmake::Config::new(shaderc_dir)
            .profile("Release")
            .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
            .define("SPIRV_SKIP_EXECUTABLES", "ON")
            .define("SHADERC_SKIP_TESTS", "ON")
            // cmake-rs tries to be clever on Windows by injecting several
            // C/C++ flags, which causes problems. So I have to manually
            // define CMAKE_*_FLAGS_* here to suppress that.
            .define("CMAKE_C_FLAGS", " /nologo /EHsc")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc")
            .define("CMAKE_INSTALL_LIBDIR", "lib")
            .build()
}

fn main() {
    if env::var("CARGO_FEATURE_BUILD_NATIVE_SHADERC").is_err() {
        println!("cargo:warning=requested to skip building native C++ shaderc");
        return
    }

    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        build_shaderc_msvc(&shaderc_dir)
    } else {
        build_shaderc(&shaderc_dir)
    };

    lib_path.push("lib");

    println!("cargo:rustc-link-search=native={}", lib_path.display());
    if target_os == "windows" {
        println!("cargo:rustc-link-lib=static=shaderc_combined");
    } else {
        println!("cargo:rustc-link-lib=dylib=shaderc_shared");
    }
}
<|endoftext|>


<commit_before>use serde_json;

// TODO: use when new version drops
//use serialport::{SerialPortInfo,UsbPortInfo,SerialPortType};

use std::fmt;
use std::sync::mpsc::Sender;

#[derive( Clone, Debug)]
pub struct SubscriptionRequest {
  pub sub_id: String,
  pub subscriber: Sender<SerialResponse>,
}

/// Represents the valid json requests that can be made
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum SerialRequest {
  // TODO: Divorce subscriptions from port open/close?
  // Right now port is only closed when last subscriber
  // unsubscribes
  /// Open a port for reading
  Open { port: String },
  /// Take control of a port for writing
  WriteLock { port: String },
  /// Release control of a port for writing
  /// If no port is given, release all write locks
  /// for all ports
  ReleaseWriteLock { port: Option<String> },
  /// Write data, only works if we have control
  Write {
    port: String,
    data: String,
    base64: Option<bool>,
  },
  /// Close a port or subscription
  Close { port: Option<String> },
  /// List serial ports
  List {},
}

impl fmt::Display for SerialRequest {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let json = serde_json::to_string(self).unwrap_or(
      "Display SerialRequest: Serialization Failed!"
        .to_string(),
    );
    write!(f, "{}", json)
  }
}

/// Represents the valid serial responses that can be made
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum SerialResponse {
  /// Error response
  Error {
    description: String,
    display: String,
  },
  /// Data that was read from port
  Read {
    port: String,
    data: String,
    base64: Option<bool>,
  },
  /// Port was closed
  Closed { port: String },
  /// Port was closed
  Opened { port: String },
  /// Command successful
  Ok { msg: String },
  /// We don't echo data back
  /// TODO: Return hash of data written?
  Wrote { port: String },
  /// Port was successfully write locked
  WriteLocked { port: String },
  /// Lock on port(s) successfully released
  WriteLockReleased { port: Option<String> },
  /// List serial ports response
  List { ports: Vec<String> },
}

/*
// TODO: Uncomment when new serialport module version drops
/// Needed for Serde Support as
/// SerialPortInfo is in seperate module
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(remote = "SerialPortInfo")]
pub struct SerialPortInfoDef {
    /// The short name of the serial port
    pub port_name: String,
    /// The hardware device type that exposes this port
    pub port_type: SerialPortType,

}

#[derive(Serialize, Deserialize)]
#[serde(remote = "sp::SerialPortType")]
/// The physical type of a `SerialPort`
pub enum SerialPortTypeDef {
    /// The serial port is connected via USB
    UsbPort(UsbPortInfoDef),
    /// The serial port is connected via PCI (permanent port)
    PciPort,
    /// The serial port is connected via Bluetooth
    BluetoothPort,
    /// It can't be determined how the serial port is connected
    Unknown,
}

#[derive(Serialize, Deserialize)]
#[serde(remote = "UsbPortInfo")]
pub struct UsbPortInfoDef {
    /// Vender ID
    pub vid: u16,
    /// Product ID
    pub pid: u16,
    /// Serial number (arbitrary string)
    pub serial_number: Option<String>,
    /// Manufacturer (arbitrary string)
    pub manufacturer: Option<String>,
    /// Product name (arbitrary string)
    pub product: Option<String>,
}
*/

impl fmt::Display for SerialResponse {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let json = serde_json::to_string(self).unwrap_or(
      "Display SerialResponse: Serialization Failed!"
        .to_string(),
    );
    write!(f, "{}", json)
  }
}
<commit_msg>Added more comments<commit_after>//! This module contains all of the enums used
//! to represent messages that can be send by clients
//! ( SerialRequest::* ) and their responses by
//! by the server ( SerialReponse::* )

use serde_json;

// TODO: use when new version drops
//use serialport::{SerialPortInfo,UsbPortInfo,SerialPortType};

use std::fmt;
use std::sync::mpsc::Sender;

#[derive( Clone, Debug)]
pub struct SubscriptionRequest {
  pub sub_id: String,
  pub subscriber: Sender<SerialResponse>,
}

/// Represents the valid json requests that can be made
///
/// On the server side, every client is associated with
/// a unique subscription id which is used
/// to associate a given connection with their
/// operations
///
/// Requests that fail or can not be met will result
/// in SerialResponse::Error responses
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum SerialRequest {
  /// Open a port for reading
  ///
  /// Opening the same port more than once is
  /// okay and has no ill effects
  ///
  ///``` json
  /// JSON:
  /// {"Open":{"port":"/dev/ttyUSB"}}
  ///```
  Open { port: String },
  /// Take control of a port for writing
  ///
  /// ``` json
  /// JSON:
  /// {"WriteLock":{"port":"/dev/ttyUSB"}}
  /// ```
  WriteLock { port: String },
  /// Release control of a port for writing
  /// If no port is given, release all write locks
  /// held by the client for all ports
  ///
  /// ``` json
  /// JSON:
  /// {"ReleaseWriteLock":{"port":"/dev/ttyUSB"}}
  ///
  /// {"ReleaseWriteLock":{}}
  /// ```
  ReleaseWriteLock { port: Option<String> },
  /// Write data, only works if the client
  /// has a WriteLock active for the given port
  ///
  /// The base64 property is only required
  /// if the data is encoded as base64
  ///
  /// ``` json
  /// JSON:
  /// {"Write":{"port":"/dev/ttyUSB",
  ///           "data": "Hello World"
  ///          }}
  ///
  /// {"Write":{"port":"/dev/ttyUSB",
  ///           "data": "SGVsbG8gV29ybGQ=",
  ///           "base64": true
  ///          }}
  /// ```
  Write {
    port: String,
    data: String,
    base64: Option<bool>,
  },
  /// Close the port, which stops any read updates
  /// from the port from being sent to this subscription
  ///
  /// If no port is specified, then all ports are 'closed'
  ///
  /// In most cases this behaves more as an 'unsubscribe'
  /// as the hardware port is only closed and released
  /// when the last client connected to it sends a Close message
  ///
  /// ``` json
  /// JSON:
  /// {"Close":{"port":"/dev/ttyUSB"}}
  ///
  /// {"Close":{}}
  /// ```
  Close { port: Option<String> },
  /// List available serial ports
  ///
  /// ``` json
  /// JSON:
  /// {"List":{}}
  /// ```
  List {},
}

impl fmt::Display for SerialRequest {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let json = serde_json::to_string(self).unwrap_or(
      "Display SerialRequest: Serialization Failed!"
        .to_string(),
    );
    write!(f, "{}", json)
  }
}

/// Represents the valid json responses that can be made
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum SerialResponse {
  /// Error response
  ///
  /// ``` json
  /// JSON:
  /// {"Error":{
  ///            "description":"Error reading serial port",
  ///            "display":"Error reading serial port '/dev/ttyUSB0'"
  ///          }}
  ///
  /// ```
  Error {
    description: String,
    display: String,
  },
  /// Data that was read from port
  ///
  /// If the data could not be parsed into a utf8 string
  /// then it is base64 encoded and the base64
  /// property is set to true.
  ///
  /// ``` json
  /// JSON:
  /// {"Read":{"port":"/dev/ttyUSB",
  ///           "data": "Hello World"
  ///          }}
  ///
  /// {"Read":{"port":"/dev/ttyUSB",
  ///           "data": "SGVsbG8gV29ybGQ=",
  ///           "base64": true
  ///          }}
  /// ```
  Read {
    port: String,
    data: String,
    base64: Option<bool>,
  },
  /// Port was closed
  ///
  /// Sent in response to SerialRequest::Close
  /// or sent when the server detects that a serial
  /// port is misbehaving, closes it, and then
  /// notifies the clients
  ///
  ///``` json
  /// JSON:
  /// {"Closed":{"port":"/dev/ttyUSB"}}
  ///```
  Closed { port: String },
  /// Port was opened
  ///
  /// Sent in response to SerialRequest::Open
  ///
  ///``` json
  /// JSON:
  /// {"Opened":{"port":"/dev/ttyUSB"}}
  ///```
  Opened { port: String },
  /// Command successful
  Ok { msg: String },
  /// Wrote data
  ///
  /// Sent in response to SerialReques::Write
  ///
  /// Notifies the client that the request to write
  /// data was received, and the data was successfully
  /// written to the port specified
  ///
  /// ``` json
  /// JSON:
  /// {"Wrote":{"port":"/dev/ttyUSB"}}
  /// ```
  ///
  /// TODO: Return hash of data written?
  Wrote { port: String },
  /// Port successfully writelocked
  ///
  /// Sent in response to SerialReques::WriteLock
  ///
  /// Notifies the client that the WriteLock was received
  /// and completed successfully
  ///
  /// ``` json
  /// JSON:
  /// {"WriteLock":{"port":"/dev/ttyUSB"}}
  /// ```
  WriteLocked { port: String },
  /// WriteLocks on Port(s) successfully released
  ///
  /// Sent in response to SerialReques::ReleaseWriteLock
  ///
  /// Notifies the client that the ReleaseWriteLock was received
  /// and completed successfully
  ///
  /// If ReleaseWrite was sent without a port specified, then
  /// it will be empty in the response. This means that all
  /// write locks held on behalf of the client were released.
  ///
  /// ``` json
  /// JSON:
  /// {"WriteLockReleased":{"port":"/dev/ttyUSB"}}
  ///
  /// {"WriteLockReleased":{}}
  /// ```
  WriteLockReleased { port: Option<String> },
  /// List serial ports response
  ///
  /// ``` json
  /// JSON:
  /// {"List":{"ports":["/dev/ttyUSB0","/dev/ttyUSB1"]}}
  /// ```
  List { ports: Vec<String> },
}

/*
// TODO: Uncomment when new serialport module version drops
/// Needed for Serde Support as
/// SerialPortInfo is in seperate module
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(remote = "SerialPortInfo")]
pub struct SerialPortInfoDef {
    /// The short name of the serial port
    pub port_name: String,
    /// The hardware device type that exposes this port
    pub port_type: SerialPortType,

}

#[derive(Serialize, Deserialize)]
#[serde(remote = "sp::SerialPortType")]
/// The physical type of a `SerialPort`
pub enum SerialPortTypeDef {
    /// The serial port is connected via USB
    UsbPort(UsbPortInfoDef),
    /// The serial port is connected via PCI (permanent port)
    PciPort,
    /// The serial port is connected via Bluetooth
    BluetoothPort,
    /// It can't be determined how the serial port is connected
    Unknown,
}

#[derive(Serialize, Deserialize)]
#[serde(remote = "UsbPortInfo")]
pub struct UsbPortInfoDef {
    /// Vender ID
    pub vid: u16,
    /// Product ID
    pub pid: u16,
    /// Serial number (arbitrary string)
    pub serial_number: Option<String>,
    /// Manufacturer (arbitrary string)
    pub manufacturer: Option<String>,
    /// Product name (arbitrary string)
    pub product: Option<String>,
}
*/

impl fmt::Display for SerialResponse {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let json = serde_json::to_string(self).unwrap_or(
      "Display SerialResponse: Serialization Failed!"
        .to_string(),
    );
    write!(f, "{}", json)
  }
}
<|endoftext|>


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

use std::{
    borrow::Borrow,
    convert::TryFrom,
    fmt, io,
    ops::Deref,
    path::{Path, PathBuf},
    rc::Rc,
};

mod actions;
mod keys;

pub use crate::engine::types::{
    actions::{
        CreateAction, DeleteAction, EngineAction, MappingCreateAction, RenameAction,
        SetCreateAction, SetDeleteAction, SetUnlockAction,
    },
    keys::{EncryptionInfo, KeyDescription, SizedKeyMemory},
};
use crate::stratis::{ErrorEnum, StratisError, StratisResult};

use uuid::Uuid;

pub type DevUuid = Uuid;
pub type FilesystemUuid = Uuid;
pub type PoolUuid = Uuid;

/// Use Clevis or keyring to unlock LUKS volume.
#[derive(Clone, Copy)]
pub enum UnlockMethod {
    Clevis,
    Keyring,
}

impl<'a> TryFrom<&'a str> for UnlockMethod {
    type Error = StratisError;

    fn try_from(s: &str) -> StratisResult<UnlockMethod> {
        match s {
            "keyring" => Ok(UnlockMethod::Keyring),
            "clevis" => Ok(UnlockMethod::Clevis),
            _ => Err(StratisError::Error(format!(
                "{} is an invalid unlock method",
                s
            ))),
        }
    }
}

/// See Design Doc section 10.2.1 for more details.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlockDevState {
    Missing = 0,
    Bad = 1,
    Spare = 2,
    NotInUse = 3,
    InUse = 4,
}

/// A struct that may contain a dbus::Path, or may not, and most certainly
/// doesn't if dbus is compiled out. This avoids littering engine code with
/// conditional code.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MaybeDbusPath(
    #[cfg(feature = "dbus_enabled")] pub Option<dbus::Path<'static>>,
    #[cfg(not(feature = "dbus_enabled"))] pub Option<()>,
);

/// Blockdev tier. Used to distinguish between blockdevs used for
/// data and blockdevs used for a cache.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlockDevTier {
    Data = 0,
    Cache = 1,
}

/// Redundancy classifications which the engine allows for pools.
#[derive(Debug, Eq, PartialEq)]
#[allow(non_camel_case_types)]
/// Redundancy specification for a pool.
pub enum Redundancy {
    NONE = 0,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Name(Rc<String>);

impl Name {
    pub fn new(name: String) -> Name {
        Name(Rc::new(name))
    }

    pub fn to_owned(&self) -> String {
        self.0.deref().to_owned()
    }
}

impl AsRef<str> for Name {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Deref for Name {
    type Target = str;

    fn deref(&self) -> &str {
        &self.0
    }
}

impl Borrow<str> for Name {
    fn borrow(&self) -> &str {
        &**self.0
    }
}

impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// The type of report for which to query.
///
/// * `ErroredPoolDevices` returns the state of devices that caused an error while
/// attempting to reconstruct a pool.
/// * `EngineState` returns the entire state of the engine.
pub enum ReportType {
    ErroredPoolDevices,
    EngineState,
}

impl<'a> TryFrom<&'a str> for ReportType {
    type Error = StratisError;

    fn try_from(name: &str) -> StratisResult<ReportType> {
        match name {
            "errored_pool_report" => Ok(ReportType::ErroredPoolDevices),
            "engine_state_report" => Ok(ReportType::EngineState),
            _ => Err(StratisError::Engine(
                ErrorEnum::NotFound,
                format!("Report name {} not understood", name),
            )),
        }
    }
}

#[derive(Clone, Debug)]
pub struct BlockDevPath {
    /// Path to the physical device.
    physical_path: PathBuf,
    /// Optional tuple for encrypted devices. The first path is the internal
    /// path of the logical device and the second is the canonicalized path
    /// of the logical device.
    logical_path: Option<(PathBuf, PathBuf)>,
}

impl BlockDevPath {
    /// Path for a device that only has a physical path.
    pub fn physical_device_path(physical: &Path) -> BlockDevPath {
        BlockDevPath {
            physical_path: physical.to_owned(),
            logical_path: None,
        }
    }

    /// Path for a device that has both an associated physical path and a mapped
    /// logical path.
    pub fn mapped_device_path(physical: &Path, logical: &Path) -> Result<BlockDevPath, io::Error> {
        let logical_path = Some((logical.to_owned(), logical.canonicalize()?));
        Ok(BlockDevPath {
            physical_path: physical.to_owned(),
            logical_path,
        })
    }

    /// Path to the physical device storing the data.
    pub fn physical_path(&self) -> &Path {
        &self.physical_path
    }

    /// Path to the physical or logical device where the Stratis metadata should
    /// be written.
    pub fn metadata_path(&self) -> &Path {
        if let Some((ref path, _)) = self.logical_path {
            path
        } else {
            &self.physical_path
        }
    }

    /// This is the path of devices that will be reported to the user.
    ///
    /// In the case of an encrypted device, this will be the canonicalized path of
    /// the path of the device path where metadata should be written. In the
    /// case of an unencrypted device, it returns the user provided path.
    pub fn user_path(&self) -> &Path {
        if let Some((_, ref path)) = self.logical_path {
            path
        } else {
            &self.physical_path
        }
    }
}
<commit_msg>Explicitly allow rc_buffer clippy lint<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{
    borrow::Borrow,
    convert::TryFrom,
    fmt, io,
    ops::Deref,
    path::{Path, PathBuf},
    rc::Rc,
};

mod actions;
mod keys;

pub use crate::engine::types::{
    actions::{
        CreateAction, DeleteAction, EngineAction, MappingCreateAction, RenameAction,
        SetCreateAction, SetDeleteAction, SetUnlockAction,
    },
    keys::{EncryptionInfo, KeyDescription, SizedKeyMemory},
};
use crate::stratis::{ErrorEnum, StratisError, StratisResult};

use uuid::Uuid;

pub type DevUuid = Uuid;
pub type FilesystemUuid = Uuid;
pub type PoolUuid = Uuid;

/// Use Clevis or keyring to unlock LUKS volume.
#[derive(Clone, Copy)]
pub enum UnlockMethod {
    Clevis,
    Keyring,
}

impl<'a> TryFrom<&'a str> for UnlockMethod {
    type Error = StratisError;

    fn try_from(s: &str) -> StratisResult<UnlockMethod> {
        match s {
            "keyring" => Ok(UnlockMethod::Keyring),
            "clevis" => Ok(UnlockMethod::Clevis),
            _ => Err(StratisError::Error(format!(
                "{} is an invalid unlock method",
                s
            ))),
        }
    }
}

/// See Design Doc section 10.2.1 for more details.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlockDevState {
    Missing = 0,
    Bad = 1,
    Spare = 2,
    NotInUse = 3,
    InUse = 4,
}

/// A struct that may contain a dbus::Path, or may not, and most certainly
/// doesn't if dbus is compiled out. This avoids littering engine code with
/// conditional code.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MaybeDbusPath(
    #[cfg(feature = "dbus_enabled")] pub Option<dbus::Path<'static>>,
    #[cfg(not(feature = "dbus_enabled"))] pub Option<()>,
);

/// Blockdev tier. Used to distinguish between blockdevs used for
/// data and blockdevs used for a cache.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlockDevTier {
    Data = 0,
    Cache = 1,
}

/// Redundancy classifications which the engine allows for pools.
#[derive(Debug, Eq, PartialEq)]
#[allow(non_camel_case_types)]
/// Redundancy specification for a pool.
pub enum Redundancy {
    NONE = 0,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[allow(clippy::rc_buffer)]
pub struct Name(Rc<String>);

impl Name {
    pub fn new(name: String) -> Name {
        Name(Rc::new(name))
    }

    pub fn to_owned(&self) -> String {
        self.0.deref().to_owned()
    }
}

impl AsRef<str> for Name {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Deref for Name {
    type Target = str;

    fn deref(&self) -> &str {
        &self.0
    }
}

impl Borrow<str> for Name {
    fn borrow(&self) -> &str {
        &**self.0
    }
}

impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// The type of report for which to query.
///
/// * `ErroredPoolDevices` returns the state of devices that caused an error while
/// attempting to reconstruct a pool.
/// * `EngineState` returns the entire state of the engine.
pub enum ReportType {
    ErroredPoolDevices,
    EngineState,
}

impl<'a> TryFrom<&'a str> for ReportType {
    type Error = StratisError;

    fn try_from(name: &str) -> StratisResult<ReportType> {
        match name {
            "errored_pool_report" => Ok(ReportType::ErroredPoolDevices),
            "engine_state_report" => Ok(ReportType::EngineState),
            _ => Err(StratisError::Engine(
                ErrorEnum::NotFound,
                format!("Report name {} not understood", name),
            )),
        }
    }
}

#[derive(Clone, Debug)]
pub struct BlockDevPath {
    /// Path to the physical device.
    physical_path: PathBuf,
    /// Optional tuple for encrypted devices. The first path is the internal
    /// path of the logical device and the second is the canonicalized path
    /// of the logical device.
    logical_path: Option<(PathBuf, PathBuf)>,
}

impl BlockDevPath {
    /// Path for a device that only has a physical path.
    pub fn physical_device_path(physical: &Path) -> BlockDevPath {
        BlockDevPath {
            physical_path: physical.to_owned(),
            logical_path: None,
        }
    }

    /// Path for a device that has both an associated physical path and a mapped
    /// logical path.
    pub fn mapped_device_path(physical: &Path, logical: &Path) -> Result<BlockDevPath, io::Error> {
        let logical_path = Some((logical.to_owned(), logical.canonicalize()?));
        Ok(BlockDevPath {
            physical_path: physical.to_owned(),
            logical_path,
        })
    }

    /// Path to the physical device storing the data.
    pub fn physical_path(&self) -> &Path {
        &self.physical_path
    }

    /// Path to the physical or logical device where the Stratis metadata should
    /// be written.
    pub fn metadata_path(&self) -> &Path {
        if let Some((ref path, _)) = self.logical_path {
            path
        } else {
            &self.physical_path
        }
    }

    /// This is the path of devices that will be reported to the user.
    ///
    /// In the case of an encrypted device, this will be the canonicalized path of
    /// the path of the device path where metadata should be written. In the
    /// case of an unencrypted device, it returns the user provided path.
    pub fn user_path(&self) -> &Path {
        if let Some((_, ref path)) = self.logical_path {
            path
        } else {
            &self.physical_path
        }
    }
}
<|endoftext|>


<commit_before>use index::btree::external::ExtNode;
use index::btree::node::read_unchecked;
use index::btree::node::NodeData;
use index::btree::BPlusTree;
use index::btree::NodeCellRef;
use index::id_from_key;
use index::EntryKey;
use index::Slice;
use serde_json;
use std::fmt::Debug;
use std::fs::File;
use std::io::Write;

#[derive(Serialize, Deserialize)]
struct DebugNode {
    keys: Vec<String>,
    nodes: Vec<DebugNode>,
    id: Option<String>,
    next: Option<String>,
    prev: Option<String>,
    len: usize,
    is_external: bool,
    bound: String,
}

pub fn dump_tree<KS, PS>(tree: &BPlusTree<KS, PS>, f: &str)
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("dumping {}", f);
    let debug_root = cascading_dump_node::<KS, PS>(&tree.get_root());
    let json = serde_json::to_string_pretty(&debug_root).unwrap();
    let mut file = File::create(f).unwrap();
    file.write_all(json.as_bytes());
}

fn cascading_dump_node<KS, PS>(node: &NodeCellRef) -> DebugNode
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    if node.is_default() {
        return DebugNode {
            keys: vec![String::from("<ERROR!!! DEFAULT NODE!!!>")],
            nodes: vec![],
            id: None,
            next: None,
            prev: None,
            len: 0,
            is_external: false,
            bound: "-".to_string(),
        };
    }
    let node = read_unchecked(&*node);
    match &*node {
        &NodeData::External(ref node) => {
            let node: &ExtNode<KS, PS> = node;
            let keys = node.keys.as_slice_immute()[..node.len]
                .iter()
                .map(|key| {
                    let id = id_from_key(key);
                    format!("{}\t{:?}", id.lower, key)
                })
                .collect();
            return DebugNode {
                keys,
                nodes: vec![],
                id: Some(format!("{:?}", node.id)),
                next: Some(format!(
                    "{:?}",
                    read_unchecked::<KS, PS>(&node.next).ext_id()
                )),
                prev: Some(format!(
                    "{:?}",
                    read_unchecked::<KS, PS>(&node.prev).ext_id()
                )),
                len: node.len,
                is_external: true,
                bound: format!("{:?}", node.right_bound),
            };
        }
        &NodeData::Internal(ref innode) => {
            let len = innode.len;
            let keys = innode.keys.as_slice_immute()[..node.len()]
                .iter()
                .map(|key| format!("{:?}", key))
                .collect();
            let nodes = innode.ptrs.as_slice_immute()[..node.len() + 1]
                .iter()
                .map(|node_ref| cascading_dump_node::<KS, PS>(node_ref))
                .collect();
            return DebugNode {
                keys,
                nodes,
                id: None,
                next: Some(innode.right.to_string::<KS, PS>()),
                prev: None,
                len,
                is_external: false,
                bound: format!("{:?}", innode.right_bound),
            };
        }
        &NodeData::None => {
            return DebugNode {
                keys: vec![String::from("<NOT FOUND>")],
                nodes: vec![],
                id: None,
                next: None,
                prev: None,
                len: 0,
                is_external: false,
                bound: "-".to_string(),
            };
        }
        &NodeData::Empty(ref n) => {
            return DebugNode {
                keys: vec![String::from("<EMPTY>")],
                nodes: vec![],
                id: None,
                next: Some(n.right.to_string::<KS, PS>()),
                prev: None,
                len: 0,
                is_external: false,
                bound: "-".to_string(),
            };
        }
    }
}
<commit_msg>safer tree dump<commit_after>use index::btree::external::ExtNode;
use index::btree::node::read_unchecked;
use index::btree::node::NodeData;
use index::btree::BPlusTree;
use index::btree::NodeCellRef;
use index::id_from_key;
use index::EntryKey;
use index::Slice;
use serde_json;
use std::fmt::Debug;
use std::fs::File;
use std::io::Write;
use itertools::Itertools;

#[derive(Serialize, Deserialize)]
struct DebugNode {
    keys: Vec<String>,
    nodes: Vec<DebugNode>,
    id: Option<String>,
    next: Option<String>,
    prev: Option<String>,
    len: usize,
    is_external: bool,
    bound: String,
}

pub fn dump_tree<KS, PS>(tree: &BPlusTree<KS, PS>, f: &str)
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("dumping {}", f);
    let debug_root = cascading_dump_node::<KS, PS>(&tree.get_root());
    let json = serde_json::to_string_pretty(&debug_root).unwrap();
    let mut file = File::create(f).unwrap();
    file.write_all(json.as_bytes());
}

fn cascading_dump_node<KS, PS>(node: &NodeCellRef) -> DebugNode
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    if node.is_default() {
        return DebugNode {
            keys: vec![String::from("<ERROR!!! DEFAULT NODE!!!>")],
            nodes: vec![],
            id: None,
            next: None,
            prev: None,
            len: 0,
            is_external: false,
            bound: "-".to_string(),
        };
    }
    let node = read_unchecked(&*node);
    match &*node {
        &NodeData::External(ref node) => {
            let node: &ExtNode<KS, PS> = node;
            let keys = node.keys.as_slice_immute()[..node.len]
                .iter()
                .map(|key| {
                    let id = id_from_key(key);
                    format!("{}\t{:?}", id.lower, key)
                })
                .collect();
            return DebugNode {
                keys,
                nodes: vec![],
                id: Some(format!("{:?}", node.id)),
                next: Some(format!(
                    "{:?}",
                    read_unchecked::<KS, PS>(&node.next).ext_id()
                )),
                prev: Some(format!(
                    "{:?}",
                    read_unchecked::<KS, PS>(&node.prev).ext_id()
                )),
                len: node.len,
                is_external: true,
                bound: format!("{:?}", node.right_bound),
            };
        }
        &NodeData::Internal(ref innode) => {
            let len = innode.len;
            let keys = innode.keys.as_slice_immute()[..node.len()]
                .iter()
                .map(|key| format!("{:?}", key))
                .collect();
            let nodes = innode.ptrs.as_slice_immute()[..node.len() + 1]
                .iter()
                .cloned()
                .collect_vec()
                .into_iter()
                .map(|node_ref| cascading_dump_node::<KS, PS>(&node_ref))
                .collect();
            return DebugNode {
                keys,
                nodes,
                id: None,
                next: Some(innode.right.to_string::<KS, PS>()),
                prev: None,
                len,
                is_external: false,
                bound: format!("{:?}", innode.right_bound),
            };
        }
        &NodeData::None => {
            return DebugNode {
                keys: vec![String::from("<NOT FOUND>")],
                nodes: vec![],
                id: None,
                next: None,
                prev: None,
                len: 0,
                is_external: false,
                bound: "-".to_string(),
            };
        }
        &NodeData::Empty(ref n) => {
            return DebugNode {
                keys: vec![String::from("<EMPTY>")],
                nodes: vec![],
                id: None,
                next: Some(n.right.to_string::<KS, PS>()),
                prev: None,
                len: 0,
                is_external: false,
                bound: "-".to_string(),
            };
        }
    }
}
<|endoftext|>


<commit_before>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq, Clone)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug, Clone, Copy)]
/// Represents a position in the source file.
/// Both line and column are represented by a 1-based
/// index, since this struct exists only to provide
/// context to the user.
pub struct Position {
    pub line: usize,
    column: usize,
}

impl Position {
    fn initial() -> Position {
        Position { line: 1, column: 1 }
    }

    fn increment_column(&mut self) -> () {
        self.column += 1;
    }

    fn increment_line(&mut self) -> () {
        self.line += 1;
        self.column = 1;
    }
}

pub type Lexeme = String;

#[derive(Debug, Clone)]
pub struct TokenWithContext {
    pub token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    pub lexeme: Lexeme,
    /// Position of the first character of the token
    pub position: Position,
}

#[derive(Debug, Clone)]
pub enum ScannerError {
    MissingStringTerminator(Position),
    UnexpectedCharacter(char, Position),
}

struct Scanner<'a> {
    current_position: Position,
    current_lexeme: String,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' | '\r' | '\t' | '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &'a str) -> Scanner {
        Scanner {
            current_position: Position::initial(),
            current_lexeme: "".into(),
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => match self.source.peek() {
                Some(&p2) => check1(p1) && check2(p2),
                None => false,
            },
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        let next = self.source.next();
        if let Some(c) = next {
            self.current_lexeme.push(c);
            if c == '\n' {
                self.current_position.increment_line();
            } else {
                self.current_position.increment_column();;
            }
        };
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn advance_while(&mut self, condition: &Fn(char) -> bool) -> () {
        while self.peek_check(condition) {
            self.advance();
        }
    }

    fn add_context(&mut self, token: Token, initial_position: Position) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            position: initial_position,
        }
    }

    fn string(&mut self) -> Result<Token, ScannerError> {
        self.advance_while(&|c| c != '"' && c != '\n');
        if !self.advance_if_match('"') {
            return Err(ScannerError::MissingStringTerminator(self.current_position));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self
            .current_lexeme
            .chars()
            .skip(1)
            .take(literal_length)
            .collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        self.advance_while(&is_digit);
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            self.advance_while(&is_digit);
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        self.advance_while(&is_alphanumeric);
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }
    }

    fn scan_next(&mut self) -> Option<Result<TokenWithContext, ScannerError>> {
        let initial_position = self.current_position;
        self.current_lexeme.clear();
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => return None,
        };
        let result = match next_char {
            '(' => Ok(Token::LeftParen),
            ')' => Ok(Token::RightParen),
            '{' => Ok(Token::LeftBrace),
            '}' => Ok(Token::RightBrace),
            ',' => Ok(Token::Comma),
            '.' => Ok(Token::Dot),
            '-' => Ok(Token::Minus),
            '+' => Ok(Token::Plus),
            ';' => Ok(Token::Semicolon),
            '*' => Ok(Token::Star),
            '!' => {
                if self.advance_if_match('=') {
                    Ok(Token::BangEqual)
                } else {
                    Ok(Token::Bang)
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Ok(Token::EqualEqual)
                } else {
                    Ok(Token::Equal)
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Ok(Token::LessEqual)
                } else {
                    Ok(Token::Less)
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Ok(Token::GreaterEqual)
                } else {
                    Ok(Token::Greater)
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    self.advance_while(&|c| c != '\n');
                    Ok(Token::Comment)
                } else {
                    Ok(Token::Slash)
                }
            }
            '"' => self.string(),
            c if is_whitespace(c) => Ok(Token::Whitespace),
            c if is_digit(c) => Ok(self.number()),
            c if is_alpha(c) => Ok(self.identifier()),
            c => Err(ScannerError::UnexpectedCharacter(c, self.current_position)),
        };
        Some(result.map(|token| self.add_context(token, initial_position)))
    }
}

struct TokensIterator<'a> {
    scanner: Scanner<'a>,
}

impl<'a> Iterator for TokensIterator<'a> {
    type Item = Result<TokenWithContext, ScannerError>;
    fn next(&mut self) -> Option<Self::Item> {
        self.scanner.scan_next()
    }
}

pub fn scan_into_iterator<'a>(
    source: &'a str,
) -> impl Iterator<Item = Result<TokenWithContext, ScannerError>> + 'a {
    TokensIterator {
        scanner: Scanner::initialize(source),
    }
}

pub fn scan(source: &str) -> (Vec<TokenWithContext>, Vec<ScannerError>) {
    let mut tokens = Vec::new();
    let mut errors = Vec::new();
    for result in scan_into_iterator(source) {
        match result {
            Ok(token_with_context) => {
                match token_with_context.token {
                    // Ignoring tokens we don't care about
                    Token::Comment | Token::Whitespace => {}
                    _ => tokens.push(token_with_context),
                };
            }
            Err(error) => errors.push(error),
        }
    }
    (tokens, errors)
}

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

    #[test]
    fn single_token() {
        let (tokens, _) = scan(&"+");
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let (tokens, _) = scan(&"1+2");
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn expression_with_whitespaces() {
        let (tokens, _) = scan(&"1 + 2");
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn assignement_with_comment() {
        let (tokens, _) = scan(&"var a = 1.0; // A comment");
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
    }

    #[test]
    fn multiline_statements() {
        let (tokens, _) = scan(
            &r#"var a = 1.0;
                var b = "Hello";"#,
        );
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[1].position.line, 1);
        assert_eq!(tokens[1].position.column, 5);
        assert_eq!(tokens[9].position.line, 2);
        assert_eq!(tokens[9].position.column, 32); // There are 16 spaces before the text!
    }

    #[test]
    fn expression_with_bad_character() {
        let (tokens, errors) = scan(&"1 + $2");
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(errors.len(), 1);
    }

    #[test]
    fn statements_with_unterminated_string() {
        let (tokens, errors) = scan(
            &r#"var a = "Unterminated;
                var b = "Hello";"#,
        );
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        // Literal not reported, it wasn't terminated
        // Semicolon got embedded into the string
        assert_eq!(tokens[3].token, Token::Var);
        assert_eq!(tokens[4].token, Token::Identifier("b".into()));
        assert_eq!(tokens[5].token, Token::Equal);
        assert_eq!(tokens[6].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[7].token, Token::Semicolon);
        assert_eq!(errors.len(), 1);
    }

}
<commit_msg>Unnecessary clone<commit_after>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq, Clone)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug, Clone, Copy)]
/// Represents a position in the source file.
/// Both line and column are represented by a 1-based
/// index, since this struct exists only to provide
/// context to the user.
pub struct Position {
    pub line: usize,
    column: usize,
}

impl Position {
    fn initial() -> Position {
        Position { line: 1, column: 1 }
    }

    fn increment_column(&mut self) -> () {
        self.column += 1;
    }

    fn increment_line(&mut self) -> () {
        self.line += 1;
        self.column = 1;
    }
}

pub type Lexeme = String;

#[derive(Debug)]
pub struct TokenWithContext {
    pub token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    pub lexeme: Lexeme,
    /// Position of the first character of the token
    pub position: Position,
}

#[derive(Debug, Clone)]
pub enum ScannerError {
    MissingStringTerminator(Position),
    UnexpectedCharacter(char, Position),
}

struct Scanner<'a> {
    current_position: Position,
    current_lexeme: String,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' | '\r' | '\t' | '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &'a str) -> Scanner {
        Scanner {
            current_position: Position::initial(),
            current_lexeme: "".into(),
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => match self.source.peek() {
                Some(&p2) => check1(p1) && check2(p2),
                None => false,
            },
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        let next = self.source.next();
        if let Some(c) = next {
            self.current_lexeme.push(c);
            if c == '\n' {
                self.current_position.increment_line();
            } else {
                self.current_position.increment_column();;
            }
        };
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn advance_while(&mut self, condition: &Fn(char) -> bool) -> () {
        while self.peek_check(condition) {
            self.advance();
        }
    }

    fn add_context(&mut self, token: Token, initial_position: Position) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            position: initial_position,
        }
    }

    fn string(&mut self) -> Result<Token, ScannerError> {
        self.advance_while(&|c| c != '"' && c != '\n');
        if !self.advance_if_match('"') {
            return Err(ScannerError::MissingStringTerminator(self.current_position));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self
            .current_lexeme
            .chars()
            .skip(1)
            .take(literal_length)
            .collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        self.advance_while(&is_digit);
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            self.advance_while(&is_digit);
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        self.advance_while(&is_alphanumeric);
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }
    }

    fn scan_next(&mut self) -> Option<Result<TokenWithContext, ScannerError>> {
        let initial_position = self.current_position;
        self.current_lexeme.clear();
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => return None,
        };
        let result = match next_char {
            '(' => Ok(Token::LeftParen),
            ')' => Ok(Token::RightParen),
            '{' => Ok(Token::LeftBrace),
            '}' => Ok(Token::RightBrace),
            ',' => Ok(Token::Comma),
            '.' => Ok(Token::Dot),
            '-' => Ok(Token::Minus),
            '+' => Ok(Token::Plus),
            ';' => Ok(Token::Semicolon),
            '*' => Ok(Token::Star),
            '!' => {
                if self.advance_if_match('=') {
                    Ok(Token::BangEqual)
                } else {
                    Ok(Token::Bang)
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Ok(Token::EqualEqual)
                } else {
                    Ok(Token::Equal)
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Ok(Token::LessEqual)
                } else {
                    Ok(Token::Less)
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Ok(Token::GreaterEqual)
                } else {
                    Ok(Token::Greater)
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    self.advance_while(&|c| c != '\n');
                    Ok(Token::Comment)
                } else {
                    Ok(Token::Slash)
                }
            }
            '"' => self.string(),
            c if is_whitespace(c) => Ok(Token::Whitespace),
            c if is_digit(c) => Ok(self.number()),
            c if is_alpha(c) => Ok(self.identifier()),
            c => Err(ScannerError::UnexpectedCharacter(c, self.current_position)),
        };
        Some(result.map(|token| self.add_context(token, initial_position)))
    }
}

struct TokensIterator<'a> {
    scanner: Scanner<'a>,
}

impl<'a> Iterator for TokensIterator<'a> {
    type Item = Result<TokenWithContext, ScannerError>;
    fn next(&mut self) -> Option<Self::Item> {
        self.scanner.scan_next()
    }
}

pub fn scan_into_iterator<'a>(
    source: &'a str,
) -> impl Iterator<Item = Result<TokenWithContext, ScannerError>> + 'a {
    TokensIterator {
        scanner: Scanner::initialize(source),
    }
}

pub fn scan(source: &str) -> (Vec<TokenWithContext>, Vec<ScannerError>) {
    let mut tokens = Vec::new();
    let mut errors = Vec::new();
    for result in scan_into_iterator(source) {
        match result {
            Ok(token_with_context) => {
                match token_with_context.token {
                    // Ignoring tokens we don't care about
                    Token::Comment | Token::Whitespace => {}
                    _ => tokens.push(token_with_context),
                };
            }
            Err(error) => errors.push(error),
        }
    }
    (tokens, errors)
}

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

    #[test]
    fn single_token() {
        let (tokens, _) = scan(&"+");
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let (tokens, _) = scan(&"1+2");
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn expression_with_whitespaces() {
        let (tokens, _) = scan(&"1 + 2");
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn assignement_with_comment() {
        let (tokens, _) = scan(&"var a = 1.0; // A comment");
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
    }

    #[test]
    fn multiline_statements() {
        let (tokens, _) = scan(
            &r#"var a = 1.0;
                var b = "Hello";"#,
        );
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[1].position.line, 1);
        assert_eq!(tokens[1].position.column, 5);
        assert_eq!(tokens[9].position.line, 2);
        assert_eq!(tokens[9].position.column, 32); // There are 16 spaces before the text!
    }

    #[test]
    fn expression_with_bad_character() {
        let (tokens, errors) = scan(&"1 + $2");
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(errors.len(), 1);
    }

    #[test]
    fn statements_with_unterminated_string() {
        let (tokens, errors) = scan(
            &r#"var a = "Unterminated;
                var b = "Hello";"#,
        );
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        // Literal not reported, it wasn't terminated
        // Semicolon got embedded into the string
        assert_eq!(tokens[3].token, Token::Var);
        assert_eq!(tokens[4].token, Token::Identifier("b".into()));
        assert_eq!(tokens[5].token, Token::Equal);
        assert_eq!(tokens[6].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[7].token, Token::Semicolon);
        assert_eq!(errors.len(), 1);
    }

}
<|endoftext|>