<commit_before>use rustc::ty::{self, Ty, TypeAndMut};
use rustc::ty::layout::{self, TyLayout, Size};
use rustc::ty::adjustment::{PointerCast};
use syntax::ast::FloatTy;
use syntax::symbol::sym;

use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::{Float, FloatConvert};
use rustc::mir::interpret::{
    Scalar, InterpResult, Pointer, PointerArithmetic,
};
use rustc::mir::CastKind;

use super::{InterpCx, Machine, PlaceTy, OpTy, Immediate, FnVal};

impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
        match ty.sty {
            ty::RawPtr(ty::TypeAndMut { ty, .. }) |
            ty::Ref(_, ty, _) => !self.type_is_sized(ty),
            ty::Adt(def, _) if def.is_box() => !self.type_is_sized(ty.boxed_ty()),
            _ => false,
        }
    }

    pub fn cast(
        &mut self,
        src: OpTy<'tcx, M::PointerTag>,
        kind: CastKind,
        dest: PlaceTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx> {
        use rustc::mir::CastKind::*;
        match kind {
            Pointer(PointerCast::Unsize) => {
                self.unsize_into(src, dest)?;
            }

            Misc | Pointer(PointerCast::MutToConstPointer) => {
                let src = self.read_immediate(src)?;

                if self.type_is_fat_ptr(src.layout.ty) {
                    match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
                        // pointers to extern types
                        (Immediate::Scalar(_),_) |
                        // slices and trait objects to other slices/trait objects
                        (Immediate::ScalarPair(..), true) => {
                            // No change to immediate
                            self.write_immediate(*src, dest)?;
                        }
                        // slices and trait objects to thin pointers (dropping the metadata)
                        (Immediate::ScalarPair(data, _), false) => {
                            self.write_scalar(data, dest)?;
                        }
                    }
                } else {
                    match src.layout.variants {
                        layout::Variants::Single { index } => {
                            if let Some(discr) =
                                src.layout.ty.discriminant_for_variant(*self.tcx, index)
                            {
                                // Cast from a univariant enum
                                assert!(src.layout.is_zst());
                                return self.write_scalar(
                                    Scalar::from_uint(discr.val, dest.layout.size),
                                    dest);
                            }
                        }
                        layout::Variants::Multiple { .. } => {},
                    }

                    let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
                    self.write_scalar(dest_val, dest)?;
                }
            }

            Pointer(PointerCast::ReifyFnPointer) => {
                // The src operand does not matter, just its type
                match src.layout.ty.sty {
                    ty::FnDef(def_id, substs) => {
                        if self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
                            bug!("reifying a fn ptr that requires const arguments");
                        }
                        let instance = ty::Instance::resolve(
                            *self.tcx,
                            self.param_env,
                            def_id,
                            substs,
                        ).ok_or_else(|| err_inval!(TooGeneric))?;
                        let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
                        self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
                    }
                    _ => bug!("reify fn pointer on {:?}", src.layout.ty),
                }
            }

            Pointer(PointerCast::UnsafeFnPointer) => {
                let src = self.read_immediate(src)?;
                match dest.layout.ty.sty {
                    ty::FnPtr(_) => {
                        // No change to value
                        self.write_immediate(*src, dest)?;
                    }
                    _ => bug!("fn to unsafe fn cast on {:?}", dest.layout.ty),
                }
            }

            Pointer(PointerCast::ClosureFnPointer(_)) => {
                // The src operand does not matter, just its type
                match src.layout.ty.sty {
                    ty::Closure(def_id, substs) => {
                        let substs = self.subst_and_normalize_erasing_regions(substs)?;
                        let instance = ty::Instance::resolve_closure(
                            *self.tcx,
                            def_id,
                            substs,
                            ty::ClosureKind::FnOnce,
                        );
                        let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
                        let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into());
                        self.write_immediate(val, dest)?;
                    }
                    _ => bug!("closure fn pointer on {:?}", src.layout.ty),
                }
            }
        }
        Ok(())
    }

    fn cast_scalar(
        &self,
        val: Scalar<M::PointerTag>,
        src_layout: TyLayout<'tcx>,
        dest_layout: TyLayout<'tcx>,
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
        use rustc::ty::TyKind::*;
        trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty);

        match src_layout.ty.sty {
            // Floating point
            Float(FloatTy::F32) => self.cast_from_float(val.to_f32()?, dest_layout.ty),
            Float(FloatTy::F64) => self.cast_from_float(val.to_f64()?, dest_layout.ty),
            // Integer(-like), including fn ptr casts and casts from enums that
            // are represented as integers (this excludes univariant enums, which
            // are handled in `cast` directly).
            _ => {
                assert!(
                    src_layout.ty.is_bool()       || src_layout.ty.is_char()     ||
                    src_layout.ty.is_enum()       || src_layout.ty.is_integral() ||
                    src_layout.ty.is_unsafe_ptr() || src_layout.ty.is_fn_ptr()   ||
                    src_layout.ty.is_region_ptr(),
                    "Unexpected cast from type {:?}", src_layout.ty
                );
                match val.to_bits_or_ptr(src_layout.size, self) {
                    Err(ptr) => self.cast_from_ptr(ptr, src_layout, dest_layout),
                    Ok(data) => self.cast_from_int(data, src_layout, dest_layout),
                }
            }
        }
    }

    fn cast_from_int(
        &self,
        v: u128, // raw bits
        src_layout: TyLayout<'tcx>,
        dest_layout: TyLayout<'tcx>,
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
        // Let's make sure v is sign-extended *if* it has a signed type.
        let signed = src_layout.abi.is_signed();
        let v = if signed {
            self.sign_extend(v, src_layout)
        } else {
            v
        };
        trace!("cast_from_int: {}, {}, {}", v, src_layout.ty, dest_layout.ty);
        use rustc::ty::TyKind::*;
        match dest_layout.ty.sty {
            Int(_) | Uint(_) | RawPtr(_) => {
                let v = self.truncate(v, dest_layout);
                Ok(Scalar::from_uint(v, dest_layout.size))
            }

            Float(FloatTy::F32) if signed => Ok(Scalar::from_f32(
                Single::from_i128(v as i128).value
            )),
            Float(FloatTy::F64) if signed => Ok(Scalar::from_f64(
                Double::from_i128(v as i128).value
            )),
            Float(FloatTy::F32) => Ok(Scalar::from_f32(
                Single::from_u128(v).value
            )),
            Float(FloatTy::F64) => Ok(Scalar::from_f64(
                Double::from_u128(v).value
            )),

            Char => {
                // `u8` to `char` cast
                debug_assert_eq!(v as u8 as u128, v);
                Ok(Scalar::from_uint(v, Size::from_bytes(4)))
            },

            // Casts to bool are not permitted by rustc, no need to handle them here.
            _ => throw_unsup!(Unimplemented(format!("int to {:?} cast", dest_layout.ty))),
        }
    }

    fn cast_from_float<F>(
        &self,
        f: F,
        dest_ty: Ty<'tcx>
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>>
    where F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>
    {
        use rustc::ty::TyKind::*;
        match dest_ty.sty {
            // float -> uint
            Uint(t) => {
                let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
                let v = f.to_u128(width).value;
                // This should already fit the bit width
                Ok(Scalar::from_uint(v, Size::from_bits(width as u64)))
            },
            // float -> int
            Int(t) => {
                let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
                let v = f.to_i128(width).value;
                Ok(Scalar::from_int(v, Size::from_bits(width as u64)))
            },
            // float -> f32
            Float(FloatTy::F32) =>
                Ok(Scalar::from_f32(f.convert(&mut false).value)),
            // float -> f64
            Float(FloatTy::F64) =>
                Ok(Scalar::from_f64(f.convert(&mut false).value)),
            // That's it.
            _ => bug!("invalid float to {:?} cast", dest_ty),
        }
    }

    fn cast_from_ptr(
        &self,
        ptr: Pointer<M::PointerTag>,
        src_layout: TyLayout<'tcx>,
        dest_layout: TyLayout<'tcx>,
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
        use rustc::ty::TyKind::*;

        match dest_layout.ty.sty {
            // Casting to a reference or fn pointer is not permitted by rustc,
            // no need to support it here.
            RawPtr(_) => Ok(ptr.into()),
            Int(_) | Uint(_) => {
                let size = self.memory.pointer_size();

                match self.force_bits(Scalar::Ptr(ptr), size) {
                    Ok(bits) => self.cast_from_int(bits, src_layout, dest_layout),
                    Err(_) if dest_layout.size == size => Ok(ptr.into()),
                    Err(e) => Err(e),
                }
            }
            _ => bug!("invalid MIR: ptr to {:?} cast", dest_layout.ty)
        }
    }

    fn unsize_into_ptr(
        &mut self,
        src: OpTy<'tcx, M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
        // The pointee types
        sty: Ty<'tcx>,
        dty: Ty<'tcx>,
    ) -> InterpResult<'tcx> {
        // A<Struct> -> A<Trait> conversion
        let (src_pointee_ty, dest_pointee_ty) =
            self.tcx.struct_lockstep_tails_erasing_lifetimes(sty, dty, self.param_env);

        match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
            (&ty::Array(_, length), &ty::Slice(_)) => {
                let ptr = self.read_immediate(src)?.to_scalar_ptr()?;
                // u64 cast is from usize to u64, which is always good
                let val = Immediate::new_slice(
                    ptr,
                    length.unwrap_usize(self.tcx.tcx),
                    self,
                );
                self.write_immediate(val, dest)
            }
            (&ty::Dynamic(..), &ty::Dynamic(..)) => {
                // For now, upcasts are limited to changes in marker
                // traits, and hence never actually require an actual
                // change to the vtable.
                let val = self.read_immediate(src)?;
                self.write_immediate(*val, dest)
            }
            (_, &ty::Dynamic(ref data, _)) => {
                // Initial cast from sized to dyn trait
                let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
                let ptr = self.read_immediate(src)?.to_scalar_ptr()?;
                let val = Immediate::new_dyn_trait(ptr, vtable);
                self.write_immediate(val, dest)
            }

            _ => bug!("invalid unsizing {:?} -> {:?}", src.layout.ty, dest.layout.ty),
        }
    }

    fn unsize_into(
        &mut self,
        src: OpTy<'tcx, M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx> {
        trace!("Unsizing {:?} into {:?}", src, dest);
        match (&src.layout.ty.sty, &dest.layout.ty.sty) {
            (&ty::Ref(_, s, _), &ty::Ref(_, d, _)) |
            (&ty::Ref(_, s, _), &ty::RawPtr(TypeAndMut { ty: d, .. })) |
            (&ty::RawPtr(TypeAndMut { ty: s, .. }),
             &ty::RawPtr(TypeAndMut { ty: d, .. })) => {
                self.unsize_into_ptr(src, dest, s, d)
            }
            (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
                assert_eq!(def_a, def_b);
                if def_a.is_box() || def_b.is_box() {
                    if !def_a.is_box() || !def_b.is_box() {
                        bug!("invalid unsizing between {:?} -> {:?}", src.layout, dest.layout);
                    }
                    return self.unsize_into_ptr(
                        src,
                        dest,
                        src.layout.ty.boxed_ty(),
                        dest.layout.ty.boxed_ty(),
                    );
                }

                // unsizing of generic struct with pointer fields
                // Example: `Arc<T>` -> `Arc<Trait>`
                // here we need to increase the size of every &T thin ptr field to a fat ptr
                for i in 0..src.layout.fields.count() {
                    let dst_field = self.place_field(dest, i as u64)?;
                    if dst_field.layout.is_zst() {
                        continue;
                    }
                    let src_field = self.operand_field(src, i as u64)?;
                    if src_field.layout.ty == dst_field.layout.ty {
                        self.copy_op(src_field, dst_field)?;
                    } else {
                        self.unsize_into(src_field, dst_field)?;
                    }
                }
                Ok(())
            }
            _ => {
                bug!(
                    "unsize_into: invalid conversion: {:?} -> {:?}",
                    src.layout,
                    dest.layout
                )
            }
        }
    }
}
<commit_msg>cast: no need to catch errors any more, force_bits should succeed<commit_after>use rustc::ty::{self, Ty, TypeAndMut};
use rustc::ty::layout::{self, TyLayout, Size};
use rustc::ty::adjustment::{PointerCast};
use syntax::ast::FloatTy;
use syntax::symbol::sym;

use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::{Float, FloatConvert};
use rustc::mir::interpret::{
    Scalar, InterpResult, Pointer, PointerArithmetic,
};
use rustc::mir::CastKind;

use super::{InterpCx, Machine, PlaceTy, OpTy, Immediate, FnVal};

impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
        match ty.sty {
            ty::RawPtr(ty::TypeAndMut { ty, .. }) |
            ty::Ref(_, ty, _) => !self.type_is_sized(ty),
            ty::Adt(def, _) if def.is_box() => !self.type_is_sized(ty.boxed_ty()),
            _ => false,
        }
    }

    pub fn cast(
        &mut self,
        src: OpTy<'tcx, M::PointerTag>,
        kind: CastKind,
        dest: PlaceTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx> {
        use rustc::mir::CastKind::*;
        match kind {
            Pointer(PointerCast::Unsize) => {
                self.unsize_into(src, dest)?;
            }

            Misc | Pointer(PointerCast::MutToConstPointer) => {
                let src = self.read_immediate(src)?;

                if self.type_is_fat_ptr(src.layout.ty) {
                    match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
                        // pointers to extern types
                        (Immediate::Scalar(_),_) |
                        // slices and trait objects to other slices/trait objects
                        (Immediate::ScalarPair(..), true) => {
                            // No change to immediate
                            self.write_immediate(*src, dest)?;
                        }
                        // slices and trait objects to thin pointers (dropping the metadata)
                        (Immediate::ScalarPair(data, _), false) => {
                            self.write_scalar(data, dest)?;
                        }
                    }
                } else {
                    match src.layout.variants {
                        layout::Variants::Single { index } => {
                            if let Some(discr) =
                                src.layout.ty.discriminant_for_variant(*self.tcx, index)
                            {
                                // Cast from a univariant enum
                                assert!(src.layout.is_zst());
                                return self.write_scalar(
                                    Scalar::from_uint(discr.val, dest.layout.size),
                                    dest);
                            }
                        }
                        layout::Variants::Multiple { .. } => {},
                    }

                    let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
                    self.write_scalar(dest_val, dest)?;
                }
            }

            Pointer(PointerCast::ReifyFnPointer) => {
                // The src operand does not matter, just its type
                match src.layout.ty.sty {
                    ty::FnDef(def_id, substs) => {
                        if self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
                            bug!("reifying a fn ptr that requires const arguments");
                        }
                        let instance = ty::Instance::resolve(
                            *self.tcx,
                            self.param_env,
                            def_id,
                            substs,
                        ).ok_or_else(|| err_inval!(TooGeneric))?;
                        let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
                        self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
                    }
                    _ => bug!("reify fn pointer on {:?}", src.layout.ty),
                }
            }

            Pointer(PointerCast::UnsafeFnPointer) => {
                let src = self.read_immediate(src)?;
                match dest.layout.ty.sty {
                    ty::FnPtr(_) => {
                        // No change to value
                        self.write_immediate(*src, dest)?;
                    }
                    _ => bug!("fn to unsafe fn cast on {:?}", dest.layout.ty),
                }
            }

            Pointer(PointerCast::ClosureFnPointer(_)) => {
                // The src operand does not matter, just its type
                match src.layout.ty.sty {
                    ty::Closure(def_id, substs) => {
                        let substs = self.subst_and_normalize_erasing_regions(substs)?;
                        let instance = ty::Instance::resolve_closure(
                            *self.tcx,
                            def_id,
                            substs,
                            ty::ClosureKind::FnOnce,
                        );
                        let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
                        let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into());
                        self.write_immediate(val, dest)?;
                    }
                    _ => bug!("closure fn pointer on {:?}", src.layout.ty),
                }
            }
        }
        Ok(())
    }

    fn cast_scalar(
        &self,
        val: Scalar<M::PointerTag>,
        src_layout: TyLayout<'tcx>,
        dest_layout: TyLayout<'tcx>,
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
        use rustc::ty::TyKind::*;
        trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty);

        match src_layout.ty.sty {
            // Floating point
            Float(FloatTy::F32) => self.cast_from_float(val.to_f32()?, dest_layout.ty),
            Float(FloatTy::F64) => self.cast_from_float(val.to_f64()?, dest_layout.ty),
            // Integer(-like), including fn ptr casts and casts from enums that
            // are represented as integers (this excludes univariant enums, which
            // are handled in `cast` directly).
            _ => {
                assert!(
                    src_layout.ty.is_bool()       || src_layout.ty.is_char()     ||
                    src_layout.ty.is_enum()       || src_layout.ty.is_integral() ||
                    src_layout.ty.is_unsafe_ptr() || src_layout.ty.is_fn_ptr()   ||
                    src_layout.ty.is_region_ptr(),
                    "Unexpected cast from type {:?}", src_layout.ty
                );
                match val.to_bits_or_ptr(src_layout.size, self) {
                    Err(ptr) => self.cast_from_ptr(ptr, src_layout, dest_layout),
                    Ok(data) => self.cast_from_int(data, src_layout, dest_layout),
                }
            }
        }
    }

    fn cast_from_int(
        &self,
        v: u128, // raw bits
        src_layout: TyLayout<'tcx>,
        dest_layout: TyLayout<'tcx>,
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
        // Let's make sure v is sign-extended *if* it has a signed type.
        let signed = src_layout.abi.is_signed();
        let v = if signed {
            self.sign_extend(v, src_layout)
        } else {
            v
        };
        trace!("cast_from_int: {}, {}, {}", v, src_layout.ty, dest_layout.ty);
        use rustc::ty::TyKind::*;
        match dest_layout.ty.sty {
            Int(_) | Uint(_) | RawPtr(_) => {
                let v = self.truncate(v, dest_layout);
                Ok(Scalar::from_uint(v, dest_layout.size))
            }

            Float(FloatTy::F32) if signed => Ok(Scalar::from_f32(
                Single::from_i128(v as i128).value
            )),
            Float(FloatTy::F64) if signed => Ok(Scalar::from_f64(
                Double::from_i128(v as i128).value
            )),
            Float(FloatTy::F32) => Ok(Scalar::from_f32(
                Single::from_u128(v).value
            )),
            Float(FloatTy::F64) => Ok(Scalar::from_f64(
                Double::from_u128(v).value
            )),

            Char => {
                // `u8` to `char` cast
                debug_assert_eq!(v as u8 as u128, v);
                Ok(Scalar::from_uint(v, Size::from_bytes(4)))
            },

            // Casts to bool are not permitted by rustc, no need to handle them here.
            _ => throw_unsup!(Unimplemented(format!("int to {:?} cast", dest_layout.ty))),
        }
    }

    fn cast_from_float<F>(
        &self,
        f: F,
        dest_ty: Ty<'tcx>
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>>
    where F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>
    {
        use rustc::ty::TyKind::*;
        match dest_ty.sty {
            // float -> uint
            Uint(t) => {
                let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
                let v = f.to_u128(width).value;
                // This should already fit the bit width
                Ok(Scalar::from_uint(v, Size::from_bits(width as u64)))
            },
            // float -> int
            Int(t) => {
                let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
                let v = f.to_i128(width).value;
                Ok(Scalar::from_int(v, Size::from_bits(width as u64)))
            },
            // float -> f32
            Float(FloatTy::F32) =>
                Ok(Scalar::from_f32(f.convert(&mut false).value)),
            // float -> f64
            Float(FloatTy::F64) =>
                Ok(Scalar::from_f64(f.convert(&mut false).value)),
            // That's it.
            _ => bug!("invalid float to {:?} cast", dest_ty),
        }
    }

    fn cast_from_ptr(
        &self,
        ptr: Pointer<M::PointerTag>,
        src_layout: TyLayout<'tcx>,
        dest_layout: TyLayout<'tcx>,
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
        use rustc::ty::TyKind::*;

        match dest_layout.ty.sty {
            // Casting to a reference or fn pointer is not permitted by rustc,
            // no need to support it here.
            RawPtr(_) => Ok(ptr.into()),
            Int(_) | Uint(_) => {
                let size = self.memory.pointer_size();
                let bits = self.force_bits(Scalar::Ptr(ptr), size)?;
                self.cast_from_int(bits, src_layout, dest_layout)
            }
            _ => bug!("invalid MIR: ptr to {:?} cast", dest_layout.ty)
        }
    }

    fn unsize_into_ptr(
        &mut self,
        src: OpTy<'tcx, M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
        // The pointee types
        sty: Ty<'tcx>,
        dty: Ty<'tcx>,
    ) -> InterpResult<'tcx> {
        // A<Struct> -> A<Trait> conversion
        let (src_pointee_ty, dest_pointee_ty) =
            self.tcx.struct_lockstep_tails_erasing_lifetimes(sty, dty, self.param_env);

        match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
            (&ty::Array(_, length), &ty::Slice(_)) => {
                let ptr = self.read_immediate(src)?.to_scalar_ptr()?;
                // u64 cast is from usize to u64, which is always good
                let val = Immediate::new_slice(
                    ptr,
                    length.unwrap_usize(self.tcx.tcx),
                    self,
                );
                self.write_immediate(val, dest)
            }
            (&ty::Dynamic(..), &ty::Dynamic(..)) => {
                // For now, upcasts are limited to changes in marker
                // traits, and hence never actually require an actual
                // change to the vtable.
                let val = self.read_immediate(src)?;
                self.write_immediate(*val, dest)
            }
            (_, &ty::Dynamic(ref data, _)) => {
                // Initial cast from sized to dyn trait
                let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
                let ptr = self.read_immediate(src)?.to_scalar_ptr()?;
                let val = Immediate::new_dyn_trait(ptr, vtable);
                self.write_immediate(val, dest)
            }

            _ => bug!("invalid unsizing {:?} -> {:?}", src.layout.ty, dest.layout.ty),
        }
    }

    fn unsize_into(
        &mut self,
        src: OpTy<'tcx, M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx> {
        trace!("Unsizing {:?} into {:?}", src, dest);
        match (&src.layout.ty.sty, &dest.layout.ty.sty) {
            (&ty::Ref(_, s, _), &ty::Ref(_, d, _)) |
            (&ty::Ref(_, s, _), &ty::RawPtr(TypeAndMut { ty: d, .. })) |
            (&ty::RawPtr(TypeAndMut { ty: s, .. }),
             &ty::RawPtr(TypeAndMut { ty: d, .. })) => {
                self.unsize_into_ptr(src, dest, s, d)
            }
            (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
                assert_eq!(def_a, def_b);
                if def_a.is_box() || def_b.is_box() {
                    if !def_a.is_box() || !def_b.is_box() {
                        bug!("invalid unsizing between {:?} -> {:?}", src.layout, dest.layout);
                    }
                    return self.unsize_into_ptr(
                        src,
                        dest,
                        src.layout.ty.boxed_ty(),
                        dest.layout.ty.boxed_ty(),
                    );
                }

                // unsizing of generic struct with pointer fields
                // Example: `Arc<T>` -> `Arc<Trait>`
                // here we need to increase the size of every &T thin ptr field to a fat ptr
                for i in 0..src.layout.fields.count() {
                    let dst_field = self.place_field(dest, i as u64)?;
                    if dst_field.layout.is_zst() {
                        continue;
                    }
                    let src_field = self.operand_field(src, i as u64)?;
                    if src_field.layout.ty == dst_field.layout.ty {
                        self.copy_op(src_field, dst_field)?;
                    } else {
                        self.unsize_into(src_field, dst_field)?;
                    }
                }
                Ok(())
            }
            _ => {
                bug!(
                    "unsize_into: invalid conversion: {:?} -> {:?}",
                    src.layout,
                    dest.layout
                )
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use ast;
use ast::{token_tree, tt_delim, tt_tok, tt_seq, tt_nonterminal,Ident};
use codemap::{Span, DUMMY_SP};
use diagnostic::SpanHandler;
use ext::tt::macro_parser::{named_match, matched_seq, matched_nonterminal};
use parse::token::{EOF, INTERPOLATED, IDENT, Token, nt_ident};
use parse::token::{ident_to_str};
use parse::lexer::TokenAndSpan;

use std::cell::RefCell;
use std::hashmap::HashMap;
use std::option;

///an unzipping of `token_tree`s
struct TtFrame {
    forest: @~[ast::token_tree],
    idx: uint,
    dotdotdoted: bool,
    sep: Option<Token>,
    up: Option<@mut TtFrame>,
}

pub struct TtReader {
    sp_diag: @SpanHandler,
    // the unzipped tree:
    priv stack: RefCell<@mut TtFrame>,
    /* for MBE-style macro transcription */
    priv interpolations: RefCell<HashMap<Ident, @named_match>>,
    priv repeat_idx: RefCell<~[uint]>,
    priv repeat_len: RefCell<~[uint]>,
    /* cached: */
    cur_tok: RefCell<Token>,
    cur_span: RefCell<Span>,
}

/** This can do Macro-By-Example transcription. On the other hand, if
 *  `src` contains no `tt_seq`s and `tt_nonterminal`s, `interp` can (and
 *  should) be none. */
pub fn new_tt_reader(sp_diag: @SpanHandler,
                     interp: Option<HashMap<Ident,@named_match>>,
                     src: ~[ast::token_tree])
                     -> @TtReader {
    let r = @TtReader {
        sp_diag: sp_diag,
        stack: RefCell::new(@mut TtFrame {
            forest: @src,
            idx: 0u,
            dotdotdoted: false,
            sep: None,
            up: option::None
        }),
        interpolations: match interp { /* just a convienience */
            None => RefCell::new(HashMap::new()),
            Some(x) => RefCell::new(x),
        },
        repeat_idx: RefCell::new(~[]),
        repeat_len: RefCell::new(~[]),
        /* dummy values, never read: */
        cur_tok: RefCell::new(EOF),
        cur_span: RefCell::new(DUMMY_SP),
    };
    tt_next_token(r); /* get cur_tok and cur_span set up */
    return r;
}

fn dup_tt_frame(f: @mut TtFrame) -> @mut TtFrame {
    @mut TtFrame {
        forest: @(*f.forest).clone(),
        idx: f.idx,
        dotdotdoted: f.dotdotdoted,
        sep: f.sep.clone(),
        up: match f.up {
            Some(up_frame) => Some(dup_tt_frame(up_frame)),
            None => None
        }
    }
}

pub fn dup_tt_reader(r: @TtReader) -> @TtReader {
    @TtReader {
        sp_diag: r.sp_diag,
        stack: RefCell::new(dup_tt_frame(r.stack.get())),
        repeat_idx: r.repeat_idx.clone(),
        repeat_len: r.repeat_len.clone(),
        cur_tok: r.cur_tok.clone(),
        cur_span: r.cur_span.clone(),
        interpolations: r.interpolations.clone(),
    }
}

fn lookup_cur_matched_by_matched(r: &TtReader, start: @named_match)
                                 -> @named_match {
    fn red(ad: @named_match, idx: &uint) -> @named_match {
        match *ad {
          matched_nonterminal(_) => {
            // end of the line; duplicate henceforth
            ad
          }
          matched_seq(ref ads, _) => ads[*idx]
        }
    }
    let repeat_idx = r.repeat_idx.borrow();
    repeat_idx.get().iter().fold(start, red)
}

fn lookup_cur_matched(r: &TtReader, name: Ident) -> @named_match {
    let matched_opt = {
        let interpolations = r.interpolations.borrow();
        interpolations.get().find_copy(&name)
    };
    match matched_opt {
        Some(s) => lookup_cur_matched_by_matched(r, s),
        None => {
            r.sp_diag.span_fatal(r.cur_span.get(),
                                 format!("unknown macro variable `{}`",
                                         ident_to_str(&name)));
        }
    }
}

#[deriving(Clone)]
enum lis {
    lis_unconstrained,
    lis_constraint(uint, Ident),
    lis_contradiction(~str),
}

fn lockstep_iter_size(t: &token_tree, r: &TtReader) -> lis {
    fn lis_merge(lhs: lis, rhs: lis) -> lis {
        match lhs {
          lis_unconstrained => rhs.clone(),
          lis_contradiction(_) => lhs.clone(),
          lis_constraint(l_len, ref l_id) => match rhs {
            lis_unconstrained => lhs.clone(),
            lis_contradiction(_) => rhs.clone(),
            lis_constraint(r_len, _) if l_len == r_len => lhs.clone(),
            lis_constraint(r_len, ref r_id) => {
                let l_n = ident_to_str(l_id);
                let r_n = ident_to_str(r_id);
                lis_contradiction(format!("Inconsistent lockstep iteration: \
                                           '{}' has {} items, but '{}' has {}",
                                           l_n, l_len, r_n, r_len))
            }
          }
        }
    }
    match *t {
      tt_delim(ref tts) | tt_seq(_, ref tts, _, _) => {
        tts.iter().fold(lis_unconstrained, |lis, tt| {
            let lis2 = lockstep_iter_size(tt, r);
            lis_merge(lis, lis2)
        })
      }
      tt_tok(..) => lis_unconstrained,
      tt_nonterminal(_, name) => match *lookup_cur_matched(r, name) {
        matched_nonterminal(_) => lis_unconstrained,
        matched_seq(ref ads, _) => lis_constraint(ads.len(), name)
      }
    }
}

// return the next token from the TtReader.
// EFFECT: advances the reader's token field
pub fn tt_next_token(r: &TtReader) -> TokenAndSpan {
    // XXX(pcwalton): Bad copy?
    let ret_val = TokenAndSpan {
        tok: r.cur_tok.get(),
        sp: r.cur_span.get(),
    };
    loop {
        {
            let mut stack = r.stack.borrow_mut();
            if stack.get().idx < stack.get().forest.len() {
                break;
            }
        }

        /* done with this set; pop or repeat? */
        if !r.stack.get().dotdotdoted || {
                let repeat_idx = r.repeat_idx.borrow();
                let repeat_len = r.repeat_len.borrow();
                *repeat_idx.get().last() == *repeat_len.get().last() - 1
            } {

            match r.stack.get().up {
              None => {
                r.cur_tok.set(EOF);
                return ret_val;
              }
              Some(tt_f) => {
                if r.stack.get().dotdotdoted {
                    {
                        let mut repeat_idx = r.repeat_idx.borrow_mut();
                        let mut repeat_len = r.repeat_len.borrow_mut();
                        repeat_idx.get().pop();
                        repeat_len.get().pop();
                    }
                }

                r.stack.set(tt_f);
                r.stack.get().idx += 1u;
              }
            }

        } else { /* repeat */
            r.stack.get().idx = 0u;
            {
                let mut repeat_idx = r.repeat_idx.borrow_mut();
                repeat_idx.get()[repeat_idx.get().len() - 1u] += 1u;
            }
            match r.stack.get().sep.clone() {
              Some(tk) => {
                r.cur_tok.set(tk); /* repeat same span, I guess */
                return ret_val;
              }
              None => ()
            }
        }
    }
    loop { /* because it's easiest, this handles `tt_delim` not starting
    with a `tt_tok`, even though it won't happen */
        // XXX(pcwalton): Bad copy.
        match r.stack.get().forest[r.stack.get().idx].clone() {
          tt_delim(tts) => {
            r.stack.set(@mut TtFrame {
                forest: tts,
                idx: 0u,
                dotdotdoted: false,
                sep: None,
                up: option::Some(r.stack.get())
            });
            // if this could be 0-length, we'd need to potentially recur here
          }
          tt_tok(sp, tok) => {
            r.cur_span.set(sp);
            r.cur_tok.set(tok);
            r.stack.get().idx += 1u;
            return ret_val;
          }
          tt_seq(sp, tts, sep, zerok) => {
            // XXX(pcwalton): Bad copy.
            let t = tt_seq(sp, tts, sep.clone(), zerok);
            match lockstep_iter_size(&t, r) {
              lis_unconstrained => {
                r.sp_diag.span_fatal(
                    sp, /* blame macro writer */
                      "attempted to repeat an expression \
                       containing no syntax \
                       variables matched as repeating at this depth");
                  }
                  lis_contradiction(ref msg) => {
                      /* FIXME #2887 blame macro invoker instead*/
                      r.sp_diag.span_fatal(sp, (*msg));
                  }
                  lis_constraint(len, _) => {
                    if len == 0 {
                      if !zerok {
                        r.sp_diag.span_fatal(sp, /* FIXME #2887 blame invoker
                        */
                                             "this must repeat at least \
                                              once");
                          }

                    r.stack.get().idx += 1u;
                    return tt_next_token(r);
                } else {
                    {
                        let mut repeat_idx = r.repeat_idx.borrow_mut();
                        let mut repeat_len = r.repeat_len.borrow_mut();
                        repeat_len.get().push(len);
                        repeat_idx.get().push(0u);
                        r.stack.set(@mut TtFrame {
                            forest: tts,
                            idx: 0u,
                            dotdotdoted: true,
                            sep: sep,
                            up: Some(r.stack.get())
                        });
                    }
                }
              }
            }
          }
          // FIXME #2887: think about span stuff here
          tt_nonterminal(sp, ident) => {
            match *lookup_cur_matched(r, ident) {
              /* sidestep the interpolation tricks for ident because
              (a) idents can be in lots of places, so it'd be a pain
              (b) we actually can, since it's a token. */
              matched_nonterminal(nt_ident(~sn,b)) => {
                r.cur_span.set(sp);
                r.cur_tok.set(IDENT(sn,b));
                r.stack.get().idx += 1u;
                return ret_val;
              }
              matched_nonterminal(ref other_whole_nt) => {
                // XXX(pcwalton): Bad copy.
                r.cur_span.set(sp);
                r.cur_tok.set(INTERPOLATED((*other_whole_nt).clone()));
                r.stack.get().idx += 1u;
                return ret_val;
              }
              matched_seq(..) => {
                r.sp_diag.span_fatal(
                    r.cur_span.get(), /* blame the macro writer */
                    format!("variable '{}' is still repeating at this depth",
                         ident_to_str(&ident)));
              }
            }
          }
        }
    }

}
<commit_msg>libsyntax: De-`@mut` `TtFrame::idx`<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use ast;
use ast::{token_tree, tt_delim, tt_tok, tt_seq, tt_nonterminal,Ident};
use codemap::{Span, DUMMY_SP};
use diagnostic::SpanHandler;
use ext::tt::macro_parser::{named_match, matched_seq, matched_nonterminal};
use parse::token::{EOF, INTERPOLATED, IDENT, Token, nt_ident};
use parse::token::{ident_to_str};
use parse::lexer::TokenAndSpan;

use std::cell::{Cell, RefCell};
use std::hashmap::HashMap;
use std::option;

///an unzipping of `token_tree`s
struct TtFrame {
    forest: @~[ast::token_tree],
    idx: Cell<uint>,
    dotdotdoted: bool,
    sep: Option<Token>,
    up: Option<@mut TtFrame>,
}

pub struct TtReader {
    sp_diag: @SpanHandler,
    // the unzipped tree:
    priv stack: RefCell<@mut TtFrame>,
    /* for MBE-style macro transcription */
    priv interpolations: RefCell<HashMap<Ident, @named_match>>,
    priv repeat_idx: RefCell<~[uint]>,
    priv repeat_len: RefCell<~[uint]>,
    /* cached: */
    cur_tok: RefCell<Token>,
    cur_span: RefCell<Span>,
}

/** This can do Macro-By-Example transcription. On the other hand, if
 *  `src` contains no `tt_seq`s and `tt_nonterminal`s, `interp` can (and
 *  should) be none. */
pub fn new_tt_reader(sp_diag: @SpanHandler,
                     interp: Option<HashMap<Ident,@named_match>>,
                     src: ~[ast::token_tree])
                     -> @TtReader {
    let r = @TtReader {
        sp_diag: sp_diag,
        stack: RefCell::new(@mut TtFrame {
            forest: @src,
            idx: Cell::new(0u),
            dotdotdoted: false,
            sep: None,
            up: option::None
        }),
        interpolations: match interp { /* just a convienience */
            None => RefCell::new(HashMap::new()),
            Some(x) => RefCell::new(x),
        },
        repeat_idx: RefCell::new(~[]),
        repeat_len: RefCell::new(~[]),
        /* dummy values, never read: */
        cur_tok: RefCell::new(EOF),
        cur_span: RefCell::new(DUMMY_SP),
    };
    tt_next_token(r); /* get cur_tok and cur_span set up */
    return r;
}

fn dup_tt_frame(f: @mut TtFrame) -> @mut TtFrame {
    @mut TtFrame {
        forest: @(*f.forest).clone(),
        idx: f.idx.clone(),
        dotdotdoted: f.dotdotdoted,
        sep: f.sep.clone(),
        up: match f.up {
            Some(up_frame) => Some(dup_tt_frame(up_frame)),
            None => None
        }
    }
}

pub fn dup_tt_reader(r: @TtReader) -> @TtReader {
    @TtReader {
        sp_diag: r.sp_diag,
        stack: RefCell::new(dup_tt_frame(r.stack.get())),
        repeat_idx: r.repeat_idx.clone(),
        repeat_len: r.repeat_len.clone(),
        cur_tok: r.cur_tok.clone(),
        cur_span: r.cur_span.clone(),
        interpolations: r.interpolations.clone(),
    }
}

fn lookup_cur_matched_by_matched(r: &TtReader, start: @named_match)
                                 -> @named_match {
    fn red(ad: @named_match, idx: &uint) -> @named_match {
        match *ad {
          matched_nonterminal(_) => {
            // end of the line; duplicate henceforth
            ad
          }
          matched_seq(ref ads, _) => ads[*idx]
        }
    }
    let repeat_idx = r.repeat_idx.borrow();
    repeat_idx.get().iter().fold(start, red)
}

fn lookup_cur_matched(r: &TtReader, name: Ident) -> @named_match {
    let matched_opt = {
        let interpolations = r.interpolations.borrow();
        interpolations.get().find_copy(&name)
    };
    match matched_opt {
        Some(s) => lookup_cur_matched_by_matched(r, s),
        None => {
            r.sp_diag.span_fatal(r.cur_span.get(),
                                 format!("unknown macro variable `{}`",
                                         ident_to_str(&name)));
        }
    }
}

#[deriving(Clone)]
enum lis {
    lis_unconstrained,
    lis_constraint(uint, Ident),
    lis_contradiction(~str),
}

fn lockstep_iter_size(t: &token_tree, r: &TtReader) -> lis {
    fn lis_merge(lhs: lis, rhs: lis) -> lis {
        match lhs {
          lis_unconstrained => rhs.clone(),
          lis_contradiction(_) => lhs.clone(),
          lis_constraint(l_len, ref l_id) => match rhs {
            lis_unconstrained => lhs.clone(),
            lis_contradiction(_) => rhs.clone(),
            lis_constraint(r_len, _) if l_len == r_len => lhs.clone(),
            lis_constraint(r_len, ref r_id) => {
                let l_n = ident_to_str(l_id);
                let r_n = ident_to_str(r_id);
                lis_contradiction(format!("Inconsistent lockstep iteration: \
                                           '{}' has {} items, but '{}' has {}",
                                           l_n, l_len, r_n, r_len))
            }
          }
        }
    }
    match *t {
      tt_delim(ref tts) | tt_seq(_, ref tts, _, _) => {
        tts.iter().fold(lis_unconstrained, |lis, tt| {
            let lis2 = lockstep_iter_size(tt, r);
            lis_merge(lis, lis2)
        })
      }
      tt_tok(..) => lis_unconstrained,
      tt_nonterminal(_, name) => match *lookup_cur_matched(r, name) {
        matched_nonterminal(_) => lis_unconstrained,
        matched_seq(ref ads, _) => lis_constraint(ads.len(), name)
      }
    }
}

// return the next token from the TtReader.
// EFFECT: advances the reader's token field
pub fn tt_next_token(r: &TtReader) -> TokenAndSpan {
    // XXX(pcwalton): Bad copy?
    let ret_val = TokenAndSpan {
        tok: r.cur_tok.get(),
        sp: r.cur_span.get(),
    };
    loop {
        {
            let mut stack = r.stack.borrow_mut();
            if stack.get().idx.get() < stack.get().forest.len() {
                break;
            }
        }

        /* done with this set; pop or repeat? */
        if !r.stack.get().dotdotdoted || {
                let repeat_idx = r.repeat_idx.borrow();
                let repeat_len = r.repeat_len.borrow();
                *repeat_idx.get().last() == *repeat_len.get().last() - 1
            } {

            match r.stack.get().up {
              None => {
                r.cur_tok.set(EOF);
                return ret_val;
              }
              Some(tt_f) => {
                if r.stack.get().dotdotdoted {
                    {
                        let mut repeat_idx = r.repeat_idx.borrow_mut();
                        let mut repeat_len = r.repeat_len.borrow_mut();
                        repeat_idx.get().pop();
                        repeat_len.get().pop();
                    }
                }

                r.stack.set(tt_f);
                r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
              }
            }

        } else { /* repeat */
            r.stack.get().idx.set(0u);
            {
                let mut repeat_idx = r.repeat_idx.borrow_mut();
                repeat_idx.get()[repeat_idx.get().len() - 1u] += 1u;
            }
            match r.stack.get().sep.clone() {
              Some(tk) => {
                r.cur_tok.set(tk); /* repeat same span, I guess */
                return ret_val;
              }
              None => ()
            }
        }
    }
    loop { /* because it's easiest, this handles `tt_delim` not starting
    with a `tt_tok`, even though it won't happen */
        // XXX(pcwalton): Bad copy.
        match r.stack.get().forest[r.stack.get().idx.get()].clone() {
          tt_delim(tts) => {
            r.stack.set(@mut TtFrame {
                forest: tts,
                idx: Cell::new(0u),
                dotdotdoted: false,
                sep: None,
                up: option::Some(r.stack.get())
            });
            // if this could be 0-length, we'd need to potentially recur here
          }
          tt_tok(sp, tok) => {
            r.cur_span.set(sp);
            r.cur_tok.set(tok);
            r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
            return ret_val;
          }
          tt_seq(sp, tts, sep, zerok) => {
            // XXX(pcwalton): Bad copy.
            let t = tt_seq(sp, tts, sep.clone(), zerok);
            match lockstep_iter_size(&t, r) {
              lis_unconstrained => {
                r.sp_diag.span_fatal(
                    sp, /* blame macro writer */
                      "attempted to repeat an expression \
                       containing no syntax \
                       variables matched as repeating at this depth");
                  }
                  lis_contradiction(ref msg) => {
                      /* FIXME #2887 blame macro invoker instead*/
                      r.sp_diag.span_fatal(sp, (*msg));
                  }
                  lis_constraint(len, _) => {
                    if len == 0 {
                      if !zerok {
                        r.sp_diag.span_fatal(sp, /* FIXME #2887 blame invoker
                        */
                                             "this must repeat at least \
                                              once");
                          }

                    r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
                    return tt_next_token(r);
                } else {
                    {
                        let mut repeat_idx = r.repeat_idx.borrow_mut();
                        let mut repeat_len = r.repeat_len.borrow_mut();
                        repeat_len.get().push(len);
                        repeat_idx.get().push(0u);
                        r.stack.set(@mut TtFrame {
                            forest: tts,
                            idx: Cell::new(0u),
                            dotdotdoted: true,
                            sep: sep,
                            up: Some(r.stack.get())
                        });
                    }
                }
              }
            }
          }
          // FIXME #2887: think about span stuff here
          tt_nonterminal(sp, ident) => {
            match *lookup_cur_matched(r, ident) {
              /* sidestep the interpolation tricks for ident because
              (a) idents can be in lots of places, so it'd be a pain
              (b) we actually can, since it's a token. */
              matched_nonterminal(nt_ident(~sn,b)) => {
                r.cur_span.set(sp);
                r.cur_tok.set(IDENT(sn,b));
                r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
                return ret_val;
              }
              matched_nonterminal(ref other_whole_nt) => {
                // XXX(pcwalton): Bad copy.
                r.cur_span.set(sp);
                r.cur_tok.set(INTERPOLATED((*other_whole_nt).clone()));
                r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
                return ret_val;
              }
              matched_seq(..) => {
                r.sp_diag.span_fatal(
                    r.cur_span.get(), /* blame the macro writer */
                    format!("variable '{}' is still repeating at this depth",
                         ident_to_str(&ident)));
              }
            }
          }
        }
    }

}
<|endoftext|>


<commit_before>use chrono::DateTime;
use chrono::prelude::{Local, Utc};
use errors::*;
use nom::{alphanumeric, ErrorKind, IResult};
use output::Output;
use std::str;

#[derive(Debug, PartialEq, Eq)]
struct Program {
    steps: Vec<Step>,
}

#[derive(Debug, PartialEq, Eq)]
struct Step {
    op: MacroOp,
    args: Vec<String>
}

#[derive(Debug, PartialEq, Eq)]
enum MacroOp {
    Add,
    Divide,
    Multiply,
    Name(String),
    Roll,
    Say,
    Subtract,
    Whisper,
}

#[derive(Debug, PartialEq, Eq)]
enum RollOp {
    Advantage,
    Disadvantage,
    NdD,
}

#[derive(Debug, PartialEq, Eq)]
enum SayOp {
    Message,
    To,
}

named!(command <&[u8], MacroOp>, alt!(
    map!(tag!("!roll"),     |_| MacroOp::Roll)      |
    map!(tag!("!r"),        |_| MacroOp::Roll)      |
    map!(tag!("!say"),      |_| MacroOp::Say)       |
    map!(tag!("!s"),        |_| MacroOp::Say)       |
    map!(tag!("!whisper"),  |_| MacroOp::Whisper)   |
    map!(tag!("!w"),        |_| MacroOp::Whisper)
));

named!(name <&[u8], MacroOp>, do_parse!(
    tag!("#") >>
    name: map_res!(is_not!(" \t\r\n"), |r: &[u8]| String::from_utf8(r.to_vec())) >>
    (MacroOp::Name(name))
));

named!(primitive <&[u8], MacroOp>, alt!(
    map!(tag!("*"), |_| MacroOp::Multiply)  |
    map!(tag!("+"), |_| MacroOp::Add)       |
    map!(tag!("-"), |_| MacroOp::Subtract)  |
    map!(tag!("/"), |_| MacroOp::Divide)
));

#[test]
fn test_name() {
    let (_, result) = name(b"#macro_name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro_name")));

    let (_, result) = name(b"#macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro-name")));

    let (_, result) = name(b"#123macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("123macro-name")));

    let (_, result) = name(b"#Z123macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("Z123macro-name")));

    let bad_result = name(b"macro_name");
    match bad_result {
        IResult::Error(e) => assert_eq!(e, ErrorKind::Tag),
        _ => assert_eq!(1, 0),
    }
}
<commit_msg>WIP can parse a simple macro<commit_after>use chrono::DateTime;
use chrono::prelude::{Local, Utc};
use errors::*;
use nom::{alphanumeric, ErrorKind, IResult};
use output::Output;
use std::str;

#[derive(Debug, PartialEq, Eq)]
struct Program {
    name: MacroOp,
    steps: Vec<Step>,
}

#[derive(Debug, PartialEq, Eq)]
struct Step {
    op: MacroOp,
    args: Vec<String>
}

#[derive(Debug, PartialEq, Eq)]
enum MacroOp {
    Add,
    Divide,
    Multiply,
    Name(String),
    Roll,
    Say,
    Subtract,
    Whisper,
}

#[derive(Debug, PartialEq, Eq)]
enum RollOp {
    Advantage,
    Comment,
    Disadvantage,
    NdD,
}

#[derive(Debug, PartialEq, Eq)]
enum SayOp {
    Message,
    To,
}

/// Matches a command
named!(command <&[u8], MacroOp>, alt!(
    map!(ws!(tag!("!roll")),     |_| MacroOp::Roll)      |
    map!(ws!(tag!("!r")),        |_| MacroOp::Roll)      |
    map!(ws!(tag!("!say")),      |_| MacroOp::Say)       |
    map!(ws!(tag!("!s")),        |_| MacroOp::Say)       |
    map!(ws!(tag!("!whisper")),  |_| MacroOp::Whisper)   |
    map!(ws!(tag!("!w")),        |_| MacroOp::Whisper)
));

/// Matches a macro name
named!(name <&[u8], MacroOp>, ws!(do_parse!(
    tag!("#") >>
    name: map_res!(is_not!(" \t\r\n"), |r: &[u8]| String::from_utf8(r.to_vec())) >>
    (MacroOp::Name(name))
)));

/// Matches any type of operation
named!(op <&[u8], MacroOp>, alt!(
    name |
    command |
    ws!(primitive)
));

/// Matches primitive operations
named!(primitive <&[u8], MacroOp>, alt!(
    map!(tag!("*"), |_| MacroOp::Multiply)  |
    map!(tag!("+"), |_| MacroOp::Add)       |
    map!(tag!("-"), |_| MacroOp::Subtract)  |
    map!(tag!("/"), |_| MacroOp::Divide)
));

/// Parse the complete macro
named!(parse <&[u8], Program>, do_parse!(
    prog_name: name >>
    op_type: op >>
    args: ws!(many0!(string)) >>
    (Program {
        name: prog_name,
        steps: vec![Step {
            op: op_type,
            args,
        }],
    })
));

/// Match alphanumeric values to strings
named!(string <&[u8], String>,
    do_parse!(
        word: ws!(alphanumeric) >>
        (String::from_utf8(word.to_vec()).unwrap())
    )
);

// @todo Match delimited values between " and ' to strings

#[test]
fn test_parser() {
    let program = Program {
        name: MacroOp::Name(String::from("macro-name")),
        steps: vec![Step {
            op: MacroOp::Roll,
            args: vec![ "1d20".to_string() ],
        }],
    };
    let (_, result) = parse(b"#macro-name !roll 1d20").unwrap();
    assert_eq!(result, program);

    // let program = Program {
        // name: MacroOp::Name(String::from("macro-name-2")),
        // steps: vec![Step {
            // op: MacroOp::Say,
            // args: vec![ "\"Hello, world!\"".to_string() ],
        // }],
    // };
    // let (_, result) = parse(b"#macro-name-2 !say \"Hello, world!\"").unwrap();
    // assert_eq!(result, program);
}

#[test]
fn test_name_parser() {
    let (_, result) = name(b"#macro_name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro_name")));

    let (_, result) = name(b"#macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro-name")));

    let (_, result) = name(b"#123macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("123macro-name")));

    let (_, result) = name(b"#Z123macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("Z123macro-name")));

    let (_, result) = name(b"#macro-name cannot have spaces").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro-name")));

    let bad_result = name(b"macro_name");
    match bad_result {
        IResult::Error(e) => assert_eq!(e, ErrorKind::Tag),
        _ => assert_eq!(1, 0),
    }
}

#[test]
fn test_command_parser_roll() {
    let (_, result) = command(b"!roll 1d20").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!r 1d20").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll advantage").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll adv").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll disadvantage").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll dis").unwrap();
    assert_eq!(result, MacroOp::Roll);
}

#[test]
fn test_op_parser() {
    let (_, result) = op(b"    #test-macro   ").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("test-macro")));
    let (_, result) = command(b"    !roll 1d20 ").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"   !say \"Hello!\" ").unwrap();
    assert_eq!(result, MacroOp::Say);
    let (_, result) = command(b"   !whisper").unwrap();
    assert_eq!(result, MacroOp::Whisper);
}
<|endoftext|>


<commit_before>use std::io::net::ip::{Port, IpAddr};

use router::{Router, HttpRouter};
use middleware::{MiddlewareStack, Middleware, ErrorHandler, MiddlewareResult};
use nickel_error::{ NickelError, ErrorWithStatusCode };
use server::Server;

use http::method::Method;
use http::status::NotFound;
use request::Request;
use response::Response;

//pre defined middleware
use default_error_handler::DefaultErrorHandler;

/// Nickel is the application object. It's the surface that
/// holds all public APIs.
pub struct Nickel{
    middleware_stack: MiddlewareStack,
}

impl HttpRouter for Nickel {
    fn add_route<H: Middleware>(&mut self, method: Method, uri: &str, handler: H) {
        let mut router = Router::new();
        // FIXME: Inference failure in nightly 22/10/2014
        router.add_route::<H>(method, uri, handler);
        self.utilize(router);
    }
}

impl Nickel {
    /// Creates an instance of Nickel with default error handling.
    pub fn new() -> Nickel {
        let mut middleware_stack = MiddlewareStack::new();

        // Hook up the default error handler by default. Users are
        // free to cancel it out from their custom error handler if
        // they don't like the default behaviour.
        middleware_stack.add_error_handler(DefaultErrorHandler);

        Nickel { middleware_stack: middleware_stack }
    }

    /// Registers a middleware handler which will be invoked among other middleware
    /// handlers before each request. Middleware can be stacked and is invoked in the
    /// same order it was registered.
    ///
    /// A middleware handler is nearly identical to a regular route handler with the only
    /// difference that it expects a result of either Action or NickelError.
    /// That is to indicate whether other middleware handlers (if any) further
    /// down the stack should continue or if the middleware invocation should
    /// be stopped after the current handler.
    ///
    /// # Example
    ///
    /// ```{rust}
    /// use nickel::{Nickel, Request, Response, Continue, MiddlewareResult};
    /// fn logger(req: &Request, res: &mut Response) -> MiddlewareResult {
    ///     println!("logging request: {}", req.origin.request_uri);
    ///     Ok(Continue)
    /// }
    ///
    /// let mut server = Nickel::new();
    /// server.utilize(logger);
    /// ```
    pub fn utilize<T: Middleware>(&mut self, handler: T){
        self.middleware_stack.add_middleware(handler);
    }

    /// Registers an error handler which will be invoked among other error handler
    /// as soon as any regular handler returned an error
    ///
    /// A error handler is nearly identical to a regular middleware handler with the only
    /// difference that it takes an additional error parameter or type `NickelError.
    ///
    /// # Example
    ///
    /// ```{rust}
    /// # extern crate http;
    /// # extern crate nickel;
    /// # fn main() {
    /// use nickel::{Nickel, Request, Response, Continue, Halt, MiddlewareResult};
    /// use nickel::{NickelError, ErrorWithStatusCode};
    /// use http::status::NotFound;
    /// use nickel::mimes::MediaType::Html;
    ///
    /// fn error_handler(err: &NickelError, req: &Request, response: &mut Response)
    ///                  -> MiddlewareResult {
    ///    match err.kind {
    ///        ErrorWithStatusCode(NotFound) => {
    ///            response.content_type(Html);
    ///            response.origin.status = NotFound;
    ///            response.send("<h1>Call the police!<h1>");
    ///            Ok(Halt)
    ///        },
    ///        _ => Ok(Continue)
    ///    }
    /// }
    ///
    /// let mut server = Nickel::new();
    /// server.handle_error(error_handler)
    /// # }
    /// ```
    pub fn handle_error<T: ErrorHandler>(&mut self, handler: T){
        self.middleware_stack.add_error_handler(handler);
    }

    /// Create a new middleware to serve as a router.
    ///
    ///
    /// # Example
    /// ```{rust}
    /// use nickel::{Nickel, Request, Response, HttpRouter};
    ///
    /// let mut server = Nickel::new();
    /// let mut router = Nickel::router();
    ///
    /// fn foo_handler(request: &Request, response: &mut Response) {
    ///     response.send("Hi from /foo");
    /// };
    ///
    /// router.get("/foo", foo_handler);
    /// server.utilize(router);
    /// ```
    pub fn router() -> Router {
        Router::new()
    }

    /// Bind and listen for connections on the given host and port
    ///
    /// # Example
    /// ```{rust,ignore}
    /// let mut server = Nickel::new();
    /// server.listen(Ipv4Addr(127, 0, 0, 1), 6767);
    /// ```
    pub fn listen(mut self, ip: IpAddr, port: Port) {
        fn not_found_handler(_: &Request, _: &mut Response) -> MiddlewareResult {
            Err(NickelError::new("File Not Found", ErrorWithStatusCode(NotFound)))
        }

        self.middleware_stack.add_middleware(not_found_handler);

        match port {
            80u16 =>  println!("Listening on http://{}", ip),
            _ =>  println!("Listening on http://{}:{}", ip, port),
        }
        println!("Ctrl-C to shutdown server");

        Server::new(self.middleware_stack, ip, port).serve();
    }
}
<commit_msg>fix(doctest): compile doctest for listen but dont run it<commit_after>use std::io::net::ip::{Port, IpAddr};

use router::{Router, HttpRouter};
use middleware::{MiddlewareStack, Middleware, ErrorHandler, MiddlewareResult};
use nickel_error::{ NickelError, ErrorWithStatusCode };
use server::Server;

use http::method::Method;
use http::status::NotFound;
use request::Request;
use response::Response;

//pre defined middleware
use default_error_handler::DefaultErrorHandler;

/// Nickel is the application object. It's the surface that
/// holds all public APIs.
pub struct Nickel{
    middleware_stack: MiddlewareStack,
}

impl HttpRouter for Nickel {
    fn add_route<H: Middleware>(&mut self, method: Method, uri: &str, handler: H) {
        let mut router = Router::new();
        // FIXME: Inference failure in nightly 22/10/2014
        router.add_route::<H>(method, uri, handler);
        self.utilize(router);
    }
}

impl Nickel {
    /// Creates an instance of Nickel with default error handling.
    pub fn new() -> Nickel {
        let mut middleware_stack = MiddlewareStack::new();

        // Hook up the default error handler by default. Users are
        // free to cancel it out from their custom error handler if
        // they don't like the default behaviour.
        middleware_stack.add_error_handler(DefaultErrorHandler);

        Nickel { middleware_stack: middleware_stack }
    }

    /// Registers a middleware handler which will be invoked among other middleware
    /// handlers before each request. Middleware can be stacked and is invoked in the
    /// same order it was registered.
    ///
    /// A middleware handler is nearly identical to a regular route handler with the only
    /// difference that it expects a result of either Action or NickelError.
    /// That is to indicate whether other middleware handlers (if any) further
    /// down the stack should continue or if the middleware invocation should
    /// be stopped after the current handler.
    ///
    /// # Example
    ///
    /// ```{rust}
    /// use nickel::{Nickel, Request, Response, Continue, MiddlewareResult};
    /// fn logger(req: &Request, res: &mut Response) -> MiddlewareResult {
    ///     println!("logging request: {}", req.origin.request_uri);
    ///     Ok(Continue)
    /// }
    ///
    /// let mut server = Nickel::new();
    /// server.utilize(logger);
    /// ```
    pub fn utilize<T: Middleware>(&mut self, handler: T){
        self.middleware_stack.add_middleware(handler);
    }

    /// Registers an error handler which will be invoked among other error handler
    /// as soon as any regular handler returned an error
    ///
    /// A error handler is nearly identical to a regular middleware handler with the only
    /// difference that it takes an additional error parameter or type `NickelError.
    ///
    /// # Example
    ///
    /// ```{rust}
    /// # extern crate http;
    /// # extern crate nickel;
    /// # fn main() {
    /// use nickel::{Nickel, Request, Response, Continue, Halt, MiddlewareResult};
    /// use nickel::{NickelError, ErrorWithStatusCode};
    /// use http::status::NotFound;
    /// use nickel::mimes::MediaType::Html;
    ///
    /// fn error_handler(err: &NickelError, req: &Request, response: &mut Response)
    ///                  -> MiddlewareResult {
    ///    match err.kind {
    ///        ErrorWithStatusCode(NotFound) => {
    ///            response.content_type(Html);
    ///            response.origin.status = NotFound;
    ///            response.send("<h1>Call the police!<h1>");
    ///            Ok(Halt)
    ///        },
    ///        _ => Ok(Continue)
    ///    }
    /// }
    ///
    /// let mut server = Nickel::new();
    /// server.handle_error(error_handler)
    /// # }
    /// ```
    pub fn handle_error<T: ErrorHandler>(&mut self, handler: T){
        self.middleware_stack.add_error_handler(handler);
    }

    /// Create a new middleware to serve as a router.
    ///
    ///
    /// # Example
    /// ```{rust}
    /// use nickel::{Nickel, Request, Response, HttpRouter};
    ///
    /// let mut server = Nickel::new();
    /// let mut router = Nickel::router();
    ///
    /// fn foo_handler(request: &Request, response: &mut Response) {
    ///     response.send("Hi from /foo");
    /// };
    ///
    /// router.get("/foo", foo_handler);
    /// server.utilize(router);
    /// ```
    pub fn router() -> Router {
        Router::new()
    }

    /// Bind and listen for connections on the given host and port
    ///
    /// # Example
    /// ```{rust,no_run}
    /// use nickel::Nickel;
    /// use std::io::net::ip::IpAddr::Ipv4Addr;
    ///
    /// let mut server = Nickel::new();
    /// server.listen(Ipv4Addr(127, 0, 0, 1), 6767);
    /// ```
    pub fn listen(mut self, ip: IpAddr, port: Port) {
        fn not_found_handler(_: &Request, _: &mut Response) -> MiddlewareResult {
            Err(NickelError::new("File Not Found", ErrorWithStatusCode(NotFound)))
        }

        self.middleware_stack.add_middleware(not_found_handler);

        match port {
            80u16 =>  println!("Listening on http://{}", ip),
            _ =>  println!("Listening on http://{}:{}", ip, port),
        }
        println!("Ctrl-C to shutdown server");

        Server::new(self.middleware_stack, ip, port).serve();
    }
}
<|endoftext|>


<commit_before>use syntax::ast;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::ptr::P;

use block::{Block, Describe, It, Bench};

pub fn parse(parser: &mut Parser) -> Describe {
    parse_describe("sup", parser)
}

pub fn parse_describe(name: &str, parser: &mut Parser) -> Describe {
    let brace = token::CloseDelim(token::Brace);

    let mut before = None;
    let mut after = None;
    let mut blocks = vec![];

    loop {
        if parser.token == brace || parser.token == token::Eof {
            break
        }

        let span = parser.span;
        let ident = parser.parse_ident();

        match ident.as_str() {
            "describe" | "context" => {
                let (name, _) = parser.parse_str();
                parser.expect(&token::OpenDelim(token::Brace));
                let block = Block::Describe(parse_describe(&name, parser));
                parser.expect(&token::CloseDelim(token::Brace));
                blocks.push(block);
            },

            "it" | "test" => {
                let (name, _) = parser.parse_str();
                let block = parse_block(parser);

                blocks.push(Block::It(It {
                    name: name.to_string(),
                    block: block
                }))
            },

            "bench" => {
                let (name, _) = parser.parse_str();
                parser.expect(&token::BinOp(token::Or));
                let ident = parser.parse_ident();
                parser.expect(&token::BinOp(token::Or));
                let block = parse_block(parser);

                blocks.push(Block::Bench(Bench {
                    name: name.to_string(),
                    ident: ident,
                    block: block
                }))
            },

            "before" => {
                before = Some(parse_block(parser));
            },

            "after" => {
                after = Some(parse_block(parser));
            },

            otherwise => {
                let message = format!("Expected \
`describe`, `context`, `before`, `after`, or `it`, found `{}`", otherwise);
                parser.span_fatal(span, &*message)
            }
        }
    }

    Describe {
        name: name.to_string(),
        before: before,
        after: after,
        blocks: blocks
    }
}

fn parse_block(parser: &mut Parser) -> P<ast::Block> {
    let span = parser.span;
    let block = parser.parse_block();
    if block.expr.is_some() {
        parser.span_fatal(
            span,
            "last expression in this block must be terminated by `;`")
    }
    block
}
<commit_msg>Fix for `rustc 1.1.0-nightly (5fb0259ed 2015-04-26) (built 2015-04-26)`.<commit_after>use syntax::ast;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::ptr::P;

use block::{Block, Describe, It, Bench};

pub fn parse(parser: &mut Parser) -> Describe {
    parse_describe("sup", parser)
}

pub fn parse_describe(name: &str, parser: &mut Parser) -> Describe {
    let brace = token::CloseDelim(token::Brace);

    let mut before = None;
    let mut after = None;
    let mut blocks = vec![];

    loop {
        if parser.token == brace || parser.token == token::Eof {
            break
        }

        let span = parser.span;
        let ident = parser.parse_ident().unwrap();

        match ident.as_str() {
            "describe" | "context" => {
                let (name, _) = parser.parse_str().unwrap();
                parser.expect(&token::OpenDelim(token::Brace)).unwrap();
                let block = Block::Describe(parse_describe(&name, parser));
                parser.expect(&token::CloseDelim(token::Brace)).unwrap();
                blocks.push(block);
            },

            "it" | "test" => {
                let (name, _) = parser.parse_str().unwrap();
                let block = parse_block(parser);

                blocks.push(Block::It(It {
                    name: name.to_string(),
                    block: block
                }))
            },

            "bench" => {
                let (name, _) = parser.parse_str().unwrap();
                parser.expect(&token::BinOp(token::Or)).unwrap();
                let ident = parser.parse_ident().unwrap();
                parser.expect(&token::BinOp(token::Or)).unwrap();
                let block = parse_block(parser);

                blocks.push(Block::Bench(Bench {
                    name: name.to_string(),
                    ident: ident,
                    block: block
                }))
            },

            "before" => {
                before = Some(parse_block(parser));
            },

            "after" => {
                after = Some(parse_block(parser));
            },

            otherwise => {
                let message = format!("Expected \
`describe`, `context`, `before`, `after`, or `it`, found `{}`", otherwise);
                panic!("{}", parser.span_fatal(span, &*message))
            }
        }
    }

    Describe {
        name: name.to_string(),
        before: before,
        after: after,
        blocks: blocks
    }
}

fn parse_block(parser: &mut Parser) -> P<ast::Block> {
    let span = parser.span;
    let block = parser.parse_block().unwrap();
    if block.expr.is_some() {
        panic!("{}", parser.span_fatal(
            span,
            "last expression in this block must be terminated by `;`"))
    }
    block
}
<|endoftext|>


<commit_before>use ast::*;

#[derive(Clone, Show)]
pub struct Parser {
    code: String,
    cur: usize,
    col: usize,
    line: usize
}

#[allow(dead_code)]
pub struct ParserError {
    line: usize,
    col: usize,
    desc: String
}

#[allow(dead_code)]
impl ParserError {
    pub fn new(line: usize, col: usize, desc: String) -> ParserError {
        ParserError {
            line: line,
            col: col,
            desc: desc
        }
    }
}

#[allow(dead_code)]
pub type ParseResult<T> = Result<T, ParserError>;

impl Parser {
    pub fn new() -> Parser {
        Parser{
            code: "".to_string(),
            line: 0, cur: 0, col: 0
        }
    }

    pub fn load(&mut self, code: String) {
        self.code = code;
        self.line = 1;
        self.cur = 0;
        self.col = 0;
    }

    //============= private methods =================
    pub fn read_exp(&mut self) -> Option<ExprAst> {
        if self.eof() { return None }
        self.skip_space();
        let mut cur = self.readc();
        if cur == '#' {
            let next = self.readc();
            match next {
                't' => return Some(ExprAst::Bool(BoolNode::new(true))),
                'f' => return Some(ExprAst::Bool(BoolNode::new(false))),
                '\\' => return self.read_char(),
                _ => panic!("error")
            }
        } else if cur.is_numeric() ||
            (cur == '-' && (self.peekc().is_numeric())) {
                let mut sign = 1is;
                let mut num = 0is;
                if cur == '-' {
                    sign = -1;
                } else {
                    self.unread();
                }
                loop {
                    cur = self.readc();
                    if !cur.is_numeric() {
                        break;
                    }
                    num = (num * 10is) + (cur as isize - '0' as isize);
                }
                num *= sign;
                if self.is_delimiter(cur) {
                    self.unread();
                }
                return Some(ExprAst::Int(IntNode::new(num)));
            } else if cur == '\"' {
                let mut buf = String::new();
                loop {
                    cur = self.readc();
                    if cur == '\"' {
                        break;
                    }
                    buf.push(cur);
                }
                return Some(ExprAst::Str(StrNode::new(buf.as_slice())));
            } else if cur == '(' {
                return self.read_pair();
            } else if self.is_initial(cur) {
                let mut buf = String::new();
                buf.push(cur);
                loop {
                    cur = self.readc();
                    if !(self.is_initial(cur) || cur.is_numeric()) {
                        break;
                    }
                    buf.push(cur);
                }
                if self.is_delimiter(cur) {
                    self.unread();
                }
                return Some(ExprAst::Symbol(SymbolNode::new(buf.as_slice())));
            } else if cur == '\'' {
                let quote_sym = ExprAst::Symbol(SymbolNode::new("quote"));
                let quote_exp = ExprAst::Pair(PairNode::new(self.read_exp().unwrap(),
                                                            ExprAst::Nil));
                return Some(ExprAst::Pair(PairNode::new(quote_sym, quote_exp)));
            }
        None
    }

    fn read_pair(&mut self) -> Option<ExprAst> {
        self.skip_space();
        let mut cur = self.readc();
        // rust-mode bug here
        if cur != '(' && cur == ')' {
            return Some(ExprAst::Nil);
        }
        self.unread();
        let car_obj = self.read_exp();
        self.skip_space();
        cur = self.readc();
        if cur != '.' {
            self.unread();
            let cdr_obj = self.read_pair();
            return Some(ExprAst::Pair(PairNode::new(car_obj.unwrap(),
                                                    cdr_obj.unwrap())));
        } else {
            return Some(ExprAst::Nil);
        }
    }

    fn is_delimiter(&self, ch: char) -> bool {
        ch.is_whitespace() ||
            ch == '\"' || ch == '(' || ch == ')' ||  ch == ';'
    }

    fn is_initial(&self, ch: char) -> bool {
        ch.is_alphabetic() ||
            ch == '*' || ch == '/' || ch == '+' || ch == '-' ||
            ch == '>' || ch == '<' || ch == '=' || ch == '?' ||
            ch == '!'
    }

    fn skip_space(&mut self) {
        while self.peekc().is_whitespace() {
            self.readc();
        }
    }

    fn eof(&self) -> bool {
        self.cur >= self.code.len()
    }

    fn peekc(&self) -> char {
        if self.cur >= self.code.len() {
            return 0 as char
        }
        self.code.char_at(self.cur)
    }

    fn prevc(&self) -> char {
        if self.cur <= 0 {
            panic!("invalid position");
        }
        self.code.char_at(self.cur - 1)
    }

    fn readc(&mut self) -> char {
        if self.cur < self.code.len() {
            let res = self.peekc();
            if res == '\n' {
                self.line += 1;
            }
            self.cur += 1;
            res
        } else {
            0 as char
        }
    }

    fn unread(&mut self) {
        if self.cur == 0 {
            panic!("error current position");
        }
        if self.prevc() == '\n' {
            assert!(self.line > 1);
            self.line -= 1;
        }
        self.cur -= 1;
    }

    fn read_char(&mut self) -> Option<ExprAst> {
        Some(ExprAst::Char(CharNode::new('a')))
    }
}

#[test]
fn test_parser() {
    macro_rules! test_case {
        ($test_str:expr, $expect_type:ident, $expect_val:expr) => { {
            let mut parser = Parser::new();
            parser.load($test_str.to_string());
            let res = parser.read_exp().unwrap();
            if res.$expect_type() != $expect_val {
                assert!(false);
            }
        }}
    }

    macro_rules! test_res {
        ($test_str:expr) => { {
            let mut parser = Parser::new();
            parser.load($test_str.to_string());
            parser.read_exp().unwrap()
        }}
    }

    test_case!("11", as_int, 11);
    test_case!("-11", as_int, -11);
    test_case!(r#""hello""#, as_str, "hello");

    let res = test_res!("()");
    assert!(res.is_empty());

    let res = test_res!("(1 2)");
    assert!(res.is_pair());
    assert!(res.car().as_int() == 1);
    assert!(res.cdr().car().as_int() == 2);
    assert!(res.cdr().cdr().is_empty());

    let res = test_res!("(+ 1 2)");
    assert!(res.is_pair());
    assert!(res.car().is_symbol());
    assert!(res.cdr().car().as_int() == 1);
    assert!(res.cdr().cdr().car().as_int() == 2);
}
<commit_msg>clean up<commit_after>use ast::*;

#[derive(Clone, Show)]
pub struct Parser {
    code: String,
    cur: usize,
    col: usize,
    line: usize
}

impl Parser {
    pub fn new() -> Parser {
        Parser{
            code: "".to_string(),
            line: 0, cur: 0, col: 0
        }
    }

    pub fn load(&mut self, code: String) {
        self.code = code;
        self.line = 1;
        self.cur = 0;
        self.col = 0;
    }

    //============= private methods =================
    pub fn read_exp(&mut self) -> Option<ExprAst> {
        if self.eof() { return None }
        self.skip_space();
        let mut cur = self.readc();
        if cur == '#' {
            let next = self.readc();
            match next {
                't' => return Some(ExprAst::Bool(BoolNode::new(true))),
                'f' => return Some(ExprAst::Bool(BoolNode::new(false))),
                '\\' => return self.read_char(),
                _ => panic!("error")
            }
        } else if cur.is_numeric() ||
            (cur == '-' && (self.peekc().is_numeric())) {
                let mut sign = 1is;
                let mut num = 0is;
                if cur == '-' {
                    sign = -1;
                } else {
                    self.unread();
                }
                loop {
                    cur = self.readc();
                    if !cur.is_numeric() {
                        break;
                    }
                    num = (num * 10is) + (cur as isize - '0' as isize);
                }
                num *= sign;
                if self.is_delimiter(cur) {
                    self.unread();
                }
                return Some(ExprAst::Int(IntNode::new(num)));
            } else if cur == '\"' {
                let mut buf = String::new();
                loop {
                    cur = self.readc();
                    if cur == '\"' {
                        break;
                    }
                    buf.push(cur);
                }
                return Some(ExprAst::Str(StrNode::new(buf.as_slice())));
            } else if cur == '(' {
                return self.read_pair();
            } else if self.is_initial(cur) {
                let mut buf = String::new();
                buf.push(cur);
                loop {
                    cur = self.readc();
                    if !(self.is_initial(cur) || cur.is_numeric()) {
                        break;
                    }
                    buf.push(cur);
                }
                if self.is_delimiter(cur) {
                    self.unread();
                }
                return Some(ExprAst::Symbol(SymbolNode::new(buf.as_slice())));
            } else if cur == '\'' {
                let quote_sym = ExprAst::Symbol(SymbolNode::new("quote"));
                let quote_exp = ExprAst::Pair(PairNode::new(self.read_exp().unwrap(),
                                                            ExprAst::Nil));
                return Some(ExprAst::Pair(PairNode::new(quote_sym, quote_exp)));
            }
        None
    }

    fn read_pair(&mut self) -> Option<ExprAst> {
        self.skip_space();
        let mut cur = self.readc();
        // rust-mode bug here
        if cur != '(' && cur == ')' {
            return Some(ExprAst::Nil);
        }
        self.unread();
        let car_obj = self.read_exp();
        self.skip_space();
        cur = self.readc();
        if cur != '.' {
            self.unread();
            let cdr_obj = self.read_pair();
            return Some(ExprAst::Pair(PairNode::new(car_obj.unwrap(),
                                                    cdr_obj.unwrap())));
        } else {
            return Some(ExprAst::Nil);
        }
    }

    fn is_delimiter(&self, ch: char) -> bool {
        ch.is_whitespace() ||
            ch == '\"' || ch == '(' || ch == ')' ||  ch == ';'
    }

    fn is_initial(&self, ch: char) -> bool {
        ch.is_alphabetic() ||
            ch == '*' || ch == '/' || ch == '+' || ch == '-' ||
            ch == '>' || ch == '<' || ch == '=' || ch == '?' ||
            ch == '!'
    }

    fn skip_space(&mut self) {
        while self.peekc().is_whitespace() {
            self.readc();
        }
    }

    fn eof(&self) -> bool {
        self.cur >= self.code.len()
    }

    fn peekc(&self) -> char {
        if self.cur >= self.code.len() {
            return 0 as char
        }
        self.code.char_at(self.cur)
    }

    fn prevc(&self) -> char {
        if self.cur <= 0 {
            panic!("invalid position");
        }
        self.code.char_at(self.cur - 1)
    }

    fn readc(&mut self) -> char {
        if self.cur < self.code.len() {
            let res = self.peekc();
            if res == '\n' {
                self.line += 1;
            }
            self.cur += 1;
            res
        } else {
            0 as char
        }
    }

    fn unread(&mut self) {
        if self.cur == 0 {
            panic!("error current position");
        }
        if self.prevc() == '\n' {
            assert!(self.line > 1);
            self.line -= 1;
        }
        self.cur -= 1;
    }

    fn read_char(&mut self) -> Option<ExprAst> {
        Some(ExprAst::Char(CharNode::new('a')))
    }
}

#[test]
fn test_parser() {
    macro_rules! test_case {
        ($test_str:expr, $expect_type:ident, $expect_val:expr) => { {
            let mut parser = Parser::new();
            parser.load($test_str.to_string());
            let res = parser.read_exp().unwrap();
            if res.$expect_type() != $expect_val {
                assert!(false);
            }
        }}
    }

    macro_rules! test_res {
        ($test_str:expr) => { {
            let mut parser = Parser::new();
            parser.load($test_str.to_string());
            parser.read_exp().unwrap()
        }}
    }

    test_case!("11", as_int, 11);
    test_case!("-11", as_int, -11);
    test_case!(r#""hello""#, as_str, "hello");

    let res = test_res!("()");
    assert!(res.is_empty());

    let res = test_res!("(1 2)");
    assert!(res.is_pair());
    assert!(res.car().as_int() == 1);
    assert!(res.cdr().car().as_int() == 2);
    assert!(res.cdr().cdr().is_empty());

    let res = test_res!("(+ 1 2)");
    assert!(res.is_pair());
    assert!(res.car().is_symbol());
    assert!(res.cdr().car().as_int() == 1);
    assert!(res.cdr().cdr().car().as_int() == 2);
}
<|endoftext|>