<commit_before>use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
    TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
    let def_id = body.source.def_id();
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate.kind().skip_binder() {
                ty::PredicateKind::RegionOutlives(_)
                | ty::PredicateKind::TypeOutlives(_)
                | ty::PredicateKind::WellFormed(_)
                | ty::PredicateKind::Projection(_)
                | ty::PredicateKind::ConstEvaluatable(..)
                | ty::PredicateKind::ConstEquate(..)
                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
                ty::PredicateKind::Trait(pred, _) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.self_ty().kind() {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                },
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
    for arg in ty.walk() {
        let ty = match arg.unpack() {
            GenericArgKind::Type(ty) => ty,

            // No constraints on lifetimes or constants, except potentially
            // constants' types, but `walk` will get to them as well.
            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
        };

        match ty.kind() {
            ty::Ref(_, _, hir::Mutability::Mut) => {
                return Err((span, "mutable references in const fn are unstable".into()));
            },
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                return Err((span, "function pointers in const fn are unstable".into()));
            },
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        },
                    }
                }
            },
            _ => {},
        }
    }
    Ok(())
}

fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
    match rvalue {
        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
            check_place(tcx, *place, span, body)
        },
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc_middle::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
                    Err((span, "casting pointers to ints is unstable in const fn".into()))
                },
                _ => check_operand(tcx, operand, span, body),
            }
        },
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, body)
        },
        Rvalue::Cast(
            CastKind::Pointer(
                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
            ),
            _,
            _,
        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
                deref_ty.ty
            } else {
                // We cannot allow this for now.
                return Err((span, "unsizing casts are only allowed for references right now".into()));
            };
            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
                check_operand(tcx, op, span, body)?;
                // Casting/coercing things to slices is fine.
                Ok(())
            } else {
                // We just can't allow trait objects until we have figured out trait method calls.
                Err((span, "unsizing casts are not allowed in const fn".into()))
            }
        },
        // binops are fine on integers
        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
            check_operand(tcx, lhs, span, body)?;
            check_operand(tcx, rhs, span, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        },
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, body)
            } else {
                Err((span, "only int and `bool` operations are stable in const fn".into()))
            }
        },
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, body)?;
            }
            Ok(())
        },
    }
}

fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box (place, rval)) => {
            check_place(tcx, *place, span, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        }

        StatementKind::FakeRead(_, place) |
        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),

        StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),

        StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping{
          dst, src, size,
        }) => {
          check_operand(tcx, dst, span, body)?;
          check_operand(tcx, src, span, body)?;
          check_operand(tcx, size, span, body)
        },
        // These are all NOPs
        StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Coverage(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let [ref proj_base @ .., elem] = *cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        return Err((span, "accessing union fields is unstable".into()));
                    }
                }
            },
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Downcast(..)
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {},
        }
    }

    Ok(())
}

fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        TerminatorKind::FalseEdge { .. }
        | TerminatorKind::FalseUnwind { .. }
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume
        | TerminatorKind::Unreachable => Ok(()),

        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
        TerminatorKind::DropAndReplace { place, value, .. } => {
            check_place(tcx, *place, span, body)?;
            check_operand(tcx, value, span, body)
        },

        TerminatorKind::SwitchInt {
            discr,
            switch_ty: _,
            targets: _,
        } => check_operand(tcx, discr, span, body),

        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        },

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
            fn_span: _,
        } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                // HACK: This is to "unstabilize" the `transmute` intrinsic
                // within const fns. `transmute` is allowed in all other const contexts.
                // This won't really scale to more intrinsics or functions. Let's allow const
                // transmutes in const fn before we add more hacks to this.
                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
                    return Err((
                        span,
                        "can only call `transmute` from const items, not `const fn`".into(),
                    ));
                }

                check_operand(tcx, func, span, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        },

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, cond, span, body),

        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
    }
}
<commit_msg>Update cranelift<commit_after>use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
    TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
    let def_id = body.source.def_id();
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate.kind().skip_binder() {
                ty::PredicateKind::RegionOutlives(_)
                | ty::PredicateKind::TypeOutlives(_)
                | ty::PredicateKind::WellFormed(_)
                | ty::PredicateKind::Projection(_)
                | ty::PredicateKind::ConstEvaluatable(..)
                | ty::PredicateKind::ConstEquate(..)
                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
                ty::PredicateKind::Trait(pred, _) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.self_ty().kind() {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                },
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
    for arg in ty.walk() {
        let ty = match arg.unpack() {
            GenericArgKind::Type(ty) => ty,

            // No constraints on lifetimes or constants, except potentially
            // constants' types, but `walk` will get to them as well.
            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
        };

        match ty.kind() {
            ty::Ref(_, _, hir::Mutability::Mut) => {
                return Err((span, "mutable references in const fn are unstable".into()));
            },
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                return Err((span, "function pointers in const fn are unstable".into()));
            },
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        },
                    }
                }
            },
            _ => {},
        }
    }
    Ok(())
}

fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
    match rvalue {
        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
            check_place(tcx, *place, span, body)
        },
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc_middle::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
                    Err((span, "casting pointers to ints is unstable in const fn".into()))
                },
                _ => check_operand(tcx, operand, span, body),
            }
        },
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, body)
        },
        Rvalue::Cast(
            CastKind::Pointer(
                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
            ),
            _,
            _,
        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
                deref_ty.ty
            } else {
                // We cannot allow this for now.
                return Err((span, "unsizing casts are only allowed for references right now".into()));
            };
            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
                check_operand(tcx, op, span, body)?;
                // Casting/coercing things to slices is fine.
                Ok(())
            } else {
                // We just can't allow trait objects until we have figured out trait method calls.
                Err((span, "unsizing casts are not allowed in const fn".into()))
            }
        },
        // binops are fine on integers
        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
            check_operand(tcx, lhs, span, body)?;
            check_operand(tcx, rhs, span, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        },
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, body)
            } else {
                Err((span, "only int and `bool` operations are stable in const fn".into()))
            }
        },
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, body)?;
            }
            Ok(())
        },
    }
}

fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box (place, rval)) => {
            check_place(tcx, *place, span, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        }

        StatementKind::FakeRead(_, place) |
        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),

        StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),

        StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping{
          dst, src, count,
        }) => {
          check_operand(tcx, dst, span, body)?;
          check_operand(tcx, src, span, body)?;
          check_operand(tcx, count, span, body)
        },
        // These are all NOPs
        StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Coverage(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let [ref proj_base @ .., elem] = *cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        return Err((span, "accessing union fields is unstable".into()));
                    }
                }
            },
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Downcast(..)
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {},
        }
    }

    Ok(())
}

fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        TerminatorKind::FalseEdge { .. }
        | TerminatorKind::FalseUnwind { .. }
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume
        | TerminatorKind::Unreachable => Ok(()),

        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
        TerminatorKind::DropAndReplace { place, value, .. } => {
            check_place(tcx, *place, span, body)?;
            check_operand(tcx, value, span, body)
        },

        TerminatorKind::SwitchInt {
            discr,
            switch_ty: _,
            targets: _,
        } => check_operand(tcx, discr, span, body),

        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        },

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
            fn_span: _,
        } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                // HACK: This is to "unstabilize" the `transmute` intrinsic
                // within const fns. `transmute` is allowed in all other const contexts.
                // This won't really scale to more intrinsics or functions. Let's allow const
                // transmutes in const fn before we add more hacks to this.
                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
                    return Err((
                        span,
                        "can only call `transmute` from const items, not `const fn`".into(),
                    ));
                }

                check_operand(tcx, func, span, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        },

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, cond, span, body),

        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
    }
}
<|endoftext|>


<commit_before>//! Parsing functionality - get cookie data

use std::collections::treemap::TreeMap;
use url;
use serialize::json;
use serialize::json::{Json, Null};
use iron::{Request, Response, Middleware, Alloy, Status, Continue};
use super::Cookie;

/// The cookie parsing `Middleware`.
///
/// It will parse the body of a cookie into the alloy, under type `Cookie`.
///
/// This middleware should be linked (added to the `Chain`)
/// before any other middleware using cookies, or the parsed cookie
/// will not be available to that middleware.
#[deriving(Clone)]
pub struct CookieParser {
    secret: Option<String>
}

impl CookieParser {
    /// Create a new instance of the cookie parsing `Middleware`.
    ///
    /// This instance will parse both RFC 6265-styled cookies:
    /// `key=value; key=value;`
    /// and json-styled cookies, as set with `res.set_json_cookie(...)`.
    pub fn new() -> CookieParser { CookieParser{ secret: None} }

    /// Create a cookie parser with secret, for signed cookies.
    ///
    /// This instance will parse any cookies that have been signed by
    /// you, or that are unsigned. It will not parse those cookies signed by others.
    ///
    /// Otherwise, it will behave exactly like that produced by `new`.
    pub fn signed(secret: String) -> CookieParser { CookieParser{ secret: Some(secret) } }
}

impl Middleware for CookieParser {
    /// Parse the cookie received in the HTTP header.
    ///
    /// This will parse the body of a cookie into the alloy, under type `Cookie`.
    fn enter(&mut self, req: &mut Request, _res: &mut Response, alloy: &mut Alloy) -> Status {
        // Initialize a cookie. This will store parsed cookies and generate signatures.
        let mut new_cookie = Cookie::new(self.secret.clone());

        match req.headers.extensions.find_mut(&"Cookie".to_string()) {
            Some(cookies) => {
                // Initialize an empty json object.
                let mut new_json = json::Object(TreeMap::new());
                new_cookie.map =
                    cookies
                        .as_slice()
                        .split(';')
                        // Decode from uri component encoding
                        .map(|substr| {
                            let vec: Vec<&str> = substr.splitn('=', 1).collect();
                            let key = from_rfc_compliant(vec[0]);
                            let val = from_rfc_compliant(vec[1]);
                            (key, val) })
                        // Check for signed cookies, and filter those not signed by us
                        .filter_map(|cookie| strip_signature(cookie, &new_cookie))
                        // Move json cookies into a separate container
                        .filter(|cookie| parse_json(cookie, &mut new_json))
                        .collect();

                // This cannot be inserted via iterators because strip_signature
                // is already borrowing new_cookie.
                new_cookie.json = new_json;
            },
            None => ()
        }
        alloy.insert(new_cookie);
        Continue
    }
}

fn from_rfc_compliant(string: &str) -> String {
    let decode_result = url::decode_component(
        string
            .chars()
            .skip_while(is_whitespace)
            .collect::<String>()
    );
    match decode_result {
        Ok(s) => s,
        Err(_) => fail!("Failed to decode: '{:s}'", string)
    }
}

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

fn strip_signature((key, val): (String, String), signer: &Cookie) -> Option<(String, String)> {
    if val.len() > 2 && val.as_slice().slice(0, 2) == "s:" {
        if !signer.signed { return None }
        // Extract the signature (in hex), appended onto the cookie after `.`
        return regex!(r"\.[^\.]*$").find(val.as_slice())
            // If it was signed by us, clear the signature
            .and_then(|(beg, end)| {
                signer.sign(&val.as_slice().slice(2, beg).to_string())
                    // We need to maintain access to (beg, end), so we chain the signature
                    .and_then(|signature| {
                        // If the signature is valid, strip it
                        if val.as_slice().slice(beg + 1, end) == signature.as_slice() {
                            // key must be cloned to move out of the closure capture
                            Some((key.clone(), val.as_slice().slice(2, beg).to_string()))
                        // Else, remove the cookie
                        } else {
                            None
                        }
                    })
            })
    }
    match signer.signed {
        true => None,
        false => Some((key, val))
    }
}

fn parse_json(&(ref key, ref val): &(String, String), json: &mut Json) -> bool {
    if val.len() > 2 && val.as_slice().slice(0, 2) == "j:" {
        match *json {
            json::Object(ref mut root) => {
                root.insert(key.clone(),
                    match json::from_str(val.as_slice().slice_from(2)) {
                        Ok(obj) => obj,
                        Err(_)  => Null
                    });
            },
            _                    => ()
        }
        return false
    }
    true
}

#[cfg(test)]
mod test {
    use std::mem::uninitialized;
    use std::collections::{HashMap, TreeMap};
    use http::headers::request::HeaderCollection;
    use iron::{Request, Alloy, Middleware};
    use super::*;
    use super::super::cookie::*;
    use serialize::json::{Object, String};

    // Parse a given `String` as an HTTP Cookie header, using the CookieParser middleware,
    // and return the cookie stored in the alloy by that middleware
    fn get_cookie(secret: Option<String>, cookie: String, alloy: &mut Alloy) -> &Cookie {
        let mut req = Request {
            url: "".to_string(),
            remote_addr: None,
            headers: box HeaderCollection::new(),
            body: "".to_string(),
            method: ::http::method::Get,
        };
        req.headers.extensions.insert("Cookie".to_string(), cookie);
        let mut signer = match secret {
            Some(s) => CookieParser::signed(s),
            None => CookieParser::new()
        };
        unsafe { signer.enter(&mut req, uninitialized(), alloy) };
        alloy.find::<Cookie>().unwrap()
    }

    #[test]
    fn check_cookie() {
        let mut alloy = Alloy::new();
        let cookie = get_cookie(None, "thing=thing".to_string(), &mut alloy);
        let mut map = HashMap::new();
        map.insert("thing".to_string(), "thing".to_string());
        assert_eq!(cookie.map, map);
    }

    #[test]
    fn check_escaping() {
        // Url component decoding should decode the escaped characters
        let mut alloy = Alloy::new();
        let cookie = get_cookie(None,
                                "~%60%21%40%23%24%25%5E%26%2A%28%29_%2B-%3D%7B%7D%7C%5B%5D%5C%3A%22%3B%27%3C%3E%3F%2C.%2F%27=\
                                ~%60%21%40%23%24%25%5E%26%2A%28%29_%2B-%3D%7B%7D%7C%5B%5D%5C%3A%22%3B%27%3C%3E%3F%2C.%2F%27".to_string(),
                                &mut alloy);
        let mut map = HashMap::new();
        map.insert("~`!@#$%^&*()_+-={}|[]\\:\";'<>?,./'".to_string(),
                   "~`!@#$%^&*()_+-={}|[]\\:\";'<>?,./'".to_string());
        assert_eq!(cookie.map, map);
    }

    #[test]
    fn check_signature() {
        // The signature should be the HMAC-SHA256 hash of key "@zzmp" and message "thung"
        let mut alloy = Alloy::new();
        let cookie = get_cookie(Some("@zzmp".to_string()),
                                "thing=s:thung.e99abddcf60cad18f8d4b993efae53e81410cf2b2855af0309f1ae46fa527fbb".to_string(),
                                &mut alloy);
        let mut map = HashMap::new();
        map.insert("thing".to_string(),
                   "thung".to_string());
        assert_eq!(cookie.map, map);
    }

    #[test]
    fn check_silo() {
        // The unsigned cookie should not be parsed by the signed cookie parser
        let mut alloy = Alloy::new();
        let cookie = get_cookie(Some("@zzmp".to_string()),
                                "thing=thung".to_string(),
                                &mut alloy);
        let map = HashMap::new();
        assert_eq!(cookie.map, map);
    }

    #[test]
    fn check_json() {
        // Parse the Url component JSON: {"thing":{"foo":"bar"}}
        let mut alloy = Alloy::new();
        let cookie = get_cookie(None,
                                "thing=j%3A%7B%22foo%22%3A%22bar%22%7D".to_string(),
                                &mut alloy);
        let mut child_map = TreeMap::new();
        child_map.insert("foo".to_string(), String("bar".to_string()));
        let child = Object(child_map);
        let mut root_map = TreeMap::new();
        root_map.insert("thing".to_string(), child);
        let root = Object(root_map);
        assert_eq!(cookie.json, root); // FIXME
    }
}
<commit_msg>(fix) Constant time HMAC comparison<commit_after>//! Parsing functionality - get cookie data

use std::collections::treemap::TreeMap;
use url;
use serialize::json;
use serialize::json::{Json, Null};
use iron::{Request, Response, Middleware, Alloy, Status, Continue};
use super::Cookie;
use crypto::util::fixed_time_eq;

/// The cookie parsing `Middleware`.
///
/// It will parse the body of a cookie into the alloy, under type `Cookie`.
///
/// This middleware should be linked (added to the `Chain`)
/// before any other middleware using cookies, or the parsed cookie
/// will not be available to that middleware.
#[deriving(Clone)]
pub struct CookieParser {
    secret: Option<String>
}

impl CookieParser {
    /// Create a new instance of the cookie parsing `Middleware`.
    ///
    /// This instance will parse both RFC 6265-styled cookies:
    /// `key=value; key=value;`
    /// and json-styled cookies, as set with `res.set_json_cookie(...)`.
    pub fn new() -> CookieParser { CookieParser{ secret: None} }

    /// Create a cookie parser with secret, for signed cookies.
    ///
    /// This instance will parse any cookies that have been signed by
    /// you, or that are unsigned. It will not parse those cookies signed by others.
    ///
    /// Otherwise, it will behave exactly like that produced by `new`.
    pub fn signed(secret: String) -> CookieParser { CookieParser{ secret: Some(secret) } }
}

impl Middleware for CookieParser {
    /// Parse the cookie received in the HTTP header.
    ///
    /// This will parse the body of a cookie into the alloy, under type `Cookie`.
    fn enter(&mut self, req: &mut Request, _res: &mut Response, alloy: &mut Alloy) -> Status {
        // Initialize a cookie. This will store parsed cookies and generate signatures.
        let mut new_cookie = Cookie::new(self.secret.clone());

        match req.headers.extensions.find_mut(&"Cookie".to_string()) {
            Some(cookies) => {
                // Initialize an empty json object.
                let mut new_json = json::Object(TreeMap::new());
                new_cookie.map =
                    cookies
                        .as_slice()
                        .split(';')
                        // Decode from uri component encoding
                        .map(|substr| {
                            let vec: Vec<&str> = substr.splitn('=', 1).collect();
                            let key = from_rfc_compliant(vec[0]);
                            let val = from_rfc_compliant(vec[1]);
                            (key, val) })
                        // Check for signed cookies, and filter those not signed by us
                        .filter_map(|cookie| strip_signature(cookie, &new_cookie))
                        // Move json cookies into a separate container
                        .filter(|cookie| parse_json(cookie, &mut new_json))
                        .collect();

                // This cannot be inserted via iterators because strip_signature
                // is already borrowing new_cookie.
                new_cookie.json = new_json;
            },
            None => ()
        }
        alloy.insert(new_cookie);
        Continue
    }
}

fn from_rfc_compliant(string: &str) -> String {
    let decode_result = url::decode_component(
        string
            .chars()
            .skip_while(is_whitespace)
            .collect::<String>()
    );
    match decode_result {
        Ok(s) => s,
        Err(_) => fail!("Failed to decode: '{:s}'", string)
    }
}

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

fn strip_signature((key, val): (String, String), signer: &Cookie) -> Option<(String, String)> {
    if val.len() > 2 && val.as_slice().slice(0, 2) == "s:" {
        if !signer.signed { return None }
        // Extract the signature (in hex), appended onto the cookie after `.`
        return regex!(r"\.[^\.]*$").find(val.as_slice())
            // If it was signed by us, clear the signature
            .and_then(|(beg, end)| {
                signer.sign(&val.as_slice().slice(2, beg).to_string())
                    // We need to maintain access to (beg, end), so we chain the signature
                    .and_then(|signature| {
                        // If the signature is valid, strip it
                        if fixed_time_eq(val.as_slice().slice(beg + 1, end).as_bytes(), signature.as_bytes()) {
                            // key must be cloned to move out of the closure capture
                            Some((key.clone(), val.as_slice().slice(2, beg).to_string()))
                        // Else, remove the cookie
                        } else {
                            None
                        }
                    })
            })
    }
    match signer.signed {
        true => None,
        false => Some((key, val))
    }
}

fn parse_json(&(ref key, ref val): &(String, String), json: &mut Json) -> bool {
    if val.len() > 2 && val.as_slice().slice(0, 2) == "j:" {
        match *json {
            json::Object(ref mut root) => {
                root.insert(key.clone(),
                    match json::from_str(val.as_slice().slice_from(2)) {
                        Ok(obj) => obj,
                        Err(_)  => Null
                    });
            },
            _                    => ()
        }
        return false
    }
    true
}

#[cfg(test)]
mod test {
    use std::mem::uninitialized;
    use std::collections::{HashMap, TreeMap};
    use http::headers::request::HeaderCollection;
    use iron::{Request, Alloy, Middleware};
    use super::*;
    use super::super::cookie::*;
    use serialize::json::{Object, String};

    // Parse a given `String` as an HTTP Cookie header, using the CookieParser middleware,
    // and return the cookie stored in the alloy by that middleware
    fn get_cookie(secret: Option<String>, cookie: String, alloy: &mut Alloy) -> &Cookie {
        let mut req = Request {
            url: "".to_string(),
            remote_addr: None,
            headers: box HeaderCollection::new(),
            body: "".to_string(),
            method: ::http::method::Get,
        };
        req.headers.extensions.insert("Cookie".to_string(), cookie);
        let mut signer = match secret {
            Some(s) => CookieParser::signed(s),
            None => CookieParser::new()
        };
        unsafe { signer.enter(&mut req, uninitialized(), alloy) };
        alloy.find::<Cookie>().unwrap()
    }

    #[test]
    fn check_cookie() {
        let mut alloy = Alloy::new();
        let cookie = get_cookie(None, "thing=thing".to_string(), &mut alloy);
        let mut map = HashMap::new();
        map.insert("thing".to_string(), "thing".to_string());
        assert_eq!(cookie.map, map);
    }

    #[test]
    fn check_escaping() {
        // Url component decoding should decode the escaped characters
        let mut alloy = Alloy::new();
        let cookie = get_cookie(None,
                                "~%60%21%40%23%24%25%5E%26%2A%28%29_%2B-%3D%7B%7D%7C%5B%5D%5C%3A%22%3B%27%3C%3E%3F%2C.%2F%27=\
                                ~%60%21%40%23%24%25%5E%26%2A%28%29_%2B-%3D%7B%7D%7C%5B%5D%5C%3A%22%3B%27%3C%3E%3F%2C.%2F%27".to_string(),
                                &mut alloy);
        let mut map = HashMap::new();
        map.insert("~`!@#$%^&*()_+-={}|[]\\:\";'<>?,./'".to_string(),
                   "~`!@#$%^&*()_+-={}|[]\\:\";'<>?,./'".to_string());
        assert_eq!(cookie.map, map);
    }

    #[test]
    fn check_signature() {
        // The signature should be the HMAC-SHA256 hash of key "@zzmp" and message "thung"
        let mut alloy = Alloy::new();
        let cookie = get_cookie(Some("@zzmp".to_string()),
                                "thing=s:thung.e99abddcf60cad18f8d4b993efae53e81410cf2b2855af0309f1ae46fa527fbb".to_string(),
                                &mut alloy);
        let mut map = HashMap::new();
        map.insert("thing".to_string(),
                   "thung".to_string());
        assert_eq!(cookie.map, map);
    }

    #[test]
    fn check_silo() {
        // The unsigned cookie should not be parsed by the signed cookie parser
        let mut alloy = Alloy::new();
        let cookie = get_cookie(Some("@zzmp".to_string()),
                                "thing=thung".to_string(),
                                &mut alloy);
        let map = HashMap::new();
        assert_eq!(cookie.map, map);
    }

    #[test]
    fn check_json() {
        // Parse the Url component JSON: {"thing":{"foo":"bar"}}
        let mut alloy = Alloy::new();
        let cookie = get_cookie(None,
                                "thing=j%3A%7B%22foo%22%3A%22bar%22%7D".to_string(),
                                &mut alloy);
        let mut child_map = TreeMap::new();
        child_map.insert("foo".to_string(), String("bar".to_string()));
        let child = Object(child_map);
        let mut root_map = TreeMap::new();
        root_map.insert("thing".to_string(), child);
        let root = Object(root_map);
        assert_eq!(cookie.json, root); // FIXME
    }
}
<|endoftext|>


<commit_before>use expr::{Expr, Op};

#[derive(Clone, Debug, PartialEq)]
enum Token {
  Plus,
  Integer(i64),
}

struct Lexer {
  text: String,
  pos: i64,
}

impl Lexer {
  pub fn new(text: String) -> Lexer {
    Lexer { text: text, pos: 0 }
  }

  fn remove_first(&mut self, n: usize) {
    let t = self.text.clone();
    let (old, remainder) = t.split_at(n);
    self.text = remainder.to_string();
  }

  pub fn integer(&mut self) -> Option<Token> {
    let int_str: String = self.text
      .chars()
      .take_while(|c| c.is_digit(10))
      .collect();

    match int_str.parse::<i64>() {
      Ok(n) => {
        self.remove_first(int_str.len());
        return Some(Token::Integer(n));
      }
      Err(e) => {
        println!("---------------{:?}", e);
        panic!();
      }
    }

    None
  }

  pub fn plus(&mut self) -> Option<Token> {
    if self.text.starts_with('+') {
      self.remove_first(1);
      return Some(Token::Plus);
    } else {
      panic!();
    }
  }

  pub fn get_next_token(&mut self) -> Option<Token> {
    // TODO: make this do real things

    self.pos += 1;

    match self.pos {
      1 => self.integer(),
      2 => self.plus(),
      3 => self.integer(),
      4 => self.plus(),
      5 => self.integer(),
      6 => None,
      _ => {
        println!("----------{:?}", self.pos);
        panic!();
      }
    }
  }
}

struct Parser {
  text: String,
  lexer: Lexer,
}

impl Parser {
  pub fn new(text: String) -> Parser {
    let mut lexer = Lexer::new(text.clone());

    Parser {
      text: text,
      lexer: lexer,
    }
  }

  pub fn factor(&mut self) -> Option<Expr> {
    let token = self.lexer.get_next_token();

    match token {
      Some(Token::Integer(n)) => {
        return Some(Expr::Integer(n));
      }
      Some(t) => {
        println!("invalid factor: {:?}", t);
        panic!();
      }
      _ => panic!()
    }
  }

  pub fn expr(&mut self) -> Option<Expr> {
    let mut node = self.factor();

    let mut token = self.lexer.get_next_token();

    while token == Some(Token::Plus) {
      match token {
        Some(Token::Plus) => {
          let right = self.factor();

          match (node, right) {
            (Some(e1), Some(e2)) => {
              node = Some(
                Expr::BinOp(
                  Op::Plus,
                  Box::new(e1),
                  Box::new(e2),
                )
              );
            },
            (a, b) => {
              println!("token: {:?}", a);
              println!("right: {:?}", b);
              panic!();
            }
          }
        },
        _ => panic!()
      }

      token = self.lexer.get_next_token();
      println!("------- token: {:?}", token);
    }

    node
  }
}

pub fn parse(input: &str) -> Expr {
  let mut parser = Parser::new(input.to_string());

  match parser.expr() {
    Some(e) => e,
    None => panic!(),
  }
}

#[test]
fn test_parse_int() {

  /*
  assert_eq!(
    Expr::BinOp(
      Op::Plus,
        Box::new(Expr::Integer(3)),
        Box::new(Expr::Integer(4)),
      ),
    parse("3+4")
  );
  */

  assert_eq!(
    Expr::BinOp(
      Op::Plus,
      Box::new(Expr::BinOp(
        Op::Plus,
        Box::new(Expr::Integer(3)),
        Box::new(Expr::Integer(4)),
        ),
      ),
      Box::new(Expr::Integer(5)),
    ),
    parse("3+4+5")
  );
}
<commit_msg>lexer does a real-ish thing<commit_after>use expr::{Expr, Op};

#[derive(Clone, Debug, PartialEq)]
enum Token {
  Plus,
  Integer(i64),
}

struct Lexer {
  text: String,
  pos: usize,
}

impl Lexer {
  pub fn new(text: String) -> Lexer {
    Lexer { text: text, pos: 0 }
  }

  fn remove_first(&mut self, n: usize) {
    let t = self.text.clone();
    let (old, remainder) = t.split_at(n);
    self.text = remainder.to_string();
  }

  pub fn integer(&mut self) -> Option<Token> {
    let (_, s) = self.text.split_at(self.pos);

    let int_str: String = s
      .chars()
      .take_while(|c| c.is_digit(10))
      .collect();

    match int_str.parse::<i64>() {
      Ok(n) => {
        self.pos += int_str.len();
        return Some(Token::Integer(n));
      }
      Err(e) => {
        println!("---------------{:?}", e);
        panic!();
      }
    }

    None
  }

  /*
  pub fn plus(&mut self) -> Option<Token> {
    if self.text.starts_with('+') {
      self.remove_first(1);
      return Some(Token::Plus);
    } else {
      panic!();
    }
  }
  */

  pub fn get_next_token(&mut self) -> Option<Token> {
    let old_pos = self.pos;

    match self.text.chars().nth(self.pos) {
      Some('+') => {
        self.pos += 1;
        Some(Token::Plus)
      },
      Some(x) if x.is_numeric() => self.integer(),
      None => None,
      _ => panic!()
    }
  }
}

struct Parser {
  text: String,
  lexer: Lexer,
}

impl Parser {
  pub fn new(text: String) -> Parser {
    let mut lexer = Lexer::new(text.clone());

    Parser {
      text: text,
      lexer: lexer,
    }
  }

  pub fn factor(&mut self) -> Option<Expr> {
    let token = self.lexer.get_next_token();

    match token {
      Some(Token::Integer(n)) => {
        return Some(Expr::Integer(n));
      }
      Some(t) => {
        println!("invalid factor: {:?}", t);
        panic!();
      }
      _ => panic!()
    }
  }

  pub fn expr(&mut self) -> Option<Expr> {
    let mut node = self.factor();

    let mut token = self.lexer.get_next_token();

    while token == Some(Token::Plus) {
      match token {
        Some(Token::Plus) => {
          let right = self.factor();

          match (node, right) {
            (Some(e1), Some(e2)) => {
              node = Some(
                Expr::BinOp(
                  Op::Plus,
                  Box::new(e1),
                  Box::new(e2),
                )
              );
            },
            (a, b) => {
              println!("token: {:?}", a);
              println!("right: {:?}", b);
              panic!();
            }
          }
        },
        _ => panic!()
      }

      token = self.lexer.get_next_token();
      println!("------- token: {:?}", token);
    }

    node
  }
}

pub fn parse(input: &str) -> Expr {
  let mut parser = Parser::new(input.to_string());

  match parser.expr() {
    Some(e) => e,
    None => panic!(),
  }
}

#[test]
fn test_parse_int() {
  assert_eq!(
    Expr::BinOp(
      Op::Plus,
        Box::new(Expr::Integer(3)),
        Box::new(Expr::Integer(4)),
      ),
    parse("3+4")
  );

  assert_eq!(
    Expr::BinOp(
      Op::Plus,
      Box::new(Expr::BinOp(
        Op::Plus,
        Box::new(Expr::Integer(3)),
        Box::new(Expr::Integer(4)),
        ),
      ),
      Box::new(Expr::Integer(5)),
    ),
    parse("3+4+5")
  );
}
<|endoftext|>


<commit_before>use num_bigint::Sign;

use std::i64;

use label::Label;
use program::{Program, Command, Integer, BigInteger, SizedInteger, SourceLoc};
use ::WsParseError;

#[derive(Debug, Clone)]
struct ParseState<'a> {
    line: usize,
    column: usize,
    index: usize,
    item: Option<u8>,
    buffer: &'a [u8]
}

impl<'a> Iterator for ParseState<'a> {
    type Item = u8;

    fn next(&mut self) -> Option<u8> {
        loop {
            match self.item {
                Some(b'\n') => {self.index += 1; self.line   += 1; self.column = 1;},
                Some(_)     => {self.index += 1; self.column += 1;}
                None        => ()
            }

            return match self.buffer.get(self.index) {
                Some(&new) => {
                    self.item = Some(new);
                    match new {
                        b'\n' | b'\t' | b' ' => Some(new),
                        _ => continue
                    }
                }
                None => None
            }
        }
    }
}

impl<'a> ParseState<'a> {
    fn new(buffer: &'a [u8]) -> ParseState {
        ParseState {line: 1, column: 1, index: 0, item: None, buffer: buffer}
    }

    fn parse_arg(&mut self) -> Result<SizedInteger, WsParseError> {
        let mut accum = 0u64;
        let negative;

        // this could be rewritten to a while let loop if rust'd support while { } else { }
        negative = match self.next() {
            Some(b'\n') => return Ok(SizedInteger::Small(0)),
            Some(b'\t') => true,
            Some(b' ' ) => false,
            None        => return Err(WsParseError::new("Hit EOF while expecting argument", self.index, self.line, self.column)),
            _           => unreachable!()
        };

        loop {
            match self.next() {
                Some(byte) => {
                    accum = accum << 1 | match byte {
                        b'\n' => return Ok(SizedInteger::Small(if negative {
                            -(accum as Integer)
                        } else {
                            accum as Integer
                        })),
                        b'\t' => 1,
                        b' '  => 0,
                        _     => unreachable!()
                    };
                    if accum > i64::MAX as u64 {
                        break;
                    }
                }
                None => return Err(WsParseError::new("Hit EOF while parsing argument", self.index, self.line, self.column))
            }
        }

        // The only way we get here is if the upper bit of accum is set. This
        // means that exactly two segments can be filled. Due to the way the ws
        // format works we store in big-endian order first. Then the order is
        // reversed, making the smallest digit the frontmost one. An extra 0 is
        // added to the end, and the whole vector is shifted to make up for space
        // left over in the smallest digit.
        let mut segments: Vec<u32> = Vec::new();
        segments.push((accum >> 32) as u32);
        segments.push(accum as u32);

        let mut bit = 31;
        let mut accum = 0u32;
        while let Some(byte) = self.next() {
            accum |= match byte {
                b'\n' => {
                    segments.push(accum);
                    segments.reverse();
                    segments.push(0);
                    bit += 1; // the amount we need to shift everything to align the lowest bit
                    for i in 0..segments.len() - 1 {
                        segments[i] = ((segments[i] as u64 | ((segments[i + 1] as u64)<< 32)) >> bit) as u32;
                    };
                    return Ok(SizedInteger::Big(BigInteger::new(if negative {Sign::Minus} else {Sign::Plus}, segments)));
                },
                b'\t' => 1,
                b' '  => 0,
                _     => unreachable!()
            } << bit;

            if bit == 0 {
                segments.push(accum);
                bit = 32;
                accum = 0;
            }
            bit -= 1;
        }
        Err(WsParseError::new("Hit EOF while parsing argument", self.index, self.line, self.column))
    }

    fn parse_label(&mut self) -> Result<Label, WsParseError> {
        let mut label = Label::new();

        while let Some(byte) = self.next() {
            match byte {
                b'\n' => return Ok(label),
                b'\t' => label.push(true),
                b' '  => label.push(false),
                _ => unreachable!()
            };
        }
        Err(WsParseError::new("Hit EOF while parsing label", self.index, self.line, self.column))
    }
}

impl Program {
    /// Parse a program written in whitespace to a format suitable
    /// for execution.
    pub fn parse(code: Vec<u8>) -> Result<Program, WsParseError> {

        let mut commands = Vec::<Command>::new();
        let mut sourcelocs = Vec::<SourceLoc>::new();
        {
            let mut state = ParseState::new(&code);

            let mut hash = 0;
            let mut hash_length = 0;

            let mut startline = 0usize;
            let mut startcolumn = 0usize;
            let mut startindex = 0usize;

            while let Some(byte) = state.next() {
                if hash_length == 0 {
                    startline   = state.line;
                    startcolumn = state.column;
                    startindex  = state.index;
                }

                hash = hash * 3 + match byte {
                    b' '  => 0,
                    b'\t' => 1,
                    b'\n' => 2,
                    _     => unreachable!()
                };

                hash_length += 1;

                let command = match (hash_length, hash) {
                    (2, 0)  => match try!(state.parse_arg()) {
                        SizedInteger::Small(value) => Command::Push {value: value},
                        SizedInteger::Big(value) => Command::PushBig {value: value}
                    },
                    (3, 6)  => Command::Duplicate,
                    (3, 3)  => match try!(state.parse_arg()) {
                        SizedInteger::Small(value) => Command::Copy {index: value as usize},
                        SizedInteger::Big(value) => return Err(WsParseError::new(format!("Copy argument too large: {}", value), startindex, startline, startcolumn))
                    },
                    (3, 7)  => Command::Swap,
                    (3, 8)  => Command::Discard,
                    (3, 5)  => match try!(state.parse_arg()) {
                        SizedInteger::Small(value) => Command::Slide {amount: value as usize},
                        SizedInteger::Big(value) => return Err(WsParseError::new(format!("Slide argument too large: {}", value), startindex, startline, startcolumn))
                    },

                    (4, 27) => Command::Add,
                    (4, 28) => Command::Subtract,
                    (4, 29) => Command::Multiply,
                    (4, 30) => Command::Divide,
                    (4, 31) => Command::Modulo,

                    (3, 12) => Command::Set,
                    (3, 13) => Command::Get,

                    (3, 18) => Command::Label,
                    (3, 19) => Command::Call {index: 0},
                    (3, 20) => Command::Jump {index: 0},
                    (3, 21) => Command::JumpIfZero {index: 0},
                    (3, 22) => Command::JumpIfNegative {index: 0},
                    (3, 23) => Command::EndSubroutine,
                    (3, 26) => Command::EndProgram,

                    (4, 45) => Command::PrintChar,
                    (4, 46) => Command::PrintNum,
                    (4, 48) => Command::InputChar,
                    (4, 49) => Command::InputNum,

                    (3, 4) |
                    (3, 11) |
                    (3, 14) |
                    (3, 17) |
                    (3, 24) |
                    (3, 25) |
                    (4, _) => {
                        let mut buf = [0u8; 5];
                        for i in 0 .. hash_length {
                            buf[i] = hash % 3;
                            hash /= 3;
                        }

                        let s = buf[..hash_length].iter().rev().map(|c| if *c == 0 {
                            'S'
                        } else if *c == 1 {
                            'T'
                        } else {
                            'N'
                        }).collect::<String>();

                        return Err(WsParseError::new(format!("invalid command: {}", s), startindex, startline, startcolumn));
                    },
                    (_, _) => continue
                };

                hash = 0;
                hash_length = 0;

                let label = match command {
                    Command::Label | Command::Call {..} | Command::Jump {..} |
                    Command::JumpIfZero {..} | Command::JumpIfNegative {..} => Some(try!(state.parse_label()).into()),
                    _ => None
                };

                commands.push(command);
                sourcelocs.push(SourceLoc {
                    line: startline,
                    column: startcolumn,
                    span: startindex .. state.index + 1,
                    label: label
                });
            }

            if hash_length != 0 {
                return Err(WsParseError::new("Hit EOF while parsing command", state.index, state.line, state.column));
            }
        }

        let mut program = Program {
            source: Some(code),
            commands: commands,
            locs: Some(sourcelocs),
            source_is_whitespace: true
        };

        try!(program.compile());

        Ok(program)
    }

    /// Serialize the internal representation back into a whitespace program.
    pub fn dump(&self) -> Vec<u8> {
        let mut buffer = Vec::<u8>::new();

        use program::Command::*;
        for (index, command) in self.commands.iter().enumerate() {
            let (code, arg): (&[u8], _) = match *command {
                Push {value}           => (b"  ", Some(number_to_ws(value))),
                PushBig {ref value}    => (b"  ", Some(large_number_to_ws(value))),
                Duplicate              => (b" \n ", None),
                Copy {index}           => (b" \t ", Some(number_to_ws(index as Integer))),
                Swap                   => (b" \n\t", None),
                Discard                => (b" \n\n", None),
                Slide {amount}         => (b" \t\n", Some(number_to_ws(amount as Integer))),
                Add                    => (b"\t   ", None),
                Subtract               => (b"\t  \t", None),
                Multiply               => (b"\t  \n", None),
                Divide                 => (b"\t \t ", None),
                Modulo                 => (b"\t \t\t", None),
                Set                    => (b"\t\t ", None),
                Get                    => (b"\t\t\t", None),
                Label                  => (b"\n  ", Some(label_to_ws(index))),
                Call {index}           => (b"\n \t", Some(label_to_ws(index - 1))),
                Jump {index}           => (b"\n \n", Some(label_to_ws(index - 1))),
                JumpIfZero {index}     => (b"\n\t ", Some(label_to_ws(index - 1))),
                JumpIfNegative {index} => (b"\n\t\t", Some(label_to_ws(index - 1))),
                EndSubroutine          => (b"\n\t\n", None),
                EndProgram             => (b"\n\n\n", None),
                PrintChar              => (b"\t\n  ", None),
                PrintNum               => (b"\t\n \t", None),
                InputChar              => (b"\t\n\t ", None),
                InputNum               => (b"\t\n\t\t", None),
            };
            buffer.extend(code);
            if let Some(arg) = arg {
                buffer.extend(arg);
            }
        }
        buffer
    }
}

use std::mem::size_of;

fn number_to_ws(mut n: Integer) -> Vec<u8> {
    let mut res = Vec::new();
    if n < 0 {
        n = -n;
        res.push(b'\t');
    } else if n > 0{
        res.push(b' ');
    }

    let n = n as usize;
    let mut i = size_of::<usize>() * 8;
    let mut force = false;
    while i != 0 {
        i -= 1;
        if (n & (1 << i)) != 0 {
            force = true;
            res.push(b'\t');
        } else if force {
            res.push(b' ');
        }
    }
    res.push(b'\n');
    res
}

fn large_number_to_ws(n: &BigInteger) -> Vec<u8> {
    let mut res = Vec::new();
    let (sign, bytes) = n.to_bytes_be();

    match sign {
        Sign::Minus  => res.push(b'\t'),
        Sign::Plus   => res.push(b' '),
        Sign::NoSign => {
            res.push(b'\n');
            return res;
        }
    }

    let mut force = false;
    for byte in bytes {
        for n in 0..8 {
            if byte & (1 << (7 - n)) != 0 {
                force = true;
                res.push(b'\t');
            } else if force {
                res.push(b' ');
            }
        }
    }

    res.push(b'\n');
    return res;
}

fn label_to_ws(i: usize) -> Vec<u8> {
    let label: Label = i.to_string().as_bytes().into();
    let mut res: Vec<u8> = (&label).into_iter()
                                   .map(|i| if i {b'\t'} else {b' '})
                                   .collect();
    res.push(b'\n');
    res
}
<commit_msg>Try to reuse the original label name in Program::dump<commit_after>use num_bigint::Sign;

use std::i64;
use std::rc::Rc;

use label::Label;
use program::{Program, Command, Integer, BigInteger, SizedInteger, SourceLoc};
use ::WsParseError;

#[derive(Debug, Clone)]
struct ParseState<'a> {
    line: usize,
    column: usize,
    index: usize,
    item: Option<u8>,
    buffer: &'a [u8]
}

impl<'a> Iterator for ParseState<'a> {
    type Item = u8;

    fn next(&mut self) -> Option<u8> {
        loop {
            match self.item {
                Some(b'\n') => {self.index += 1; self.line   += 1; self.column = 1;},
                Some(_)     => {self.index += 1; self.column += 1;}
                None        => ()
            }

            return match self.buffer.get(self.index) {
                Some(&new) => {
                    self.item = Some(new);
                    match new {
                        b'\n' | b'\t' | b' ' => Some(new),
                        _ => continue
                    }
                }
                None => None
            }
        }
    }
}

impl<'a> ParseState<'a> {
    fn new(buffer: &'a [u8]) -> ParseState {
        ParseState {line: 1, column: 1, index: 0, item: None, buffer: buffer}
    }

    fn parse_arg(&mut self) -> Result<SizedInteger, WsParseError> {
        let mut accum = 0u64;
        let negative;

        // this could be rewritten to a while let loop if rust'd support while { } else { }
        negative = match self.next() {
            Some(b'\n') => return Ok(SizedInteger::Small(0)),
            Some(b'\t') => true,
            Some(b' ' ) => false,
            None        => return Err(WsParseError::new("Hit EOF while expecting argument", self.index, self.line, self.column)),
            _           => unreachable!()
        };

        loop {
            match self.next() {
                Some(byte) => {
                    accum = accum << 1 | match byte {
                        b'\n' => return Ok(SizedInteger::Small(if negative {
                            -(accum as Integer)
                        } else {
                            accum as Integer
                        })),
                        b'\t' => 1,
                        b' '  => 0,
                        _     => unreachable!()
                    };
                    if accum > i64::MAX as u64 {
                        break;
                    }
                }
                None => return Err(WsParseError::new("Hit EOF while parsing argument", self.index, self.line, self.column))
            }
        }

        // The only way we get here is if the upper bit of accum is set. This
        // means that exactly two segments can be filled. Due to the way the ws
        // format works we store in big-endian order first. Then the order is
        // reversed, making the smallest digit the frontmost one. An extra 0 is
        // added to the end, and the whole vector is shifted to make up for space
        // left over in the smallest digit.
        let mut segments: Vec<u32> = Vec::new();
        segments.push((accum >> 32) as u32);
        segments.push(accum as u32);

        let mut bit = 31;
        let mut accum = 0u32;
        while let Some(byte) = self.next() {
            accum |= match byte {
                b'\n' => {
                    segments.push(accum);
                    segments.reverse();
                    segments.push(0);
                    bit += 1; // the amount we need to shift everything to align the lowest bit
                    for i in 0..segments.len() - 1 {
                        segments[i] = ((segments[i] as u64 | ((segments[i + 1] as u64)<< 32)) >> bit) as u32;
                    };
                    return Ok(SizedInteger::Big(BigInteger::new(if negative {Sign::Minus} else {Sign::Plus}, segments)));
                },
                b'\t' => 1,
                b' '  => 0,
                _     => unreachable!()
            } << bit;

            if bit == 0 {
                segments.push(accum);
                bit = 32;
                accum = 0;
            }
            bit -= 1;
        }
        Err(WsParseError::new("Hit EOF while parsing argument", self.index, self.line, self.column))
    }

    fn parse_label(&mut self) -> Result<Label, WsParseError> {
        let mut label = Label::new();

        while let Some(byte) = self.next() {
            match byte {
                b'\n' => return Ok(label),
                b'\t' => label.push(true),
                b' '  => label.push(false),
                _ => unreachable!()
            };
        }
        Err(WsParseError::new("Hit EOF while parsing label", self.index, self.line, self.column))
    }
}

impl Program {
    /// Parse a program written in whitespace to a format suitable
    /// for execution.
    pub fn parse(code: Vec<u8>) -> Result<Program, WsParseError> {

        let mut commands = Vec::<Command>::new();
        let mut sourcelocs = Vec::<SourceLoc>::new();
        {
            let mut state = ParseState::new(&code);

            let mut hash = 0;
            let mut hash_length = 0;

            let mut startline = 0usize;
            let mut startcolumn = 0usize;
            let mut startindex = 0usize;

            while let Some(byte) = state.next() {
                if hash_length == 0 {
                    startline   = state.line;
                    startcolumn = state.column;
                    startindex  = state.index;
                }

                hash = hash * 3 + match byte {
                    b' '  => 0,
                    b'\t' => 1,
                    b'\n' => 2,
                    _     => unreachable!()
                };

                hash_length += 1;

                let command = match (hash_length, hash) {
                    (2, 0)  => match try!(state.parse_arg()) {
                        SizedInteger::Small(value) => Command::Push {value: value},
                        SizedInteger::Big(value) => Command::PushBig {value: value}
                    },
                    (3, 6)  => Command::Duplicate,
                    (3, 3)  => match try!(state.parse_arg()) {
                        SizedInteger::Small(value) => Command::Copy {index: value as usize},
                        SizedInteger::Big(value) => return Err(WsParseError::new(format!("Copy argument too large: {}", value), startindex, startline, startcolumn))
                    },
                    (3, 7)  => Command::Swap,
                    (3, 8)  => Command::Discard,
                    (3, 5)  => match try!(state.parse_arg()) {
                        SizedInteger::Small(value) => Command::Slide {amount: value as usize},
                        SizedInteger::Big(value) => return Err(WsParseError::new(format!("Slide argument too large: {}", value), startindex, startline, startcolumn))
                    },

                    (4, 27) => Command::Add,
                    (4, 28) => Command::Subtract,
                    (4, 29) => Command::Multiply,
                    (4, 30) => Command::Divide,
                    (4, 31) => Command::Modulo,

                    (3, 12) => Command::Set,
                    (3, 13) => Command::Get,

                    (3, 18) => Command::Label,
                    (3, 19) => Command::Call {index: 0},
                    (3, 20) => Command::Jump {index: 0},
                    (3, 21) => Command::JumpIfZero {index: 0},
                    (3, 22) => Command::JumpIfNegative {index: 0},
                    (3, 23) => Command::EndSubroutine,
                    (3, 26) => Command::EndProgram,

                    (4, 45) => Command::PrintChar,
                    (4, 46) => Command::PrintNum,
                    (4, 48) => Command::InputChar,
                    (4, 49) => Command::InputNum,

                    (3, 4) |
                    (3, 11) |
                    (3, 14) |
                    (3, 17) |
                    (3, 24) |
                    (3, 25) |
                    (4, _) => {
                        let mut buf = [0u8; 5];
                        for i in 0 .. hash_length {
                            buf[i] = hash % 3;
                            hash /= 3;
                        }

                        let s = buf[..hash_length].iter().rev().map(|c| if *c == 0 {
                            'S'
                        } else if *c == 1 {
                            'T'
                        } else {
                            'N'
                        }).collect::<String>();

                        return Err(WsParseError::new(format!("invalid command: {}", s), startindex, startline, startcolumn));
                    },
                    (_, _) => continue
                };

                hash = 0;
                hash_length = 0;

                let label = match command {
                    Command::Label | Command::Call {..} | Command::Jump {..} |
                    Command::JumpIfZero {..} | Command::JumpIfNegative {..} => Some(try!(state.parse_label()).into()),
                    _ => None
                };

                commands.push(command);
                sourcelocs.push(SourceLoc {
                    line: startline,
                    column: startcolumn,
                    span: startindex .. state.index + 1,
                    label: label
                });
            }

            if hash_length != 0 {
                return Err(WsParseError::new("Hit EOF while parsing command", state.index, state.line, state.column));
            }
        }

        let mut program = Program {
            source: Some(code),
            commands: commands,
            locs: Some(sourcelocs),
            source_is_whitespace: true
        };

        try!(program.compile());

        Ok(program)
    }

    /// Serialize the internal representation back into a whitespace program.
    pub fn dump(&self) -> Vec<u8> {
        let mut buffer = Vec::<u8>::new();

        use program::Command::*;
        for (index, command) in self.commands.iter().enumerate() {
            let label = self.locs.as_ref().and_then(|l| l[index].label.as_ref());
            let (code, arg): (&[u8], _) = match *command {
                Push {value}           => (b"  ", Some(number_to_ws(value))),
                PushBig {ref value}    => (b"  ", Some(large_number_to_ws(value))),
                Duplicate              => (b" \n ", None),
                Copy {index}           => (b" \t ", Some(number_to_ws(index as Integer))),
                Swap                   => (b" \n\t", None),
                Discard                => (b" \n\n", None),
                Slide {amount}         => (b" \t\n", Some(number_to_ws(amount as Integer))),
                Add                    => (b"\t   ", None),
                Subtract               => (b"\t  \t", None),
                Multiply               => (b"\t  \n", None),
                Divide                 => (b"\t \t ", None),
                Modulo                 => (b"\t \t\t", None),
                Set                    => (b"\t\t ", None),
                Get                    => (b"\t\t\t", None),
                Label                  => (b"\n  ", Some(label_to_ws(label, index))),
                Call {index}           => (b"\n \t", Some(label_to_ws(label, index - 1))),
                Jump {index}           => (b"\n \n", Some(label_to_ws(label, index - 1))),
                JumpIfZero {index}     => (b"\n\t ", Some(label_to_ws(label, index - 1))),
                JumpIfNegative {index} => (b"\n\t\t", Some(label_to_ws(label, index - 1))),
                EndSubroutine          => (b"\n\t\n", None),
                EndProgram             => (b"\n\n\n", None),
                PrintChar              => (b"\t\n  ", None),
                PrintNum               => (b"\t\n \t", None),
                InputChar              => (b"\t\n\t ", None),
                InputNum               => (b"\t\n\t\t", None),
            };
            buffer.extend(code);
            if let Some(arg) = arg {
                buffer.extend(arg);
            }
        }
        buffer
    }
}

use std::mem::size_of;

fn number_to_ws(mut n: Integer) -> Vec<u8> {
    let mut res = Vec::new();
    if n < 0 {
        n = -n;
        res.push(b'\t');
    } else if n > 0{
        res.push(b' ');
    }

    let n = n as usize;
    let mut i = size_of::<usize>() * 8;
    let mut force = false;
    while i != 0 {
        i -= 1;
        if (n & (1 << i)) != 0 {
            force = true;
            res.push(b'\t');
        } else if force {
            res.push(b' ');
        }
    }
    res.push(b'\n');
    res
}

fn large_number_to_ws(n: &BigInteger) -> Vec<u8> {
    let mut res = Vec::new();
    let (sign, bytes) = n.to_bytes_be();

    match sign {
        Sign::Minus  => res.push(b'\t'),
        Sign::Plus   => res.push(b' '),
        Sign::NoSign => {
            res.push(b'\n');
            return res;
        }
    }

    let mut force = false;
    for byte in bytes {
        for n in 0..8 {
            if byte & (1 << (7 - n)) != 0 {
                force = true;
                res.push(b'\t');
            } else if force {
                res.push(b' ');
            }
        }
    }

    res.push(b'\n');
    return res;
}

fn label_to_ws(label: Option<&Rc<Label>>, i: usize) -> Vec<u8> {
    let label_storage: Label;
    let label = if let Some(l) = label {
        l
    } else {
        label_storage =  i.to_string().as_bytes().into();
        &label_storage
    };
    let mut res: Vec<u8> = (&label).into_iter()
                                   .map(|i| if i {b'\t'} else {b' '})
                                   .collect();
    res.push(b'\n');
    res
}
<|endoftext|>


<commit_before>use syntax::ast::TokenTree;
use syntax::codemap::Span;
use syntax::ptr::P;

use syntax::ast::Expr;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::parse::parser::Parser;
use syntax::parse::token::Token;

pub fn expand<'cx>(cx: &'cx mut ExtCtxt, _: Span,
               tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let mut parser = cx.new_parser_from_tts(tts);
    let expr = parse_json(cx, &mut parser);
    if &parser.token != &Token::Eof {
        cx.span_fatal(parser.span, "expected end of `json!` macro invocation");
    }
    MacEager::expr(expr)
}

fn parse_json(cx: &ExtCtxt, parser: &mut Parser) -> P<Expr> {
    use syntax::ext::build::AstBuilder;
    use syntax::parse::token::{DelimToken, IdentStyle};

    macro_rules! comma_sep {
        () =>  {
            ::syntax::parse::common::SeqSep {
                sep: Some(Token::Comma),
                trailing_sep_allowed: true // we could be JSON pedants...
            }
        }
    }

    let orig_span = parser.span;

    match &parser.token {
        &Token::OpenDelim(DelimToken::Bracket) => {
            let _ = parser.bump();
            let r_bracket = Token::CloseDelim(DelimToken::Bracket);
            let exprs = parser.parse_seq_to_end(&r_bracket, comma_sep!(), |p| {
                Ok(parse_json(cx, p))
            }).ok().unwrap();
            let exprs = cx.expr_vec(orig_span, exprs);
            quote_expr!(cx, {{
                use ::std::boxed::Box;
                let xs: Box<[_]> = Box::new($exprs);
                ::rustc_serialize::json::Json::Array(xs.into_vec())
            }})
        },
        &Token::OpenDelim(DelimToken::Brace) => {
            let _ = parser.bump();
            let r_brace = Token::CloseDelim(DelimToken::Brace);
            let kvs = parser.parse_seq_to_end(&r_brace, comma_sep!(), |p| {
                let (istr, _) = p.parse_str().ok().unwrap();
                let s = &*istr;
                let _ = p.expect(&Token::Colon);
                let key = quote_expr!(cx, {{
                    use ::std::borrow::ToOwned;
                    $s.to_owned()
                }});
                Ok((key, parse_json(cx, p)))
            }).ok().unwrap();
            let ob = quote_expr!(cx, _ob);
            let mut stmts = vec![];
            for &(ref key, ref value) in kvs.iter() {
                stmts.push(quote_stmt!(cx, $ob.insert($key, $value)));
            }
            quote_expr!(cx, {{
                let mut $ob = ::std::collections::BTreeMap::new();
                $stmts;
                ::rustc_serialize::json::Json::Object($ob)
            }})
        },
        &Token::OpenDelim(DelimToken::Paren) => {
            let expr = parser.parse_expr();
            quote_expr!(cx, {{
                use ::rustc_serialize::json::ToJson;
                ($expr).to_json()
            }})
        },
        &Token::Ident(id, IdentStyle::Plain) if id.as_str() == "null" => {
            let _ = parser.bump();
            quote_expr!(cx, { ::rustc_serialize::json::Json::Null })
        },
        _ => { // TODO: investigate can_begin_expr (maybe eliminate need for parens)?
            let expr = parser.parse_literal_maybe_minus().ok().unwrap();
            quote_expr!(cx, {{
                use ::rustc_serialize::json::ToJson;
                ($expr).to_json()
            }})
        }
    }
}
<commit_msg>Update for interpolation changes upstream<commit_after>use syntax::ast::TokenTree;
use syntax::codemap::Span;
use syntax::ptr::P;

use syntax::ast::Expr;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::parse::parser::Parser;
use syntax::parse::token::Token;

pub fn expand<'cx>(cx: &'cx mut ExtCtxt, _: Span,
               tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let mut parser = cx.new_parser_from_tts(tts);
    let expr = parse_json(cx, &mut parser);
    if &parser.token != &Token::Eof {
        cx.span_fatal(parser.span, "expected end of `json!` macro invocation");
    }
    MacEager::expr(expr)
}

fn parse_json(cx: &ExtCtxt, parser: &mut Parser) -> P<Expr> {
    use syntax::ext::build::AstBuilder;
    use syntax::parse::token::{DelimToken, IdentStyle};

    macro_rules! comma_sep {
        () =>  {
            ::syntax::parse::common::SeqSep {
                sep: Some(Token::Comma),
                trailing_sep_allowed: true // we could be JSON pedants...
            }
        }
    }

    let orig_span = parser.span;

    match &parser.token {
        &Token::OpenDelim(DelimToken::Bracket) => {
            let _ = parser.bump();
            let r_bracket = Token::CloseDelim(DelimToken::Bracket);
            let exprs = parser.parse_seq_to_end(&r_bracket, comma_sep!(), |p| {
                Ok(parse_json(cx, p))
            }).ok().unwrap();
            let exprs = cx.expr_vec(orig_span, exprs);
            quote_expr!(cx, {
                use ::std::boxed::Box;
                let xs: Box<[_]> = Box::new($exprs);
                ::rustc_serialize::json::Json::Array(xs.into_vec())
            })
        },
        &Token::OpenDelim(DelimToken::Brace) => {
            let _ = parser.bump();
            let r_brace = Token::CloseDelim(DelimToken::Brace);
            let kvs = parser.parse_seq_to_end(&r_brace, comma_sep!(), |p| {
                let (istr, _) = p.parse_str().ok().unwrap();
                let s = &*istr;
                let _ = p.expect(&Token::Colon);
                let key = quote_expr!(cx, {
                    use ::std::borrow::ToOwned;
                    $s.to_owned()
                });
                Ok((key, parse_json(cx, p)))
            }).ok().unwrap();
            let mut insertions = vec![];
            // Can't use `quote_stmt!()` and interpolate a vector of
            // statements, seemingly.  Should consider filing a bug
            // upstream.
            for &(ref key, ref value) in kvs.iter() {
                insertions.push(quote_expr!(cx, {
                    _ob.insert($key, $value);
                }));
            }
            let expr = quote_expr!(cx, {
                let mut _ob = ::std::collections::BTreeMap::new();
                $insertions;
                ::rustc_serialize::json::Json::Object(_ob)
            });
            expr
        },
        &Token::OpenDelim(DelimToken::Paren) => {
            let expr = parser.parse_expr();
            quote_expr!(cx, {{
                use ::rustc_serialize::json::ToJson;
                ($expr).to_json()
            }})
        },
        &Token::Ident(id, IdentStyle::Plain) if id.as_str() == "null" => {
            let _ = parser.bump();
            quote_expr!(cx, { ::rustc_serialize::json::Json::Null })
        },
        _ => { // TODO: investigate can_begin_expr (maybe eliminate need for parens)?
            let expr = parser.parse_literal_maybe_minus().ok().unwrap();
            quote_expr!(cx, {{
                use ::rustc_serialize::json::ToJson;
                ($expr).to_json()
            }})
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;

pub use self::rand::Rng;

use super::structure::{Program,Condition,Command,Expression,Sensor};

impl rand::Rand for Program {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 2) {
			i @ 0...1 if i < 1 => Program::If(Condition::rand(rng), Box::new(Program::rand(rng)), Box::new(Program::rand(rng))),
			i @ 1...2 if i < 2 => Program::Command(Command::rand(rng)),
			_ => panic!(),
		}
	}
}

impl rand::Rand for Condition {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 10) {
			i @ 0...1  if i < 1  => Condition::True,
			i @ 1...2  if i < 2  => Condition::False,
			i @ 2...3  if i < 3  => Condition::Not(Box::new(Condition::rand(rng))),
			i @ 3...4  if i < 4  => Condition::Or(Box::new(Condition::rand(rng)), Box::new(Condition::rand(rng))),
			i @ 4...5  if i < 5  => Condition::And(Box::new(Condition::rand(rng)), Box::new(Condition::rand(rng))),
			i @ 5...6  if i < 6  => Condition::Less(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 6...7  if i < 7  => Condition::LessEqual(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 7...8  if i < 8  => Condition::Equal(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 8...9  if i < 9  => Condition::GreaterEqual(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 9...10 if i < 10 => Condition::Greater(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			_ => panic!(),
		}
	}
}

impl rand::Rand for Command {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 4) {
			i @ 0...1 if i < 1 => Command::Skip,
			i @ 1...2 if i < 2 => Command::Left,
			i @ 2...3 if i < 3 => Command::Right,
			i @ 3...4 if i < 4 => Command::Up,
			_ => panic!(),
		}
	}
}

impl rand::Rand for Expression {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 6) {
			i @ 0...1 if i < 1 => Expression::Constant(rng.next_f32()),
			i @ 1...2 if i < 2 => Expression::Sensor(Sensor::rand(rng)),
			i @ 2...3 if i < 3 => Expression::Plus(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 3...4 if i < 4 => Expression::Minus(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 4...5 if i < 5 => Expression::Multiply(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 5...6 if i < 6 => Expression::Divide(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			_ => panic!(),
		}
	}
}

impl rand::Rand for Sensor {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 6) {
			i @ 0...1 if i < 1 => Sensor::X,
			i @ 1...2 if i < 2 => Sensor::Y,
			i @ 2...3 if i < 3 => Sensor::Vx,
			i @ 3...4 if i < 4 => Sensor::Vy,
			i @ 4...5 if i < 5 => Sensor::O,
			i @ 5...6 if i < 6 => Sensor::W,
			_ => panic!(),
		}
	}
}

#[macro_export]
macro_rules! pick {
    ($total: expr, $( $lower: expr, $upper: expr, $expression: expr),+) => {{
        let mut rng = rand::thread_rng();
		    match rng.gen_range(0, $total) {
            $( i @ $lower...$upper if i < $upper => $expression,)+
			      _ => panic!(),
		    }
    }}
}
<commit_msg>Order the macro to appear before usage<commit_after>extern crate rand;

pub use self::rand::Rng;

use super::structure::{Program,Condition,Command,Expression,Sensor};

#[macro_export]
macro_rules! pick {
    ($total: expr, $( $lower: expr, $upper: expr, $expression: expr),+) => {{
        let mut rng = rand::thread_rng();
		    match rng.gen_range(0, $total) {
            $( i @ $lower...$upper if i < $upper => $expression,)+
			      _ => panic!(),
		    }
    }}
}

impl rand::Rand for Program {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 2) {
			i @ 0...1 if i < 1 => Program::If(Condition::rand(rng), Box::new(Program::rand(rng)), Box::new(Program::rand(rng))),
			i @ 1...2 if i < 2 => Program::Command(Command::rand(rng)),
			_ => panic!(),
		}
	}
}

impl rand::Rand for Condition {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 10) {
			i @ 0...1  if i < 1  => Condition::True,
			i @ 1...2  if i < 2  => Condition::False,
			i @ 2...3  if i < 3  => Condition::Not(Box::new(Condition::rand(rng))),
			i @ 3...4  if i < 4  => Condition::Or(Box::new(Condition::rand(rng)), Box::new(Condition::rand(rng))),
			i @ 4...5  if i < 5  => Condition::And(Box::new(Condition::rand(rng)), Box::new(Condition::rand(rng))),
			i @ 5...6  if i < 6  => Condition::Less(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 6...7  if i < 7  => Condition::LessEqual(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 7...8  if i < 8  => Condition::Equal(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 8...9  if i < 9  => Condition::GreaterEqual(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 9...10 if i < 10 => Condition::Greater(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			_ => panic!(),
		}
	}
}

impl rand::Rand for Command {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 4) {
			i @ 0...1 if i < 1 => Command::Skip,
			i @ 1...2 if i < 2 => Command::Left,
			i @ 2...3 if i < 3 => Command::Right,
			i @ 3...4 if i < 4 => Command::Up,
			_ => panic!(),
		}
	}
}

impl rand::Rand for Expression {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 6) {
			i @ 0...1 if i < 1 => Expression::Constant(rng.next_f32()),
			i @ 1...2 if i < 2 => Expression::Sensor(Sensor::rand(rng)),
			i @ 2...3 if i < 3 => Expression::Plus(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 3...4 if i < 4 => Expression::Minus(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 4...5 if i < 5 => Expression::Multiply(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			i @ 5...6 if i < 6 => Expression::Divide(Box::new(Expression::rand(rng)), Box::new(Expression::rand(rng))),
			_ => panic!(),
		}
	}
}

impl rand::Rand for Sensor {
	fn rand<R: rand::Rng>(rng: &mut R) -> Self {
		match rng.gen_range(0, 6) {
			i @ 0...1 if i < 1 => Sensor::X,
			i @ 1...2 if i < 2 => Sensor::Y,
			i @ 2...3 if i < 3 => Sensor::Vx,
			i @ 3...4 if i < 4 => Sensor::Vy,
			i @ 4...5 if i < 5 => Sensor::O,
			i @ 5...6 if i < 6 => Sensor::W,
			_ => panic!(),
		}
	}
}
<|endoftext|>