<commit_before>//! Argument passing

use crate::prelude::*;
use crate::value_and_place::assert_assignable;

use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose};
use rustc_target::abi::call::{
    ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode, Reg, RegKind,
};
use smallvec::{smallvec, SmallVec};

pub(super) trait ArgAbiExt<'tcx> {
    fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]>;
    fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>);
}

fn reg_to_abi_param(reg: Reg) -> AbiParam {
    let clif_ty = match (reg.kind, reg.size.bytes()) {
        (RegKind::Integer, 1) => types::I8,
        (RegKind::Integer, 2) => types::I16,
        (RegKind::Integer, 4) => types::I32,
        (RegKind::Integer, 8) => types::I64,
        (RegKind::Integer, 16) => types::I128,
        (RegKind::Float, 4) => types::F32,
        (RegKind::Float, 8) => types::F64,
        (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(),
        _ => unreachable!("{:?}", reg),
    };
    AbiParam::new(clif_ty)
}

fn apply_arg_attrs_to_abi_param(mut param: AbiParam, arg_attrs: ArgAttributes) -> AbiParam {
    match arg_attrs.arg_ext {
        RustcArgExtension::None => {}
        RustcArgExtension::Zext => param.extension = ArgumentExtension::Uext,
        RustcArgExtension::Sext => param.extension = ArgumentExtension::Sext,
    }
    param
}

fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> {
    let (rest_count, rem_bytes) = if cast.rest.unit.size.bytes() == 0 {
        (0, 0)
    } else {
        (
            cast.rest.total.bytes() / cast.rest.unit.size.bytes(),
            cast.rest.total.bytes() % cast.rest.unit.size.bytes(),
        )
    };

    if cast.prefix.iter().all(|x| x.is_none()) {
        // Simplify to a single unit when there is no prefix and size <= unit size
        if cast.rest.total <= cast.rest.unit.size {
            let clif_ty = match (cast.rest.unit.kind, cast.rest.unit.size.bytes()) {
                (RegKind::Integer, 1) => types::I8,
                (RegKind::Integer, 2) => types::I16,
                (RegKind::Integer, 3..=4) => types::I32,
                (RegKind::Integer, 5..=8) => types::I64,
                (RegKind::Integer, 9..=16) => types::I128,
                (RegKind::Float, 4) => types::F32,
                (RegKind::Float, 8) => types::F64,
                (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(),
                _ => unreachable!("{:?}", cast.rest.unit),
            };
            return smallvec![AbiParam::new(clif_ty)];
        }
    }

    // Create list of fields in the main structure
    let mut args = cast
        .prefix
        .iter()
        .flatten()
        .map(|&kind| {
            reg_to_abi_param(Reg {
                kind,
                size: cast.prefix_chunk_size,
            })
        })
        .chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit)))
        .collect::<SmallVec<_>>();

    // Append final integer
    if rem_bytes != 0 {
        // Only integers can be really split further.
        assert_eq!(cast.rest.unit.kind, RegKind::Integer);
        args.push(reg_to_abi_param(Reg {
            kind: RegKind::Integer,
            size: Size::from_bytes(rem_bytes),
        }));
    }

    args
}

impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
    fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]> {
        match self.mode {
            PassMode::Ignore => smallvec![],
            PassMode::Direct(attrs) => match &self.layout.abi {
                Abi::Scalar(scalar) => {
                    smallvec![apply_arg_attrs_to_abi_param(
                        AbiParam::new(scalar_to_clif_type(tcx, scalar.clone())),
                        attrs
                    )]
                }
                Abi::Vector { .. } => {
                    let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap();
                    smallvec![AbiParam::new(vector_ty)]
                }
                _ => unreachable!("{:?}", self.layout.abi),
            },
            PassMode::Pair(attrs_a, attrs_b) => match &self.layout.abi {
                Abi::ScalarPair(a, b) => {
                    let a = scalar_to_clif_type(tcx, a.clone());
                    let b = scalar_to_clif_type(tcx, b.clone());
                    smallvec![
                        apply_arg_attrs_to_abi_param(AbiParam::new(a), attrs_a),
                        apply_arg_attrs_to_abi_param(AbiParam::new(b), attrs_b),
                    ]
                }
                _ => unreachable!("{:?}", self.layout.abi),
            },
            PassMode::Cast(cast) => cast_target_to_abi_params(cast),
            PassMode::Indirect {
                attrs,
                extra_attrs: None,
                on_stack,
            } => {
                if on_stack {
                    let size = u32::try_from(self.layout.size.bytes()).unwrap();
                    smallvec![apply_arg_attrs_to_abi_param(
                        AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructArgument(size),),
                        attrs
                    )]
                } else {
                    smallvec![apply_arg_attrs_to_abi_param(
                        AbiParam::new(pointer_ty(tcx)),
                        attrs
                    )]
                }
            }
            PassMode::Indirect {
                attrs,
                extra_attrs: Some(extra_attrs),
                on_stack,
            } => {
                assert!(!on_stack);
                smallvec![
                    apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs),
                    apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), extra_attrs),
                ]
            }
        }
    }

    fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>) {
        match self.mode {
            PassMode::Ignore => (None, vec![]),
            PassMode::Direct(_) => match &self.layout.abi {
                Abi::Scalar(scalar) => (
                    None,
                    vec![AbiParam::new(scalar_to_clif_type(tcx, scalar.clone()))],
                ),
                Abi::Vector { .. } => {
                    let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap();
                    (None, vec![AbiParam::new(vector_ty)])
                }
                _ => unreachable!("{:?}", self.layout.abi),
            },
            PassMode::Pair(_, _) => match &self.layout.abi {
                Abi::ScalarPair(a, b) => {
                    let a = scalar_to_clif_type(tcx, a.clone());
                    let b = scalar_to_clif_type(tcx, b.clone());
                    (None, vec![AbiParam::new(a), AbiParam::new(b)])
                }
                _ => unreachable!("{:?}", self.layout.abi),
            },
            PassMode::Cast(cast) => (None, cast_target_to_abi_params(cast).into_iter().collect()),
            PassMode::Indirect {
                attrs: _,
                extra_attrs: None,
                on_stack,
            } => {
                assert!(!on_stack);
                (
                    Some(AbiParam::special(
                        pointer_ty(tcx),
                        ArgumentPurpose::StructReturn,
                    )),
                    vec![],
                )
            }
            PassMode::Indirect {
                attrs: _,
                extra_attrs: Some(_),
                on_stack: _,
            } => unreachable!("unsized return value"),
        }
    }
}

pub(super) fn to_casted_value<'tcx>(
    fx: &mut FunctionCx<'_, 'tcx, impl Module>,
    arg: CValue<'tcx>,
    cast: CastTarget,
) -> SmallVec<[Value; 2]> {
    let (ptr, meta) = arg.force_stack(fx);
    assert!(meta.is_none());
    let mut offset = 0;
    cast_target_to_abi_params(cast)
        .into_iter()
        .map(|param| {
            let val = ptr
                .offset_i64(fx, offset)
                .load(fx, param.value_type, MemFlags::new());
            offset += i64::from(param.value_type.bytes());
            val
        })
        .collect()
}

pub(super) fn from_casted_value<'tcx>(
    fx: &mut FunctionCx<'_, 'tcx, impl Module>,
    block_params: &[Value],
    layout: TyAndLayout<'tcx>,
    cast: CastTarget,
) -> CValue<'tcx> {
    let abi_params = cast_target_to_abi_params(cast);
    let size: u32 = abi_params
        .iter()
        .map(|param| param.value_type.bytes())
        .sum();
    // Stack slot size may be bigger for for example `[u8; 3]` which is packed into an `i32`.
    assert!(u64::from(size) >= layout.size.bytes());
    let stack_slot = fx.bcx.create_stack_slot(StackSlotData {
        kind: StackSlotKind::ExplicitSlot,
        // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to
        // specify stack slot alignment.
        size: (size + 15) / 16 * 16,
        offset: None,
    });
    let ptr = Pointer::new(fx.bcx.ins().stack_addr(pointer_ty(fx.tcx), stack_slot, 0));
    let mut offset = 0;
    let mut block_params_iter = block_params.into_iter().copied();
    for param in abi_params {
        let val = ptr.offset_i64(fx, offset).store(
            fx,
            block_params_iter.next().unwrap(),
            MemFlags::new(),
        );
        offset += i64::from(param.value_type.bytes());
        val
    }
    assert_eq!(block_params_iter.next(), None, "Leftover block param");
    CValue::by_ref(ptr, layout)
}

/// Get a set of values to be passed as function arguments.
pub(super) fn adjust_arg_for_abi<'tcx>(
    fx: &mut FunctionCx<'_, 'tcx, impl Module>,
    arg: CValue<'tcx>,
    arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
) -> SmallVec<[Value; 2]> {
    assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty);
    match arg_abi.mode {
        PassMode::Ignore => smallvec![],
        PassMode::Direct(_) => smallvec![arg.load_scalar(fx)],
        PassMode::Pair(_, _) => {
            let (a, b) = arg.load_scalar_pair(fx);
            smallvec![a, b]
        }
        PassMode::Cast(cast) => to_casted_value(fx, arg, cast),
        PassMode::Indirect { .. } => match arg.force_stack(fx) {
            (ptr, None) => smallvec![ptr.get_addr(fx)],
            (ptr, Some(meta)) => smallvec![ptr.get_addr(fx), meta],
        },
    }
}

/// Create a [`CValue`] containing the value of a function parameter adding clif function parameters
/// as necessary.
pub(super) fn cvalue_for_param<'tcx>(
    fx: &mut FunctionCx<'_, 'tcx, impl Module>,
    #[cfg_attr(not(debug_assertions), allow(unused_variables))] local: Option<mir::Local>,
    #[cfg_attr(not(debug_assertions), allow(unused_variables))] local_field: Option<usize>,
    arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
    block_params_iter: &mut impl Iterator<Item = Value>,
) -> Option<CValue<'tcx>> {
    let block_params = arg_abi
        .get_abi_param(fx.tcx)
        .into_iter()
        .map(|abi_param| {
            let block_param = block_params_iter.next().unwrap();
            assert_eq!(
                fx.bcx.func.dfg.value_type(block_param),
                abi_param.value_type
            );
            block_param
        })
        .collect::<SmallVec<[_; 2]>>();

    #[cfg(debug_assertions)]
    crate::abi::comments::add_arg_comment(
        fx,
        "arg",
        local,
        local_field,
        &block_params,
        arg_abi.mode,
        arg_abi.layout,
    );

    match arg_abi.mode {
        PassMode::Ignore => None,
        PassMode::Direct(_) => {
            assert_eq!(block_params.len(), 1, "{:?}", block_params);
            Some(CValue::by_val(block_params[0], arg_abi.layout))
        }
        PassMode::Pair(_, _) => {
            assert_eq!(block_params.len(), 2, "{:?}", block_params);
            Some(CValue::by_val_pair(
                block_params[0],
                block_params[1],
                arg_abi.layout,
            ))
        }
        PassMode::Cast(cast) => Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)),
        PassMode::Indirect {
            attrs: _,
            extra_attrs: None,
            on_stack: _,
        } => {
            assert_eq!(block_params.len(), 1, "{:?}", block_params);
            Some(CValue::by_ref(
                Pointer::new(block_params[0]),
                arg_abi.layout,
            ))
        }
        PassMode::Indirect {
            attrs: _,
            extra_attrs: Some(_),
            on_stack: _,
        } => {
            assert_eq!(block_params.len(), 2, "{:?}", block_params);
            Some(CValue::by_ref_unsized(
                Pointer::new(block_params[0]),
                block_params[1],
                arg_abi.layout,
            ))
        }
    }
}
<commit_msg>Handle PassMode::Cast in combination with #[repr(align)]<commit_after>//! Argument passing

use crate::prelude::*;
use crate::value_and_place::assert_assignable;

use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose};
use rustc_target::abi::call::{
    ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode, Reg, RegKind,
};
use smallvec::{smallvec, SmallVec};

pub(super) trait ArgAbiExt<'tcx> {
    fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]>;
    fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>);
}

fn reg_to_abi_param(reg: Reg) -> AbiParam {
    let clif_ty = match (reg.kind, reg.size.bytes()) {
        (RegKind::Integer, 1) => types::I8,
        (RegKind::Integer, 2) => types::I16,
        (RegKind::Integer, 4) => types::I32,
        (RegKind::Integer, 8) => types::I64,
        (RegKind::Integer, 16) => types::I128,
        (RegKind::Float, 4) => types::F32,
        (RegKind::Float, 8) => types::F64,
        (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(),
        _ => unreachable!("{:?}", reg),
    };
    AbiParam::new(clif_ty)
}

fn apply_arg_attrs_to_abi_param(mut param: AbiParam, arg_attrs: ArgAttributes) -> AbiParam {
    match arg_attrs.arg_ext {
        RustcArgExtension::None => {}
        RustcArgExtension::Zext => param.extension = ArgumentExtension::Uext,
        RustcArgExtension::Sext => param.extension = ArgumentExtension::Sext,
    }
    param
}

fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> {
    let (rest_count, rem_bytes) = if cast.rest.unit.size.bytes() == 0 {
        (0, 0)
    } else {
        (
            cast.rest.total.bytes() / cast.rest.unit.size.bytes(),
            cast.rest.total.bytes() % cast.rest.unit.size.bytes(),
        )
    };

    if cast.prefix.iter().all(|x| x.is_none()) {
        // Simplify to a single unit when there is no prefix and size <= unit size
        if cast.rest.total <= cast.rest.unit.size {
            let clif_ty = match (cast.rest.unit.kind, cast.rest.unit.size.bytes()) {
                (RegKind::Integer, 1) => types::I8,
                (RegKind::Integer, 2) => types::I16,
                (RegKind::Integer, 3..=4) => types::I32,
                (RegKind::Integer, 5..=8) => types::I64,
                (RegKind::Integer, 9..=16) => types::I128,
                (RegKind::Float, 4) => types::F32,
                (RegKind::Float, 8) => types::F64,
                (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(),
                _ => unreachable!("{:?}", cast.rest.unit),
            };
            return smallvec![AbiParam::new(clif_ty)];
        }
    }

    // Create list of fields in the main structure
    let mut args = cast
        .prefix
        .iter()
        .flatten()
        .map(|&kind| {
            reg_to_abi_param(Reg {
                kind,
                size: cast.prefix_chunk_size,
            })
        })
        .chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit)))
        .collect::<SmallVec<_>>();

    // Append final integer
    if rem_bytes != 0 {
        // Only integers can be really split further.
        assert_eq!(cast.rest.unit.kind, RegKind::Integer);
        args.push(reg_to_abi_param(Reg {
            kind: RegKind::Integer,
            size: Size::from_bytes(rem_bytes),
        }));
    }

    args
}

impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
    fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]> {
        match self.mode {
            PassMode::Ignore => smallvec![],
            PassMode::Direct(attrs) => match &self.layout.abi {
                Abi::Scalar(scalar) => {
                    smallvec![apply_arg_attrs_to_abi_param(
                        AbiParam::new(scalar_to_clif_type(tcx, scalar.clone())),
                        attrs
                    )]
                }
                Abi::Vector { .. } => {
                    let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap();
                    smallvec![AbiParam::new(vector_ty)]
                }
                _ => unreachable!("{:?}", self.layout.abi),
            },
            PassMode::Pair(attrs_a, attrs_b) => match &self.layout.abi {
                Abi::ScalarPair(a, b) => {
                    let a = scalar_to_clif_type(tcx, a.clone());
                    let b = scalar_to_clif_type(tcx, b.clone());
                    smallvec![
                        apply_arg_attrs_to_abi_param(AbiParam::new(a), attrs_a),
                        apply_arg_attrs_to_abi_param(AbiParam::new(b), attrs_b),
                    ]
                }
                _ => unreachable!("{:?}", self.layout.abi),
            },
            PassMode::Cast(cast) => cast_target_to_abi_params(cast),
            PassMode::Indirect {
                attrs,
                extra_attrs: None,
                on_stack,
            } => {
                if on_stack {
                    let size = u32::try_from(self.layout.size.bytes()).unwrap();
                    smallvec![apply_arg_attrs_to_abi_param(
                        AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructArgument(size),),
                        attrs
                    )]
                } else {
                    smallvec![apply_arg_attrs_to_abi_param(
                        AbiParam::new(pointer_ty(tcx)),
                        attrs
                    )]
                }
            }
            PassMode::Indirect {
                attrs,
                extra_attrs: Some(extra_attrs),
                on_stack,
            } => {
                assert!(!on_stack);
                smallvec![
                    apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs),
                    apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), extra_attrs),
                ]
            }
        }
    }

    fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>) {
        match self.mode {
            PassMode::Ignore => (None, vec![]),
            PassMode::Direct(_) => match &self.layout.abi {
                Abi::Scalar(scalar) => (
                    None,
                    vec![AbiParam::new(scalar_to_clif_type(tcx, scalar.clone()))],
                ),
                Abi::Vector { .. } => {
                    let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap();
                    (None, vec![AbiParam::new(vector_ty)])
                }
                _ => unreachable!("{:?}", self.layout.abi),
            },
            PassMode::Pair(_, _) => match &self.layout.abi {
                Abi::ScalarPair(a, b) => {
                    let a = scalar_to_clif_type(tcx, a.clone());
                    let b = scalar_to_clif_type(tcx, b.clone());
                    (None, vec![AbiParam::new(a), AbiParam::new(b)])
                }
                _ => unreachable!("{:?}", self.layout.abi),
            },
            PassMode::Cast(cast) => (None, cast_target_to_abi_params(cast).into_iter().collect()),
            PassMode::Indirect {
                attrs: _,
                extra_attrs: None,
                on_stack,
            } => {
                assert!(!on_stack);
                (
                    Some(AbiParam::special(
                        pointer_ty(tcx),
                        ArgumentPurpose::StructReturn,
                    )),
                    vec![],
                )
            }
            PassMode::Indirect {
                attrs: _,
                extra_attrs: Some(_),
                on_stack: _,
            } => unreachable!("unsized return value"),
        }
    }
}

pub(super) fn to_casted_value<'tcx>(
    fx: &mut FunctionCx<'_, 'tcx, impl Module>,
    arg: CValue<'tcx>,
    cast: CastTarget,
) -> SmallVec<[Value; 2]> {
    let (ptr, meta) = arg.force_stack(fx);
    assert!(meta.is_none());
    let mut offset = 0;
    cast_target_to_abi_params(cast)
        .into_iter()
        .map(|param| {
            let val = ptr
                .offset_i64(fx, offset)
                .load(fx, param.value_type, MemFlags::new());
            offset += i64::from(param.value_type.bytes());
            val
        })
        .collect()
}

pub(super) fn from_casted_value<'tcx>(
    fx: &mut FunctionCx<'_, 'tcx, impl Module>,
    block_params: &[Value],
    layout: TyAndLayout<'tcx>,
    cast: CastTarget,
) -> CValue<'tcx> {
    let abi_params = cast_target_to_abi_params(cast);
    let abi_param_size: u32 = abi_params
        .iter()
        .map(|param| param.value_type.bytes())
        .sum();
    let layout_size = u32::try_from(layout.size.bytes()).unwrap();
    let stack_slot = fx.bcx.create_stack_slot(StackSlotData {
        kind: StackSlotKind::ExplicitSlot,
        // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to
        // specify stack slot alignment.
        // Stack slot size may be bigger for for example `[u8; 3]` which is packed into an `i32`.
        // It may also be smaller for example when the type is a wrapper around an integer with a
        // larger alignment than the integer.
        size: (std::cmp::max(abi_param_size, layout_size) + 15) / 16 * 16,
        offset: None,
    });
    let ptr = Pointer::new(fx.bcx.ins().stack_addr(pointer_ty(fx.tcx), stack_slot, 0));
    let mut offset = 0;
    let mut block_params_iter = block_params.into_iter().copied();
    for param in abi_params {
        let val = ptr.offset_i64(fx, offset).store(
            fx,
            block_params_iter.next().unwrap(),
            MemFlags::new(),
        );
        offset += i64::from(param.value_type.bytes());
        val
    }
    assert_eq!(block_params_iter.next(), None, "Leftover block param");
    CValue::by_ref(ptr, layout)
}

/// Get a set of values to be passed as function arguments.
pub(super) fn adjust_arg_for_abi<'tcx>(
    fx: &mut FunctionCx<'_, 'tcx, impl Module>,
    arg: CValue<'tcx>,
    arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
) -> SmallVec<[Value; 2]> {
    assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty);
    match arg_abi.mode {
        PassMode::Ignore => smallvec![],
        PassMode::Direct(_) => smallvec![arg.load_scalar(fx)],
        PassMode::Pair(_, _) => {
            let (a, b) = arg.load_scalar_pair(fx);
            smallvec![a, b]
        }
        PassMode::Cast(cast) => to_casted_value(fx, arg, cast),
        PassMode::Indirect { .. } => match arg.force_stack(fx) {
            (ptr, None) => smallvec![ptr.get_addr(fx)],
            (ptr, Some(meta)) => smallvec![ptr.get_addr(fx), meta],
        },
    }
}

/// Create a [`CValue`] containing the value of a function parameter adding clif function parameters
/// as necessary.
pub(super) fn cvalue_for_param<'tcx>(
    fx: &mut FunctionCx<'_, 'tcx, impl Module>,
    #[cfg_attr(not(debug_assertions), allow(unused_variables))] local: Option<mir::Local>,
    #[cfg_attr(not(debug_assertions), allow(unused_variables))] local_field: Option<usize>,
    arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
    block_params_iter: &mut impl Iterator<Item = Value>,
) -> Option<CValue<'tcx>> {
    let block_params = arg_abi
        .get_abi_param(fx.tcx)
        .into_iter()
        .map(|abi_param| {
            let block_param = block_params_iter.next().unwrap();
            assert_eq!(
                fx.bcx.func.dfg.value_type(block_param),
                abi_param.value_type
            );
            block_param
        })
        .collect::<SmallVec<[_; 2]>>();

    #[cfg(debug_assertions)]
    crate::abi::comments::add_arg_comment(
        fx,
        "arg",
        local,
        local_field,
        &block_params,
        arg_abi.mode,
        arg_abi.layout,
    );

    match arg_abi.mode {
        PassMode::Ignore => None,
        PassMode::Direct(_) => {
            assert_eq!(block_params.len(), 1, "{:?}", block_params);
            Some(CValue::by_val(block_params[0], arg_abi.layout))
        }
        PassMode::Pair(_, _) => {
            assert_eq!(block_params.len(), 2, "{:?}", block_params);
            Some(CValue::by_val_pair(
                block_params[0],
                block_params[1],
                arg_abi.layout,
            ))
        }
        PassMode::Cast(cast) => Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)),
        PassMode::Indirect {
            attrs: _,
            extra_attrs: None,
            on_stack: _,
        } => {
            assert_eq!(block_params.len(), 1, "{:?}", block_params);
            Some(CValue::by_ref(
                Pointer::new(block_params[0]),
                arg_abi.layout,
            ))
        }
        PassMode::Indirect {
            attrs: _,
            extra_attrs: Some(_),
            on_stack: _,
        } => {
            assert_eq!(block_params.len(), 2, "{:?}", block_params);
            Some(CValue::by_ref_unsized(
                Pointer::new(block_params[0]),
                block_params[1],
                arg_abi.layout,
            ))
        }
    }
}
<|endoftext|>


<commit_before>use std::convert::TryInto;
use std::fmt;
use std::ops;

mod strftime;

use super::Date;

/// Liquid's native date + time type.
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
#[serde(transparent)]
#[repr(transparent)]
pub struct DateTime {
    #[serde(with = "friendly_date_time")]
    inner: DateTimeImpl,
}

type DateTimeImpl = time::OffsetDateTime;

impl DateTime {
    /// Create a `DateTime` from the current moment.
    pub fn now() -> Self {
        Self {
            inner: DateTimeImpl::now_utc(),
        }
    }

    /// Makes a new NaiveDate from the calendar date (year, month and day).
    ///
    /// Panics on the out-of-range date, invalid month and/or day.
    pub fn from_ymd(year: i32, month: u8, day: u8) -> Self {
        Self {
            inner: time::Date::from_calendar_date(
                year,
                month.try_into().expect("the month is out of range"),
                day,
            )
            .expect("one or more components were invalid")
            .with_hms(0, 0, 0)
            .expect("one or more components were invalid")
            .assume_offset(time::macros::offset!(UTC)),
        }
    }

    /// Convert a `str` to `Self`
    #[allow(clippy::should_implement_trait)]
    pub fn from_str(other: &str) -> Option<Self> {
        parse_date_time(other).map(|d| Self { inner: d })
    }

    /// Replace date with `other`.
    pub fn with_date(self, other: Date) -> Self {
        Self {
            inner: self.inner.replace_date(other.inner),
        }
    }

    /// Changes the associated time zone. This does not change the actual DateTime (but will change the string representation).
    pub fn with_offset(self, offset: time::UtcOffset) -> Self {
        Self {
            inner: self.inner.to_offset(offset),
        }
    }

    /// Retrieves a date component.
    pub fn date(self) -> Date {
        Date {
            inner: self.inner.date(),
        }
    }

    /// Formats the combined date and time with the specified format string.
    ///
    /// See the [chrono::format::strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)
    /// module on the supported escape sequences.
    #[inline]
    pub fn format(&self, fmt: &str) -> Result<String, strftime::DateFormatError> {
        strftime::strftime(self.inner, fmt)
    }
}

impl DateTime {
    /// Get the year of the date.
    #[inline]
    pub fn year(&self) -> i32 {
        self.inner.year()
    }
    /// Get the month.
    #[inline]
    pub fn month(&self) -> u8 {
        self.inner.month() as u8
    }
    /// Get the day of the month.
    ///
    //// The returned value will always be in the range 1..=31.
    #[inline]
    pub fn day(&self) -> u8 {
        self.inner.day()
    }
    /// Get the day of the year.
    ///
    /// The returned value will always be in the range 1..=366 (1..=365 for common years).
    #[inline]
    pub fn ordinal(&self) -> u16 {
        self.inner.ordinal()
    }
    /// Get the ISO week number.
    ///
    /// The returned value will always be in the range 1..=53.
    #[inline]
    pub fn iso_week(&self) -> u8 {
        self.inner.iso_week()
    }
}

impl Default for DateTime {
    fn default() -> Self {
        Self {
            inner: DateTimeImpl::UNIX_EPOCH,
        }
    }
}

const DATE_TIME_FORMAT: &[time::format_description::FormatItem<'static>] = time::macros::format_description!(
    "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]"
);

impl fmt::Display for DateTime {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            self.inner
                .format(DATE_TIME_FORMAT)
                .map_err(|_e| fmt::Error)?
        )
    }
}

impl ops::Deref for DateTime {
    type Target = DateTimeImpl;
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl ops::DerefMut for DateTime {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

mod friendly_date_time {
    use super::*;
    use serde::{self, Deserialize, Deserializer, Serializer};

    pub(crate) fn serialize<S>(date: &DateTimeImpl, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = date
            .format(DATE_TIME_FORMAT)
            .map_err(serde::ser::Error::custom)?;
        serializer.serialize_str(&s)
    }

    pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<DateTimeImpl, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s: std::borrow::Cow<'_, str> = Deserialize::deserialize(deserializer)?;
        DateTimeImpl::parse(&s, DATE_TIME_FORMAT).map_err(serde::de::Error::custom)
    }
}

fn parse_date_time(s: &str) -> Option<DateTimeImpl> {
    const USER_FORMATS: &[&[time::format_description::FormatItem<'_>]] = &[
        time::macros::format_description!("[day] [month repr:long] [year] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]"),
        time::macros::format_description!("[day] [month repr:short] [year] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]"),
        DATE_TIME_FORMAT,
    ];

    match s {
        "now" => Some(DateTimeImpl::now_utc()),
        _ => USER_FORMATS
            .iter()
            .filter_map(|f| DateTimeImpl::parse(s, f).ok())
            .next(),
    }
}

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

    #[test]
    fn parse_date_time_empty_is_bad() {
        let input = "";
        let actual = parse_date_time(input);
        assert!(actual.is_none());
    }

    #[test]
    fn parse_date_time_bad() {
        let input = "aaaaa";
        let actual = parse_date_time(input);
        assert!(actual.is_none());
    }

    #[test]
    fn parse_date_time_now() {
        let input = "now";
        let actual = parse_date_time(input);
        assert!(actual.is_some());
    }

    #[test]
    fn parse_date_time_serialized_format() {
        let input = "2016-02-16 10:00:00 +0100";
        let actual = parse_date_time(input);
        assert!(actual.is_some());
    }

    #[test]
    fn parse_date_time_to_string() {
        let date = DateTime::now();
        let input = date.to_string();
        let actual = parse_date_time(&input);
        assert!(actual.is_some());
    }
}
<commit_msg>fix(date): Add RFC 2822 formatting support<commit_after>use std::convert::TryInto;
use std::fmt;
use std::ops;

mod strftime;

use super::Date;

/// Liquid's native date + time type.
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
#[serde(transparent)]
#[repr(transparent)]
pub struct DateTime {
    #[serde(with = "friendly_date_time")]
    inner: DateTimeImpl,
}

type DateTimeImpl = time::OffsetDateTime;

impl DateTime {
    /// Create a `DateTime` from the current moment.
    pub fn now() -> Self {
        Self {
            inner: DateTimeImpl::now_utc(),
        }
    }

    /// Makes a new NaiveDate from the calendar date (year, month and day).
    ///
    /// Panics on the out-of-range date, invalid month and/or day.
    pub fn from_ymd(year: i32, month: u8, day: u8) -> Self {
        Self {
            inner: time::Date::from_calendar_date(
                year,
                month.try_into().expect("the month is out of range"),
                day,
            )
            .expect("one or more components were invalid")
            .with_hms(0, 0, 0)
            .expect("one or more components were invalid")
            .assume_offset(time::macros::offset!(UTC)),
        }
    }

    /// Convert a `str` to `Self`
    #[allow(clippy::should_implement_trait)]
    pub fn from_str(other: &str) -> Option<Self> {
        parse_date_time(other).map(|d| Self { inner: d })
    }

    /// Replace date with `other`.
    pub fn with_date(self, other: Date) -> Self {
        Self {
            inner: self.inner.replace_date(other.inner),
        }
    }

    /// Changes the associated time zone. This does not change the actual DateTime (but will change the string representation).
    pub fn with_offset(self, offset: time::UtcOffset) -> Self {
        Self {
            inner: self.inner.to_offset(offset),
        }
    }

    /// Retrieves a date component.
    pub fn date(self) -> Date {
        Date {
            inner: self.inner.date(),
        }
    }

    /// Formats the combined date and time with the specified format string.
    ///
    /// See the [chrono::format::strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)
    /// module on the supported escape sequences.
    #[inline]
    pub fn format(&self, fmt: &str) -> Result<String, strftime::DateFormatError> {
        strftime::strftime(self.inner, fmt)
    }

    /// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`.
    pub fn to_rfc2822(&self) -> String {
        self.inner
            .format(&time::format_description::well_known::Rfc2822)
            .expect("always valid")
    }
}

impl DateTime {
    /// Get the year of the date.
    #[inline]
    pub fn year(&self) -> i32 {
        self.inner.year()
    }
    /// Get the month.
    #[inline]
    pub fn month(&self) -> u8 {
        self.inner.month() as u8
    }
    /// Get the day of the month.
    ///
    //// The returned value will always be in the range 1..=31.
    #[inline]
    pub fn day(&self) -> u8 {
        self.inner.day()
    }
    /// Get the day of the year.
    ///
    /// The returned value will always be in the range 1..=366 (1..=365 for common years).
    #[inline]
    pub fn ordinal(&self) -> u16 {
        self.inner.ordinal()
    }
    /// Get the ISO week number.
    ///
    /// The returned value will always be in the range 1..=53.
    #[inline]
    pub fn iso_week(&self) -> u8 {
        self.inner.iso_week()
    }
}

impl Default for DateTime {
    fn default() -> Self {
        Self {
            inner: DateTimeImpl::UNIX_EPOCH,
        }
    }
}

const DATE_TIME_FORMAT: &[time::format_description::FormatItem<'static>] = time::macros::format_description!(
    "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]"
);

impl fmt::Display for DateTime {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            self.inner
                .format(DATE_TIME_FORMAT)
                .map_err(|_e| fmt::Error)?
        )
    }
}

impl ops::Deref for DateTime {
    type Target = DateTimeImpl;
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl ops::DerefMut for DateTime {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

mod friendly_date_time {
    use super::*;
    use serde::{self, Deserialize, Deserializer, Serializer};

    pub(crate) fn serialize<S>(date: &DateTimeImpl, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = date
            .format(DATE_TIME_FORMAT)
            .map_err(serde::ser::Error::custom)?;
        serializer.serialize_str(&s)
    }

    pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<DateTimeImpl, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s: std::borrow::Cow<'_, str> = Deserialize::deserialize(deserializer)?;
        DateTimeImpl::parse(&s, DATE_TIME_FORMAT).map_err(serde::de::Error::custom)
    }
}

fn parse_date_time(s: &str) -> Option<DateTimeImpl> {
    const USER_FORMATS: &[&[time::format_description::FormatItem<'_>]] = &[
        time::macros::format_description!("[day] [month repr:long] [year] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]"),
        time::macros::format_description!("[day] [month repr:short] [year] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]"),
        DATE_TIME_FORMAT,
    ];

    match s {
        "now" => Some(DateTimeImpl::now_utc()),
        _ => USER_FORMATS
            .iter()
            .filter_map(|f| DateTimeImpl::parse(s, f).ok())
            .next(),
    }
}

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

    #[test]
    fn parse_date_time_empty_is_bad() {
        let input = "";
        let actual = parse_date_time(input);
        assert!(actual.is_none());
    }

    #[test]
    fn parse_date_time_bad() {
        let input = "aaaaa";
        let actual = parse_date_time(input);
        assert!(actual.is_none());
    }

    #[test]
    fn parse_date_time_now() {
        let input = "now";
        let actual = parse_date_time(input);
        assert!(actual.is_some());
    }

    #[test]
    fn parse_date_time_serialized_format() {
        let input = "2016-02-16 10:00:00 +0100";
        let actual = parse_date_time(input);
        assert!(actual.is_some());
    }

    #[test]
    fn parse_date_time_to_string() {
        let date = DateTime::now();
        let input = date.to_string();
        let actual = parse_date_time(&input);
        assert!(actual.is_some());
    }
}
<|endoftext|>


<commit_before>use ast::AST;

#[deriving(Show, PartialEq)]
pub enum Error {
    UnboundVariable(String),
    UnappliableValue(AST),
    WrongArgumentType(AST),
    BadArity(String),
}

pub fn eval(value: AST) -> Result<AST, Error> {
    match value {
        AST::Atom(atom) =>
            Err(Error::UnboundVariable(atom)),
        AST::Bool(_b) =>
            Ok(value),
        AST::Integer(_i) =>
            Ok(value),
        AST::List(ref list) =>
            eval_list(list.as_slice()),
    }
}

fn eval_list(list: &[AST]) -> Result<AST, Error> {
    if list.is_empty() {
        return Ok(AST::List(vec!()));
    }

    let fun = list.head().unwrap();

    match fun {
        &AST::Atom(ref atom) if atom.as_slice() == "quote" =>
            eval_quote(list),
        &AST::Atom(ref atom) => {
            let args = try!(eval_args(list.tail()));
            apply(atom.as_slice(), args.as_slice())
        },
        _ =>
            Err(Error::UnappliableValue(fun.clone()))
    }
}

fn eval_args(args: &[AST]) -> Result<Vec<AST>, Error> {
    let mut out = Vec::with_capacity(args.len());

    for arg in args.iter() {
        let evald_arg = try!(eval(arg.clone()));
        out.push(evald_arg);
    }

    Ok(out)
}

fn apply(name: &str, args: &[AST]) -> Result<AST, Error> {
    match name {
        "+" => eval_fun_plus(args),
        "-" => eval_fun_minus(args),
        "*" => eval_fun_product(args),
        "/" => eval_fun_division(args),
        "=" => eval_fun_equals(args),
        "<" => eval_fun_less_than(args),
        "<=" => eval_fun_less_than_or_equal(args),
        ">" => eval_fun_greater_than(args),
        ">=" => eval_fun_greater_than_or_equal(args),
        "not" => eval_fun_not(args),
        _   => Err(Error::UnboundVariable(name.to_string()))
    }
}

fn eval_fun_plus(args: &[AST]) -> Result<AST, Error> {
    let args_ = try!(list_of_integers(args));
    let sum = args_.iter().fold(0, |sum, n| sum + *n);
    Ok(AST::Integer(sum))
}

fn eval_fun_minus(args_: &[AST]) -> Result<AST, Error> {
    let args = try!(list_of_integers(args_));

    if args.len() == 0 {
        return Err(Error::BadArity("-".to_string()))
    }

    let head = args.head().unwrap();
    let tail = args.tail();

    if tail.is_empty() {
        return Ok(AST::Integer(- *head))
    }

    let tail_sum = tail.iter().fold(0, |sum, n| sum + *n);
    Ok(AST::Integer(*head - tail_sum))
}

fn eval_fun_division(args_: &[AST]) -> Result<AST, Error> {
    let args = try!(list_of_integers(args_));

    if args.len() == 0 {
        return Err(Error::BadArity("/".to_string()))
    }

    let head = args.head().unwrap();
    let tail = args.tail();

    if tail.is_empty() {
        return Ok(AST::Integer(1 / *head))
    }

    let div = tail.iter().fold(*head, |div, n| div / *n);
    Ok(AST::Integer(div))
}

fn eval_fun_product(args_: &[AST]) -> Result<AST, Error> {
    let args = try!(list_of_integers(args_));
    let product = args.iter().fold(1, |product, n| product * *n);
    Ok(AST::Integer(product))
}

fn eval_fun_equals(args_: &[AST]) -> Result<AST, Error> {
    if args_.len() < 2 {
        return Ok(AST::Bool(true))
    }

    let args = try!(list_of_integers(args_));
    let head = args.head().unwrap();
    let outcome = args.iter().skip(1).all(|n| *n == *head);
    Ok(AST::Bool(outcome))
}

fn eval_fun_less_than(args_: &[AST]) -> Result<AST, Error> {
    if args_.len() < 2 {
        return Ok(AST::Bool(true))
    }

    let args = try!(list_of_integers(args_));
    let outcome = range(0, args.len() - 1).all(|i| args[i] < args[i + 1u]);
    Ok(AST::Bool(outcome))
}

fn eval_fun_less_than_or_equal(args_: &[AST]) -> Result<AST, Error> {
    if args_.len() < 2 {
        return Ok(AST::Bool(true))
    }

    let args = try!(list_of_integers(args_));
    let outcome = range(0, args.len() - 1).all(|i| args[i] <= args[i + 1u]);
    Ok(AST::Bool(outcome))
}

fn eval_fun_greater_than(args_: &[AST]) -> Result<AST, Error> {
    if args_.len() < 2 {
        return Ok(AST::Bool(true))
    }

    let args = try!(list_of_integers(args_));
    let outcome = range(0, args.len() - 1).all(|i| args[i] > args[i + 1u]);
    Ok(AST::Bool(outcome))
}

fn eval_fun_greater_than_or_equal(args_: &[AST]) -> Result<AST, Error> {
    if args_.len() < 2 {
        return Ok(AST::Bool(true))
    }

    let args = try!(list_of_integers(args_));
    let outcome = range(0, args.len() - 1).all(|i| args[i] >= args[i + 1u]);
    Ok(AST::Bool(outcome))
}

fn eval_fun_not(args: &[AST]) -> Result<AST, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity("not".to_string()))
    }

    let outcome = match args.head().unwrap() {
        &AST::Bool(false) => true,
        _                 => false,
    };

    Ok(AST::Bool(outcome))
}

fn eval_quote(list: &[AST]) -> Result<AST, Error> {
    Ok(list.tail()[0].clone())
}

fn list_of_integers(list: &[AST]) -> Result<Vec<i64>, Error> {
    let mut integers = Vec::with_capacity(list.len());

    for val in list.iter() {
        if let &AST::Integer(n) = val {
            integers.push(n);
        } else {
            return Err(Error::WrongArgumentType(val.clone()))
        };
    }

    Ok(integers)
}
<commit_msg>Removed duplication in eval regarding comparison procedures.<commit_after>use ast::AST;

#[deriving(Show, PartialEq)]
pub enum Error {
    UnboundVariable(String),
    UnappliableValue(AST),
    WrongArgumentType(AST),
    BadArity(String),
}

pub fn eval(value: AST) -> Result<AST, Error> {
    match value {
        AST::Atom(atom) =>
            Err(Error::UnboundVariable(atom)),
        AST::Bool(_b) =>
            Ok(value),
        AST::Integer(_i) =>
            Ok(value),
        AST::List(ref list) =>
            eval_list(list.as_slice()),
    }
}

fn eval_list(list: &[AST]) -> Result<AST, Error> {
    if list.is_empty() {
        return Ok(AST::List(vec!()));
    }

    let fun = list.head().unwrap();

    match fun {
        &AST::Atom(ref atom) if atom.as_slice() == "quote" =>
            eval_quote(list),
        &AST::Atom(ref atom) => {
            let args = try!(eval_args(list.tail()));
            apply(atom.as_slice(), args.as_slice())
        },
        _ =>
            Err(Error::UnappliableValue(fun.clone()))
    }
}

fn eval_args(args: &[AST]) -> Result<Vec<AST>, Error> {
    let mut out = Vec::with_capacity(args.len());

    for arg in args.iter() {
        let evald_arg = try!(eval(arg.clone()));
        out.push(evald_arg);
    }

    Ok(out)
}

fn apply(name: &str, args: &[AST]) -> Result<AST, Error> {
    match name {
        "+" => eval_fun_plus(args),
        "-" => eval_fun_minus(args),
        "*" => eval_fun_product(args),
        "/" => eval_fun_division(args),
        "=" => eval_fun_equals(args),
        "<" => eval_fun_less_than(args),
        "<=" => eval_fun_less_than_or_equal(args),
        ">" => eval_fun_greater_than(args),
        ">=" => eval_fun_greater_than_or_equal(args),
        "not" => eval_fun_not(args),
        _   => Err(Error::UnboundVariable(name.to_string()))
    }
}

fn eval_fun_plus(args: &[AST]) -> Result<AST, Error> {
    let args_ = try!(list_of_integers(args));
    let sum = args_.iter().fold(0, |sum, n| sum + *n);
    Ok(AST::Integer(sum))
}

fn eval_fun_minus(args_: &[AST]) -> Result<AST, Error> {
    let args = try!(list_of_integers(args_));

    if args.len() == 0 {
        return Err(Error::BadArity("-".to_string()))
    }

    let head = args.head().unwrap();
    let tail = args.tail();

    if tail.is_empty() {
        return Ok(AST::Integer(- *head))
    }

    let tail_sum = tail.iter().fold(0, |sum, n| sum + *n);
    Ok(AST::Integer(*head - tail_sum))
}

fn eval_fun_division(args_: &[AST]) -> Result<AST, Error> {
    let args = try!(list_of_integers(args_));

    if args.len() == 0 {
        return Err(Error::BadArity("/".to_string()))
    }

    let head = args.head().unwrap();
    let tail = args.tail();

    if tail.is_empty() {
        return Ok(AST::Integer(1 / *head))
    }

    let div = tail.iter().fold(*head, |div, n| div / *n);
    Ok(AST::Integer(div))
}

fn eval_fun_product(args_: &[AST]) -> Result<AST, Error> {
    let args = try!(list_of_integers(args_));
    let product = args.iter().fold(1, |product, n| product * *n);
    Ok(AST::Integer(product))
}

fn eval_fun_equals(args_: &[AST]) -> Result<AST, Error> {
    if args_.len() < 2 {
        return Ok(AST::Bool(true))
    }

    let args = try!(list_of_integers(args_));
    let head = args.head().unwrap();
    let outcome = args.iter().skip(1).all(|n| *n == *head);
    Ok(AST::Bool(outcome))
}

fn eval_fun_less_than(args: &[AST]) -> Result<AST, Error> {
    eval_fun_ord(args, |a, b| a < b)
}

fn eval_fun_less_than_or_equal(args: &[AST]) -> Result<AST, Error> {
    eval_fun_ord(args, |a, b| a <= b)
}

fn eval_fun_greater_than(args: &[AST]) -> Result<AST, Error> {
    eval_fun_ord(args, |a, b| a > b)
}

fn eval_fun_greater_than_or_equal(args: &[AST]) -> Result<AST, Error> {
    eval_fun_ord(args, |a, b| a >= b)
}

fn eval_fun_ord(args_: &[AST], cmp: |i64, i64| -> bool) -> Result<AST, Error> {
    if args_.len() < 2 {
        return Ok(AST::Bool(true))
    }

    let args = try!(list_of_integers(args_));
    let outcome = range(0, args.len() - 1).all(|i|
        cmp(args[i], args[i + 1u])
    );

    Ok(AST::Bool(outcome))
}

fn eval_fun_not(args: &[AST]) -> Result<AST, Error> {
    if args.len() != 1 {
        return Err(Error::BadArity("not".to_string()))
    }

    let outcome = match args.head().unwrap() {
        &AST::Bool(false) => true,
        _                 => false,
    };

    Ok(AST::Bool(outcome))
}

fn eval_quote(list: &[AST]) -> Result<AST, Error> {
    Ok(list.tail()[0].clone())
}

fn list_of_integers(list: &[AST]) -> Result<Vec<i64>, Error> {
    let mut integers = Vec::with_capacity(list.len());

    for val in list.iter() {
        if let &AST::Integer(n) = val {
            integers.push(n);
        } else {
            return Err(Error::WrongArgumentType(val.clone()))
        };
    }

    Ok(integers)
}
<|endoftext|>


<commit_before>use ast::AST;

#[deriving(Show, PartialEq)]
pub enum Error {
    UnboundVariable(String),
    UnappliableValue(AST),
    WrongArgumentType(AST),
    BadArity(String),
}

pub fn eval(value: AST) -> Result<AST, Error> {
    match value {
        AST::Atom(atom) =>
            Err(Error::UnboundVariable(atom)),
        AST::Bool(_b) =>
            Ok(value),
        AST::Integer(_i) =>
            Ok(value),
        AST::List(ref list) =>
            eval_list(list.as_slice()),
    }
}

fn eval_list(list: &[AST]) -> Result<AST, Error> {
    if list.is_empty() {
        return Ok(AST::List(vec!()));
    }

    let fun = list.head().unwrap();
    let args = list.tail();

    match fun {
        &AST::Atom(ref atom) =>
            eval_fun(atom.as_slice(), args),
        _ =>
            Err(Error::UnappliableValue(fun.clone()))
    }
}

fn eval_fun(name: &str, args: &[AST]) -> Result<AST, Error> {
    match name {
        "quote" => eval_fun_quote(args),
        "+"     => eval_fun_plus(args),
        "-"     => eval_fun_minus(args),
        "*"     => eval_fun_product(args),
        "/"     => eval_fun_division(args),
        _       => Err(Error::UnboundVariable(name.to_string()))
    }
}

fn eval_fun_plus(args: &[AST]) -> Result<AST, Error> {
    let mut sum: i64 = 0;

    for val in args.iter() {
        let evald_val = try!(eval(val.clone()));

        match evald_val {
            AST::Integer(n) =>
                sum = sum + n,
            _ =>
                return Err(Error::WrongArgumentType(evald_val))
        };
    }

    Ok(AST::Integer(sum))
}

fn eval_fun_minus(args: &[AST]) -> Result<AST, Error> {
    if args.len() == 0 {
        Err(Error::BadArity("-".to_string()))
    } else {
        let head = args.head().unwrap();
        let tail = args.tail();

        let evald_head = try!(eval(head.clone()));

        let start = match evald_head {
            AST::Integer(n) => n,
            _               => return Err(Error::WrongArgumentType(evald_head))
        };

        if tail.is_empty() {
            Ok(AST::Integer(-start))
        } else {
            let mut sum: i64 = 0;

            for val in tail.iter() {
                let evald_val = try!(eval(val.clone()));

                match evald_val {
                    AST::Integer(n) =>
                        sum = sum + n,
                    _ =>
                        return Err(Error::WrongArgumentType(evald_val))
                };
            }

            Ok(AST::Integer(start - sum))
        }
    }
}

fn eval_fun_division(args: &[AST]) -> Result<AST, Error> {
    if args.len() == 0 {
        Err(Error::BadArity("/".to_string()))
    } else {
        let head = args.head().unwrap();
        let tail = args.tail();

        let evald_head = try!(eval(head.clone()));

        let mut div = match evald_head {
            AST::Integer(n) => n,
            _               => return Err(Error::WrongArgumentType(evald_head))
        };

        if tail.is_empty() {
            Ok(AST::Integer(1 / div))
        } else {
            for val in tail.iter() {
                let evald_val = try!(eval(val.clone()));

                match evald_val {
                    AST::Integer(n) =>
                        div = div / n,
                    _ =>
                        return Err(Error::WrongArgumentType(evald_val))
                };
            }

            Ok(AST::Integer(div))
        }
    }
}

fn eval_fun_product(args: &[AST]) -> Result<AST, Error> {
    let mut product: i64 = 1;

    for val in args.iter() {
        let evald_val = try!(eval(val.clone()));

        match evald_val {
            AST::Integer(n) =>
                product = product * n,
            _ =>
                return Err(Error::WrongArgumentType(evald_val))
        };
    }

    Ok(AST::Integer(product))
}

fn eval_fun_quote(args: &[AST]) -> Result<AST, Error> {
    Ok(args[0].clone())
}
<commit_msg>Renamed eval_fun to apply.<commit_after>use ast::AST;

#[deriving(Show, PartialEq)]
pub enum Error {
    UnboundVariable(String),
    UnappliableValue(AST),
    WrongArgumentType(AST),
    BadArity(String),
}

pub fn eval(value: AST) -> Result<AST, Error> {
    match value {
        AST::Atom(atom) =>
            Err(Error::UnboundVariable(atom)),
        AST::Bool(_b) =>
            Ok(value),
        AST::Integer(_i) =>
            Ok(value),
        AST::List(ref list) =>
            eval_list(list.as_slice()),
    }
}

fn eval_list(list: &[AST]) -> Result<AST, Error> {
    if list.is_empty() {
        return Ok(AST::List(vec!()));
    }

    let fun = list.head().unwrap();
    let args = list.tail();

    match fun {
        &AST::Atom(ref atom) =>
            apply(atom.as_slice(), args),
        _ =>
            Err(Error::UnappliableValue(fun.clone()))
    }
}

fn apply(name: &str, args: &[AST]) -> Result<AST, Error> {
    match name {
        "quote" => eval_fun_quote(args),
        "+"     => eval_fun_plus(args),
        "-"     => eval_fun_minus(args),
        "*"     => eval_fun_product(args),
        "/"     => eval_fun_division(args),
        _       => Err(Error::UnboundVariable(name.to_string()))
    }
}

fn eval_fun_plus(args: &[AST]) -> Result<AST, Error> {
    let mut sum: i64 = 0;

    for val in args.iter() {
        let evald_val = try!(eval(val.clone()));

        match evald_val {
            AST::Integer(n) =>
                sum = sum + n,
            _ =>
                return Err(Error::WrongArgumentType(evald_val))
        };
    }

    Ok(AST::Integer(sum))
}

fn eval_fun_minus(args: &[AST]) -> Result<AST, Error> {
    if args.len() == 0 {
        Err(Error::BadArity("-".to_string()))
    } else {
        let head = args.head().unwrap();
        let tail = args.tail();

        let evald_head = try!(eval(head.clone()));

        let start = match evald_head {
            AST::Integer(n) => n,
            _               => return Err(Error::WrongArgumentType(evald_head))
        };

        if tail.is_empty() {
            Ok(AST::Integer(-start))
        } else {
            let mut sum: i64 = 0;

            for val in tail.iter() {
                let evald_val = try!(eval(val.clone()));

                match evald_val {
                    AST::Integer(n) =>
                        sum = sum + n,
                    _ =>
                        return Err(Error::WrongArgumentType(evald_val))
                };
            }

            Ok(AST::Integer(start - sum))
        }
    }
}

fn eval_fun_division(args: &[AST]) -> Result<AST, Error> {
    if args.len() == 0 {
        Err(Error::BadArity("/".to_string()))
    } else {
        let head = args.head().unwrap();
        let tail = args.tail();

        let evald_head = try!(eval(head.clone()));

        let mut div = match evald_head {
            AST::Integer(n) => n,
            _               => return Err(Error::WrongArgumentType(evald_head))
        };

        if tail.is_empty() {
            Ok(AST::Integer(1 / div))
        } else {
            for val in tail.iter() {
                let evald_val = try!(eval(val.clone()));

                match evald_val {
                    AST::Integer(n) =>
                        div = div / n,
                    _ =>
                        return Err(Error::WrongArgumentType(evald_val))
                };
            }

            Ok(AST::Integer(div))
        }
    }
}

fn eval_fun_product(args: &[AST]) -> Result<AST, Error> {
    let mut product: i64 = 1;

    for val in args.iter() {
        let evald_val = try!(eval(val.clone()));

        match evald_val {
            AST::Integer(n) =>
                product = product * n,
            _ =>
                return Err(Error::WrongArgumentType(evald_val))
        };
    }

    Ok(AST::Integer(product))
}

fn eval_fun_quote(args: &[AST]) -> Result<AST, Error> {
    Ok(args[0].clone())
}
<|endoftext|>


<commit_before>use std::process::exit;

use clap::ArgMatches;
use toml::Value;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::build_entry_path;
use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

pub fn retrieve(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("retrieve")
        .map(|scmd| {
            scmd.value_of("id")
                .map(|id| {
                    let path = build_entry_path(rt.store(), id);
                    if path.is_err() {
                        trace_error(&path.unwrap_err());
                        exit(1);
                    }
                    let path = path.unwrap();
                    debug!("path = {:?}", path);

                    rt.store()
                        // "id" must be present, enforced via clap spec
                        .retrieve(path)
                        .map(|e| print_entry(rt, scmd, e))
                        .map_err(|e| {
                            debug!("No entry.");
                            debug!("{}:", e);
                            trace_error(&e);
                        })
                })
        });
}

fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) {
    if do_print_raw(scmd) {
        debug!("Printing raw content...");
        println!("{}", e.to_str());
    } else if do_filter(scmd) {
        debug!("Filtering...");
        warn!("Filtering via header specs is currently now supported.");
        warn!("Will fail now!");
        unimplemented!()
    } else {
        debug!("Printing structured...");
        if do_print_header(scmd) {
            debug!("Printing header...");
            if do_print_header_as_json(rt.cli()) {
                debug!("Printing header as json...");
                warn!("Printing as JSON currently not supported.");
                warn!("Will fail now!");
                unimplemented!()
            } else {
                debug!("Printing header as TOML...");
                // We have to Value::Table() for Display
                println!("{}", Value::Table(e.get_header().clone().into()))
            }
        }

        if do_print_content(scmd) {
            debug!("Printing content...");
            println!("{}", e.get_content());
        }

    }
}

fn do_print_header(m: &ArgMatches) -> bool {
    m.is_present("header")
}

fn do_print_header_as_json(m: &ArgMatches) -> bool {
    m.is_present("header-json")
}

fn do_print_content(m: &ArgMatches) -> bool {
    m.is_present("content")
}

fn do_print_raw(m: &ArgMatches) -> bool {
    m.is_present("raw")
}

fn do_filter(m: &ArgMatches) -> bool {
    m.subcommand_matches("filter-header").is_some()
}

<commit_msg>Make print_entry() pub to be available in get() implementation<commit_after>use std::process::exit;

use clap::ArgMatches;
use toml::Value;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::build_entry_path;
use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

pub fn retrieve(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("retrieve")
        .map(|scmd| {
            scmd.value_of("id")
                .map(|id| {
                    let path = build_entry_path(rt.store(), id);
                    if path.is_err() {
                        trace_error(&path.unwrap_err());
                        exit(1);
                    }
                    let path = path.unwrap();
                    debug!("path = {:?}", path);

                    rt.store()
                        // "id" must be present, enforced via clap spec
                        .retrieve(path)
                        .map(|e| print_entry(rt, scmd, e))
                        .map_err(|e| {
                            debug!("No entry.");
                            debug!("{}:", e);
                            trace_error(&e);
                        })
                })
        });
}

pub fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) {
    if do_print_raw(scmd) {
        debug!("Printing raw content...");
        println!("{}", e.to_str());
    } else if do_filter(scmd) {
        debug!("Filtering...");
        warn!("Filtering via header specs is currently now supported.");
        warn!("Will fail now!");
        unimplemented!()
    } else {
        debug!("Printing structured...");
        if do_print_header(scmd) {
            debug!("Printing header...");
            if do_print_header_as_json(rt.cli()) {
                debug!("Printing header as json...");
                warn!("Printing as JSON currently not supported.");
                warn!("Will fail now!");
                unimplemented!()
            } else {
                debug!("Printing header as TOML...");
                // We have to Value::Table() for Display
                println!("{}", Value::Table(e.get_header().clone().into()))
            }
        }

        if do_print_content(scmd) {
            debug!("Printing content...");
            println!("{}", e.get_content());
        }

    }
}

fn do_print_header(m: &ArgMatches) -> bool {
    m.is_present("header")
}

fn do_print_header_as_json(m: &ArgMatches) -> bool {
    m.is_present("header-json")
}

fn do_print_content(m: &ArgMatches) -> bool {
    m.is_present("content")
}

fn do_print_raw(m: &ArgMatches) -> bool {
    m.is_present("raw")
}

fn do_filter(m: &ArgMatches) -> bool {
    m.subcommand_matches("filter-header").is_some()
}

<|endoftext|>


<commit_before>use super::*;
use std::collections::{BTreeMap, VecDeque};

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct TermId {
    address: Vec<usize>,
    compos_var_pos: usize,
}

impl From<(Vec<usize>, usize)> for TermId {
    fn from((address, compos_var_pos): (Vec<usize>, usize)) -> TermId {
        TermId { address, compos_var_pos }
    }
}

pub fn identify_terminals<A>(tree_map: &GornTree<Composition<A>>)
        -> (GornTree<Composition<TermId>>, BTreeMap<TermId, A>)
    where A: Clone,
{
    let mut identified_tree_map = GornTree::new();
    let mut terminal_map = BTreeMap::new();

    for (address, composition) in tree_map {
        let vec_compos = &composition.composition;
        let mut identified_compos = Vec::new();
        let mut compos_var_pos = 0;

        for component in vec_compos {
            let mut identified_compon = Vec::new();

            for variable in component {
                match variable {
                    &VarT::Var(x, y) => {
                        identified_compon.push(VarT::Var(x, y));
                    },
                    &VarT::T(ref terminal) => {
                        let terminal_id = TermId { address: address.clone(), compos_var_pos };
                        identified_compon.push(VarT::T(terminal_id.clone()));
                        terminal_map.insert(terminal_id, terminal.clone());
                    },
                };

                compos_var_pos += 1;
            }

            identified_compos.push(identified_compon);
        }

        identified_tree_map.insert(address.clone(), Composition::from(identified_compos));
    }

    (identified_tree_map, terminal_map)
}

pub fn to_negra<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>, sentence_id: usize)
        -> String
    where H: Clone + ToString,
          T: Clone + ToString,
{
    if !meets_negra_criteria(&tree_map) {
        panic!("The given tree does not meet the negra criteria! All rules must either consist \
                only of nonterminals or of exactly one terminal symbol.");
    }

    let negra_vector = to_negra_vector(&tree_map);
    let mut output = format!("#BOS {}\n", sentence_id);

    for (word, tag, parent) in negra_vector {
        output.push_str(&format!("{}\t{}\t--\t--\t{}\n", word, tag, parent));
    }

    output.push_str(&format!("#EOS {}", sentence_id));
    output
}

pub fn meets_negra_criteria<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>)
        -> bool
{
    for (_address, rule) in tree_map {
        let &PMCFGRule { head: _, tail: _, ref composition, weight: _ } = rule;
        let mut contains_nonterminal = false;
        let mut contains_terminal = false;

        for component in &composition.composition {
            for variable in component {
                match variable {
                    &VarT::Var(_, _) => {
                        if contains_terminal {
                            return false;
                        }

                        contains_nonterminal = true;
                    },
                    &VarT::T(_) => {
                        if contains_nonterminal || contains_terminal {
                            return false;
                        }

                        contains_terminal = true;
                    },
                }
            }
        }
    }

    true
}

pub fn to_negra_vector<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>)
        -> Vec<(String, String, usize)>
    where H: Clone + ToString,
          T: Clone + ToString,
{
    let (term_map, nonterminal_map) = to_term(&tree_map);
    let (identified_tree_map, terminal_map) = identify_terminals(&term_map);
    let evaluated_compos = evaluate(&identified_tree_map);

    let mut negra_vector = Vec::new();
    let mut rule_queue = VecDeque::new();
    let mut rule_number_map = GornTree::new();
    let mut rule_counter = 1;

    for component in evaluated_compos.composition {
        for variable in component {
            match variable {
                VarT::Var(_, _) => {
                    panic!("Nonterminals must not appear in a fully evaluated configuration!");
                },
                VarT::T(terminal_id) => {
                    let terminal_symbol = terminal_map.get(&terminal_id).unwrap();
                    let address = terminal_id.address;
                    let rule_label = nonterminal_map.get(&address).unwrap();

                    let mut parent_address = address;
                    let parent_number = if let None = parent_address.pop() {
                        panic!("Terminals must have a nonterminal-only rule as their parent!");
                    } else {
                        get_rule_number(parent_address, &mut rule_queue, &mut rule_number_map,
                                        &mut rule_counter)
                    };
                    negra_vector.push((terminal_symbol.to_string(), rule_label.to_string(), parent_number));
                },
            }
        }
    }

    while let Some((address, rule_number)) = rule_queue.pop_front() {
        let mut parent_address = address.clone();

        let parent_number = if let None = parent_address.pop() {
            0
        } else {
            get_rule_number(parent_address, &mut rule_queue, &mut rule_number_map, &mut rule_counter)
        };

        let rule_label = nonterminal_map.get(&address).unwrap();
        negra_vector.push((format!("#{}", rule_number), rule_label.to_string(), parent_number));
    }

    negra_vector
}

fn get_rule_number(address: Vec<usize>, rule_queue: &mut VecDeque<(Vec<usize>, usize)>, rule_number_map: &mut GornTree<usize>, rule_counter: &mut usize)
        -> usize
{
    if let Some(rule_number) = rule_number_map.get(&address) {
        return *rule_number
    }

    let rule_number = *rule_counter;
    *rule_counter = *rule_counter + 1;

    rule_number_map.insert(address.clone(), rule_number.clone());
    rule_queue.push_back((address, rule_number));
    rule_number
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::tests::*;
    use self::VarT::{Var, T};
    use std::str::FromStr;

    #[test]
    fn test_identify_terminals() {
        let mut tree_map = GornTree::new();
        tree_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T("a"), Var(0, 1), T("b")]
        ]));
        tree_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T("c")]
        ]));
        tree_map.insert(vec![0, 1], Composition::from(vec![
            vec![T("d")]
        ]));

        let mut identified_tree_map = GornTree::new();
        identified_tree_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T(TermId::from((vec![], 1))), Var(0, 1), T(TermId::from((vec![], 3)))]
        ]));
        identified_tree_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T(TermId::from((vec![0], 1)))]
        ]));
        identified_tree_map.insert(vec![0, 1], Composition::from(vec![
            vec![T(TermId::from((vec![0, 1], 0)))]
        ]));

        let mut terminal_map = BTreeMap::new();
        terminal_map.insert(TermId::from((vec![], 1)), "a");
        terminal_map.insert(TermId::from((vec![], 3)), "b");
        terminal_map.insert(TermId::from((vec![0], 1)), "c");
        terminal_map.insert(TermId::from((vec![0, 1], 0)), "d");

        assert_eq!((identified_tree_map, terminal_map), identify_terminals(&tree_map));
    }

    #[test]
    fn test_to_negra_vector() {
        let tree_map = example_tree_map();
        let negra_vector = vec![
            (String::from("a"), String::from("a"), 1),
            (String::from("a"), String::from("a"), 2),
            (String::from("b"), String::from("b"), 3),
            (String::from("c"), String::from("c"), 1),
            (String::from("c"), String::from("c"), 2),
            (String::from("d"), String::from("d"), 3),
            (String::from("#1"), String::from("A"), 4),
            (String::from("#2"), String::from("A"), 1),
            (String::from("#3"), String::from("B"), 4),
            (String::from("#4"), String::from("S"), 0)
        ];

        assert_eq!(negra_vector, to_negra_vector(&tree_map));
    }

    #[test]
    fn test_meets_negra_criteria() {
        let mut tree_map: GornTree<PMCFGRule<String, char, usize>> = GornTree::new();
        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[T a, T b]] () #1"
        ).unwrap());

        assert_eq!(false, meets_negra_criteria(&tree_map));

        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[Var 0 0, T a]] (A) #1"
        ).unwrap());

        assert_eq!(false, meets_negra_criteria(&tree_map));

        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[Var 0 0], [Var 1 0]] (a, b) #1"
        ).unwrap());
        tree_map.insert(vec![], PMCFGRule::from_str(
            "A -> [[T a]] () #1"
        ).unwrap());

        assert_eq!(true, meets_negra_criteria(&tree_map));
    }

    #[test]
    #[should_panic(expected =
        "The given tree does not meet the negra criteria! All rules must either consist \
         only of nonterminals or of exactly one terminal symbol."
    )]
    fn test_to_negra_violated_criteria() {
        let mut tree_map: GornTree<PMCFGRule<String, char, usize>> = GornTree::new();
        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[T a, T b]] () #1"
        ).unwrap());

        to_negra(&tree_map, 0);
    }
}
<commit_msg>src/pmcfg/negra.rs: added identify_terminals() test<commit_after>use super::*;
use std::collections::{BTreeMap, VecDeque};

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct TermId {
    address: Vec<usize>,
    compos_var_pos: usize,
}

impl From<(Vec<usize>, usize)> for TermId {
    fn from((address, compos_var_pos): (Vec<usize>, usize)) -> TermId {
        TermId { address, compos_var_pos }
    }
}

pub fn identify_terminals<A>(tree_map: &GornTree<Composition<A>>)
        -> (GornTree<Composition<TermId>>, BTreeMap<TermId, A>)
    where A: Clone,
{
    let mut identified_tree_map = GornTree::new();
    let mut terminal_map = BTreeMap::new();

    for (address, composition) in tree_map {
        let vec_compos = &composition.composition;
        let mut identified_compos = Vec::new();
        let mut compos_var_pos = 0;

        for component in vec_compos {
            let mut identified_compon = Vec::new();

            for variable in component {
                match variable {
                    &VarT::Var(x, y) => {
                        identified_compon.push(VarT::Var(x, y));
                    },
                    &VarT::T(ref terminal) => {
                        let terminal_id = TermId { address: address.clone(), compos_var_pos };
                        identified_compon.push(VarT::T(terminal_id.clone()));
                        terminal_map.insert(terminal_id, terminal.clone());
                    },
                };

                compos_var_pos += 1;
            }

            identified_compos.push(identified_compon);
        }

        identified_tree_map.insert(address.clone(), Composition::from(identified_compos));
    }

    (identified_tree_map, terminal_map)
}

pub fn to_negra<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>, sentence_id: usize)
        -> String
    where H: Clone + ToString,
          T: Clone + ToString,
{
    if !meets_negra_criteria(&tree_map) {
        panic!("The given tree does not meet the negra criteria! All rules must either consist \
                only of nonterminals or of exactly one terminal symbol.");
    }

    let negra_vector = to_negra_vector(&tree_map);
    let mut output = format!("#BOS {}\n", sentence_id);

    for (word, tag, parent) in negra_vector {
        output.push_str(&format!("{}\t{}\t--\t--\t{}\n", word, tag, parent));
    }

    output.push_str(&format!("#EOS {}", sentence_id));
    output
}

pub fn meets_negra_criteria<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>)
        -> bool
{
    for (_address, rule) in tree_map {
        let &PMCFGRule { head: _, tail: _, ref composition, weight: _ } = rule;
        let mut contains_nonterminal = false;
        let mut contains_terminal = false;

        for component in &composition.composition {
            for variable in component {
                match variable {
                    &VarT::Var(_, _) => {
                        if contains_terminal {
                            return false;
                        }

                        contains_nonterminal = true;
                    },
                    &VarT::T(_) => {
                        if contains_nonterminal || contains_terminal {
                            return false;
                        }

                        contains_terminal = true;
                    },
                }
            }
        }
    }

    true
}

pub fn to_negra_vector<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>)
        -> Vec<(String, String, usize)>
    where H: Clone + ToString,
          T: Clone + ToString,
{
    let (term_map, nonterminal_map) = to_term(&tree_map);
    let (identified_tree_map, terminal_map) = identify_terminals(&term_map);
    let evaluated_compos = evaluate(&identified_tree_map);

    let mut negra_vector = Vec::new();
    let mut rule_queue = VecDeque::new();
    let mut rule_number_map = GornTree::new();
    let mut rule_counter = 1;

    for component in evaluated_compos.composition {
        for variable in component {
            match variable {
                VarT::Var(_, _) => {
                    panic!("Nonterminals must not appear in a fully evaluated configuration!");
                },
                VarT::T(terminal_id) => {
                    let terminal_symbol = terminal_map.get(&terminal_id).unwrap();
                    let address = terminal_id.address;
                    let rule_label = nonterminal_map.get(&address).unwrap();

                    let mut parent_address = address;
                    let parent_number = if let None = parent_address.pop() {
                        panic!("Terminals must have a nonterminal-only rule as their parent!");
                    } else {
                        get_rule_number(parent_address, &mut rule_queue, &mut rule_number_map,
                                        &mut rule_counter)
                    };
                    negra_vector.push((terminal_symbol.to_string(), rule_label.to_string(), parent_number));
                },
            }
        }
    }

    while let Some((address, rule_number)) = rule_queue.pop_front() {
        let mut parent_address = address.clone();

        let parent_number = if let None = parent_address.pop() {
            0
        } else {
            get_rule_number(parent_address, &mut rule_queue, &mut rule_number_map, &mut rule_counter)
        };

        let rule_label = nonterminal_map.get(&address).unwrap();
        negra_vector.push((format!("#{}", rule_number), rule_label.to_string(), parent_number));
    }

    negra_vector
}

fn get_rule_number(address: Vec<usize>, rule_queue: &mut VecDeque<(Vec<usize>, usize)>, rule_number_map: &mut GornTree<usize>, rule_counter: &mut usize)
        -> usize
{
    if let Some(rule_number) = rule_number_map.get(&address) {
        return *rule_number
    }

    let rule_number = *rule_counter;
    *rule_counter = *rule_counter + 1;

    rule_number_map.insert(address.clone(), rule_number.clone());
    rule_queue.push_back((address, rule_number));
    rule_number
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::tests::*;
    use self::VarT::{Var, T};
    use std::str::FromStr;

    #[test]
    fn test_identify_terminals() {
        let mut tree_map = GornTree::new();
        tree_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T("a"), Var(0, 1), T("b")]
        ]));
        tree_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T("c")]
        ]));
        tree_map.insert(vec![0, 1], Composition::from(vec![
            vec![T("d")]
        ]));

        let mut identified_tree_map = GornTree::new();
        identified_tree_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T(TermId::from((vec![], 1))), Var(0, 1), T(TermId::from((vec![], 3)))]
        ]));
        identified_tree_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T(TermId::from((vec![0], 1)))]
        ]));
        identified_tree_map.insert(vec![0, 1], Composition::from(vec![
            vec![T(TermId::from((vec![0, 1], 0)))]
        ]));

        let mut terminal_map = BTreeMap::new();
        terminal_map.insert(TermId::from((vec![], 1)), "a");
        terminal_map.insert(TermId::from((vec![], 3)), "b");
        terminal_map.insert(TermId::from((vec![0], 1)), "c");
        terminal_map.insert(TermId::from((vec![0, 1], 0)), "d");

        assert_eq!((identified_tree_map, terminal_map), identify_terminals(&tree_map));
    }

    #[test]
    fn test_identify_terminals_inverse() {
        let (tree_map, _) = to_term(&example_tree_map());
        let (identified_tree_map, terminal_map) = identify_terminals(&tree_map);
        let mut unidentified_tree_map = GornTree::new();

        for (address, composition) in identified_tree_map {
            let mut unidentified_compos = Vec::new();

            for component in composition.composition {
                let mut unidentified_compon: Vec<VarT<char>> = Vec::new();

                for variable in component {
                    match variable {
                        VarT::Var(x, y) => {
                            unidentified_compon.push(VarT::Var(x, y));
                        },
                        VarT::T(terminal_id) => {
                            unidentified_compon.push(
                                VarT::T(terminal_map.get(&terminal_id).unwrap().clone())
                            );
                        },
                    }
                }

                unidentified_compos.push(unidentified_compon);
            }

            unidentified_tree_map.insert(address, Composition::from(unidentified_compos));
        }

        assert_eq!(tree_map, unidentified_tree_map);
    }

    #[test]
    fn test_to_negra_vector() {
        let tree_map = example_tree_map();
        let negra_vector = vec![
            (String::from("a"), String::from("a"), 1),
            (String::from("a"), String::from("a"), 2),
            (String::from("b"), String::from("b"), 3),
            (String::from("c"), String::from("c"), 1),
            (String::from("c"), String::from("c"), 2),
            (String::from("d"), String::from("d"), 3),
            (String::from("#1"), String::from("A"), 4),
            (String::from("#2"), String::from("A"), 1),
            (String::from("#3"), String::from("B"), 4),
            (String::from("#4"), String::from("S"), 0)
        ];

        assert_eq!(negra_vector, to_negra_vector(&tree_map));
    }

    #[test]
    fn test_meets_negra_criteria() {
        let mut tree_map: GornTree<PMCFGRule<String, char, usize>> = GornTree::new();
        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[T a, T b]] () #1"
        ).unwrap());

        assert_eq!(false, meets_negra_criteria(&tree_map));

        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[Var 0 0, T a]] (A) #1"
        ).unwrap());

        assert_eq!(false, meets_negra_criteria(&tree_map));

        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[Var 0 0], [Var 1 0]] (a, b) #1"
        ).unwrap());
        tree_map.insert(vec![], PMCFGRule::from_str(
            "A -> [[T a]] () #1"
        ).unwrap());

        assert_eq!(true, meets_negra_criteria(&tree_map));
    }

    #[test]
    #[should_panic(expected =
        "The given tree does not meet the negra criteria! All rules must either consist \
         only of nonterminals or of exactly one terminal symbol."
    )]
    fn test_to_negra_violated_criteria() {
        let mut tree_map: GornTree<PMCFGRule<String, char, usize>> = GornTree::new();
        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[T a, T b]] () #1"
        ).unwrap());

        to_negra(&tree_map, 0);
    }
}
<|endoftext|>


<commit_before>use super::imp::{BitMaskWord, BITMASK_MASK, BITMASK_STRIDE};
#[cfg(feature = "nightly")]
use core::intrinsics;

/// A bit mask which contains the result of a `Match` operation on a `Group` and
/// allows iterating through them.
///
/// The bit mask is arranged so that low-order bits represent lower memory
/// addresses for group match results.
///
/// For implementation reasons, the bits in the set may be sparsely packed, so
/// that there is only one bit-per-byte used (the high bit, 7). If this is the
/// case, `BITMASK_STRIDE` will be 8 to indicate a divide-by-8 should be
/// performed on counts/indices to normalize this difference. `BITMASK_MASK` is
/// similarly a mask of all the actually-used bits.
#[derive(Copy, Clone)]
pub struct BitMask(pub BitMaskWord);

#[allow(clippy::use_self)]
impl BitMask {
    /// Returns a new `BitMask` with all bits inverted.
    #[inline]
    #[must_use]
    pub fn invert(self) -> Self {
        BitMask(self.0 ^ BITMASK_MASK)
    }

    /// Flip the bit in the mask for the entry at the given index.
    ///
    /// Returns the bit's previous state.
    #[inline]
    #[allow(clippy::cast_ptr_alignment)]
    #[cfg(feature = "raw")]
    pub unsafe fn flip(&mut self, index: usize) -> bool {
        let mask = 1 << (index * BITMASK_STRIDE);
        self.0 ^= mask;
        // The bit was set if the bit is now 0.
        self.0 & mask == 0
    }

    /// Returns a new `BitMask` with the lowest bit removed.
    #[inline]
    #[must_use]
    pub fn remove_lowest_bit(self) -> Self {
        BitMask(self.0 & (self.0 - 1))
    }
    /// Returns whether the `BitMask` has at least one set bit.
    #[inline]
    pub fn any_bit_set(self) -> bool {
        self.0 != 0
    }

    /// Returns the first set bit in the `BitMask`, if there is one.
    #[inline]
    pub fn lowest_set_bit(self) -> Option<usize> {
        if self.0 == 0 {
            None
        } else {
            Some(unsafe { self.lowest_set_bit_nonzero() })
        }
    }

    /// Returns the first set bit in the `BitMask`, if there is one. The
    /// bitmask must not be empty.
    #[inline]
    #[cfg(feature = "nightly")]
    pub unsafe fn lowest_set_bit_nonzero(self) -> usize {
        intrinsics::cttz_nonzero(self.0) as usize / BITMASK_STRIDE
    }
    #[inline]
    #[cfg(not(feature = "nightly"))]
    pub unsafe fn lowest_set_bit_nonzero(self) -> usize {
        self.trailing_zeros()
    }

    /// Returns the number of trailing zeroes in the `BitMask`.
    #[inline]
    pub fn trailing_zeros(self) -> usize {
        // ARM doesn't have a trailing_zeroes instruction, and instead uses
        // reverse_bits (RBIT) + leading_zeroes (CLZ). However older ARM
        // versions (pre-ARMv7) don't have RBIT and need to emulate it
        // instead. Since we only have 1 bit set in each byte on ARM, we can
        // use swap_bytes (REV) + leading_zeroes instead.
        if cfg!(target_arch = "arm") && BITMASK_STRIDE % 8 == 0 {
            self.0.swap_bytes().leading_zeros() as usize / BITMASK_STRIDE
        } else {
            self.0.trailing_zeros() as usize / BITMASK_STRIDE
        }
    }

    /// Returns the number of leading zeroes in the `BitMask`.
    #[inline]
    pub fn leading_zeros(self) -> usize {
        self.0.leading_zeros() as usize / BITMASK_STRIDE
    }
}

impl IntoIterator for BitMask {
    type Item = usize;
    type IntoIter = BitMaskIter;

    #[inline]
    fn into_iter(self) -> BitMaskIter {
        BitMaskIter(self)
    }
}

/// Iterator over the contents of a `BitMask`, returning the indicies of set
/// bits.
pub struct BitMaskIter(BitMask);

impl Iterator for BitMaskIter {
    type Item = usize;

    #[inline]
    fn next(&mut self) -> Option<usize> {
        let bit = self.0.lowest_set_bit()?;
        self.0 = self.0.remove_lowest_bit();
        Some(bit)
    }
}
<commit_msg>Make BitMask::flip also work w/o SSE2<commit_after>use super::imp::{BitMaskWord, BITMASK_MASK, BITMASK_STRIDE};
#[cfg(feature = "nightly")]
use core::intrinsics;

/// A bit mask which contains the result of a `Match` operation on a `Group` and
/// allows iterating through them.
///
/// The bit mask is arranged so that low-order bits represent lower memory
/// addresses for group match results.
///
/// For implementation reasons, the bits in the set may be sparsely packed, so
/// that there is only one bit-per-byte used (the high bit, 7). If this is the
/// case, `BITMASK_STRIDE` will be 8 to indicate a divide-by-8 should be
/// performed on counts/indices to normalize this difference. `BITMASK_MASK` is
/// similarly a mask of all the actually-used bits.
#[derive(Copy, Clone)]
pub struct BitMask(pub BitMaskWord);

#[allow(clippy::use_self)]
impl BitMask {
    /// Returns a new `BitMask` with all bits inverted.
    #[inline]
    #[must_use]
    pub fn invert(self) -> Self {
        BitMask(self.0 ^ BITMASK_MASK)
    }

    /// Flip the bit in the mask for the entry at the given index.
    ///
    /// Returns the bit's previous state.
    #[inline]
    #[allow(clippy::cast_ptr_alignment)]
    #[cfg(feature = "raw")]
    pub unsafe fn flip(&mut self, index: usize) -> bool {
        // NOTE: The + BITMASK_STRIDE - 1 is to set the high bit.
        let mask = 1 << (index * BITMASK_STRIDE + BITMASK_STRIDE - 1);
        self.0 ^= mask;
        // The bit was set if the bit is now 0.
        self.0 & mask == 0
    }

    /// Returns a new `BitMask` with the lowest bit removed.
    #[inline]
    #[must_use]
    pub fn remove_lowest_bit(self) -> Self {
        BitMask(self.0 & (self.0 - 1))
    }
    /// Returns whether the `BitMask` has at least one set bit.
    #[inline]
    pub fn any_bit_set(self) -> bool {
        self.0 != 0
    }

    /// Returns the first set bit in the `BitMask`, if there is one.
    #[inline]
    pub fn lowest_set_bit(self) -> Option<usize> {
        if self.0 == 0 {
            None
        } else {
            Some(unsafe { self.lowest_set_bit_nonzero() })
        }
    }

    /// Returns the first set bit in the `BitMask`, if there is one. The
    /// bitmask must not be empty.
    #[inline]
    #[cfg(feature = "nightly")]
    pub unsafe fn lowest_set_bit_nonzero(self) -> usize {
        intrinsics::cttz_nonzero(self.0) as usize / BITMASK_STRIDE
    }
    #[inline]
    #[cfg(not(feature = "nightly"))]
    pub unsafe fn lowest_set_bit_nonzero(self) -> usize {
        self.trailing_zeros()
    }

    /// Returns the number of trailing zeroes in the `BitMask`.
    #[inline]
    pub fn trailing_zeros(self) -> usize {
        // ARM doesn't have a trailing_zeroes instruction, and instead uses
        // reverse_bits (RBIT) + leading_zeroes (CLZ). However older ARM
        // versions (pre-ARMv7) don't have RBIT and need to emulate it
        // instead. Since we only have 1 bit set in each byte on ARM, we can
        // use swap_bytes (REV) + leading_zeroes instead.
        if cfg!(target_arch = "arm") && BITMASK_STRIDE % 8 == 0 {
            self.0.swap_bytes().leading_zeros() as usize / BITMASK_STRIDE
        } else {
            self.0.trailing_zeros() as usize / BITMASK_STRIDE
        }
    }

    /// Returns the number of leading zeroes in the `BitMask`.
    #[inline]
    pub fn leading_zeros(self) -> usize {
        self.0.leading_zeros() as usize / BITMASK_STRIDE
    }
}

impl IntoIterator for BitMask {
    type Item = usize;
    type IntoIter = BitMaskIter;

    #[inline]
    fn into_iter(self) -> BitMaskIter {
        BitMaskIter(self)
    }
}

/// Iterator over the contents of a `BitMask`, returning the indicies of set
/// bits.
pub struct BitMaskIter(BitMask);

impl Iterator for BitMaskIter {
    type Item = usize;

    #[inline]
    fn next(&mut self) -> Option<usize> {
        let bit = self.0.lowest_set_bit()?;
        self.0 = self.0.remove_lowest_bit();
        Some(bit)
    }
}
<|endoftext|>


<commit_before>use super::super::{Limiter, SteeringAcceleration, SteeringAccelerationCalculator, SteeringBehavior};
use nalgebra::{angle, distance, Point3, Vector3};
use alga::general::Real;
use alga::general::AbstractModule;
use std::f32::MAX;
use num_traits::identities::Zero;
use Steerable;

#[derive(Builder)]
pub struct Face<'a, T>
where
    T: 'a + Real,
{
    /// Common behavior attributes
    pub behavior: SteeringBehavior<'a, T>,
    pub target: &'a Steerable<T>,
    pub allign_tolerance: T,
    pub deceleration_radius: T,
    pub time_to_target: T,
}

fn wrap_angle_around_zero<T: Real>(inp: T) -> T {
    if inp >= T::zero() {
        let mut rotation = inp % T::two_pi();
        if rotation > T::pi() {
            rotation -= T::two_pi();
        }
        rotation
    } else {
        let mut rotation = -inp % T::two_pi();
        if rotation > T::pi() {
            rotation -= T::two_pi();
        }
        -rotation
    }
}

impl<'a, T: 'a + Real> Face<'a, T> {
    fn reach_orientation<'b>(
        &self,
        steering_acceleration: &'b mut SteeringAcceleration<T>,
        owner: &'b Steerable<T>,
        target_orientation: T,
    ) -> &'b mut SteeringAcceleration<T> {
        let rotation = wrap_angle_around_zero(target_orientation - owner.get_orientation());
        let abs_rotation = Real::abs(rotation);
        if abs_rotation <= self.allign_tolerance {
            steering_acceleration.set_zero();
        }

        let mut target_rotation = match self.behavior.limiter {
            Some(lim) => lim.get_max_angular_speed(),
            None => T::from_f32(MAX).unwrap(),
        };
        if abs_rotation < self.deceleration_radius {
            target_rotation *= abs_rotation / self.deceleration_radius;
        }

        target_rotation *= rotation / abs_rotation;

        steering_acceleration.angular =
            (target_rotation - owner.get_angular_velocity()) / self.time_to_target;
        let angular_acceleration = Real::abs(steering_acceleration.angular);
        if let Some(lim) = self.behavior.limiter {
            if angular_acceleration > lim.get_max_angular_speed() {
                steering_acceleration.angular *= lim.get_max_angular_speed() / angular_acceleration;
            }
        }
        steering_acceleration.linear = Vector3::zero();
        steering_acceleration
    }
}

impl<'a, T: 'a + Real> SteeringAccelerationCalculator<T> for Face<'a, T> {
    fn calculate_real_steering<'b>(
        &self,
        steering_acceleration: &'b mut SteeringAcceleration<T>,
        owner: &'b Steerable<T>,
    ) -> &'b mut SteeringAcceleration<T> {
        steering_acceleration.linear = *self.target.get_position() - *owner.get_position();
        let to_target = distance(
            &Point3::from_coordinates(steering_acceleration.linear),
            &Point3::origin(),
        );

        if let Some(lim) = self.behavior.limiter {
            if to_target.powi(2) < lim.get_zero_linear_speed_threshold() {
                steering_acceleration.set_zero();
            }
        }

        let target_orientation = angle(
            &steering_acceleration.linear,
            &Vector3::new(T::zero(), T::one(), T::zero()),
        );
        self.reach_orientation(steering_acceleration, owner, target_orientation)
    }

    fn is_enabled(self: &Self) -> bool {
        self.behavior.enabled
    }
}
<commit_msg>removed unnecessary use<commit_after>use super::super::{Limiter, SteeringAcceleration, SteeringAccelerationCalculator, SteeringBehavior};
use nalgebra::{angle, distance, Point3, Vector3};
use alga::general::Real;
use std::f32::MAX;
use num_traits::identities::Zero;
use Steerable;

#[derive(Builder)]
pub struct Face<'a, T>
where
    T: 'a + Real,
{
    /// Common behavior attributes
    pub behavior: SteeringBehavior<'a, T>,
    pub target: &'a Steerable<T>,
    pub allign_tolerance: T,
    pub deceleration_radius: T,
    pub time_to_target: T,
}

fn wrap_angle_around_zero<T: Real>(inp: T) -> T {
    if inp >= T::zero() {
        let mut rotation = inp % T::two_pi();
        if rotation > T::pi() {
            rotation -= T::two_pi();
        }
        rotation
    } else {
        let mut rotation = -inp % T::two_pi();
        if rotation > T::pi() {
            rotation -= T::two_pi();
        }
        -rotation
    }
}

impl<'a, T: 'a + Real> Face<'a, T> {
    fn reach_orientation<'b>(
        &self,
        steering_acceleration: &'b mut SteeringAcceleration<T>,
        owner: &'b Steerable<T>,
        target_orientation: T,
    ) -> &'b mut SteeringAcceleration<T> {
        let rotation = wrap_angle_around_zero(target_orientation - owner.get_orientation());
        let abs_rotation = Real::abs(rotation);
        if abs_rotation <= self.allign_tolerance {
            steering_acceleration.set_zero();
        }

        let mut target_rotation = match self.behavior.limiter {
            Some(lim) => lim.get_max_angular_speed(),
            None => T::from_f32(MAX).unwrap(),
        };
        if abs_rotation < self.deceleration_radius {
            target_rotation *= abs_rotation / self.deceleration_radius;
        }

        target_rotation *= rotation / abs_rotation;

        steering_acceleration.angular =
            (target_rotation - owner.get_angular_velocity()) / self.time_to_target;
        let angular_acceleration = Real::abs(steering_acceleration.angular);
        if let Some(lim) = self.behavior.limiter {
            if angular_acceleration > lim.get_max_angular_speed() {
                steering_acceleration.angular *= lim.get_max_angular_speed() / angular_acceleration;
            }
        }
        steering_acceleration.linear = Vector3::zero();
        steering_acceleration
    }
}

impl<'a, T: 'a + Real> SteeringAccelerationCalculator<T> for Face<'a, T> {
    fn calculate_real_steering<'b>(
        &self,
        steering_acceleration: &'b mut SteeringAcceleration<T>,
        owner: &'b Steerable<T>,
    ) -> &'b mut SteeringAcceleration<T> {
        steering_acceleration.linear = *self.target.get_position() - *owner.get_position();
        let to_target = distance(
            &Point3::from_coordinates(steering_acceleration.linear),
            &Point3::origin(),
        );

        if let Some(lim) = self.behavior.limiter {
            if to_target.powi(2) < lim.get_zero_linear_speed_threshold() {
                steering_acceleration.set_zero();
            }
        }

        let target_orientation = angle(
            &steering_acceleration.linear,
            &Vector3::new(T::zero(), T::one(), T::zero()),
        );
        self.reach_orientation(steering_acceleration, owner, target_orientation)
    }

    fn is_enabled(self: &Self) -> bool {
        self.behavior.enabled
    }
}
<|endoftext|>