<commit_before>use std::rc::Rc;
use std::ops::Deref;

use expr::{Expr,Prim};
use env::Env;
use read::read;

fn f_foldl<F:Fn(&mut Env, Expr, &Expr) -> Expr>(mut env: &mut Env, f: &F, init: Expr, args: &Expr) -> Expr {
    let mut res = init;
    let mut head = args;
    let nil = &Expr::Nil;
    while head != nil {
        match head {
            &Expr::Cons(ref car, ref cdr) => {
                res = f(env, res, car);
                head = cdr;
            }
            _ => panic!("invalid argument {:?} to function", args.clone())
        }
    }
    res
}

fn f_reverse(mut env: &mut Env, args: &Expr) -> Expr {
    f_foldl(env, &|_, acc, x| Expr::Cons(Rc::new(x.clone()), Rc::new(acc)), Expr::Nil, args)
}

fn f_foldr<F:Fn(&mut Env, Expr, &Expr) -> Expr>(mut env: &mut Env, f: &F, init: Expr, args: &Expr) -> Expr {
    match args {
        &Expr::Nil => init,
        &Expr::Cons(ref car, ref cdr) => {
            let v = f_foldr(env, f, init, cdr);
            f(env, v, car)
        }
        _ => panic!("invalid argument {:?} to function", args)
    }
}

fn f_map<F:Fn(&mut Env, Expr) -> Expr>(mut env: &mut Env, f: &F, list: &Expr) -> Expr {
    f_foldr(env, &|env, acc, x| Expr::cons(f(env, x.clone()), acc)
                 , Expr::Nil, list)
}

// since rust's macro cannot treat binop, work around macro is needed.
macro_rules! expr {
    ($e:expr) => {
        $e
    }
}

macro_rules! def_arith_op {
    ($name: ident, $op: tt, $init: expr) => {
        fn $name(mut env: &mut Env, args: Expr) -> Expr {
            f_foldl(env, &|_, x, y| match (x, y) {
                (Expr::Int(x), &Expr::Int(y)) => Expr::Int(expr!(x $op y)),
                (x, y) => panic!("non int args {:?} and {:?} are given to $op", x, y)
            }, $init, &args)
        }
    }
}

def_arith_op!(k_add, +, Expr::Int(0));
def_arith_op!(k_sub, -, Expr::Int(0));
def_arith_op!(k_mul, *, Expr::Int(1));
def_arith_op!(k_div, /, Expr::Int(1));

fn funcall(mut env: &mut Env, f: &Expr, args: Expr) -> Expr {
    match f {
        &Expr::FLambda(ref prim) => match prim {
            &Prim::Add => k_add(env, args),
            &Prim::Sub => k_sub(env, args),
            &Prim::Mul => k_mul(env, args),
            &Prim::Div => k_div(env, args),
            _ => panic!("unknown function ")
        },
        &Expr::Lambda(ref params, ref body) => panic!("not implemented"),
        _ => panic!("not a function ")
    }
}

fn feval(mut env: &mut Env, expr: &Expr) -> Expr {
    match expr {
        &Expr::Sym(ref sym) => match &sym[..] {
            "+" => Expr::FLambda(Prim::Add),
            "-" => Expr::FLambda(Prim::Sub),
            "*" => Expr::FLambda(Prim::Mul),
            "/" => Expr::FLambda(Prim::Div),
            "concat" => Expr::FLambda(Prim::Concat),
            fun => panic!("function {:?} not found", fun)
        },
        &Expr::Lambda(_, _) => expr.clone(),
        x => panic!("{:?} is not a function", x)
    }
}

fn eval(mut env: &mut Env, expr: Expr) -> Expr {
    match expr {
        Expr::Nil |
        Expr::EOF |
        Expr::Str(_) |
        Expr::Int(_) |
        Expr::Lambda(_, _) |
        Expr::FLambda(_) => expr,
        Expr::Sym(_) => panic!("symbol evaluation is not supported"),
        Expr::Cons(car, cdr) => {
            let f = feval(env, car.deref());
            let arg = f_map(env, &|env, x| eval(env, x), cdr.deref());
            funcall(env, &f, arg)
        }
    }
}

#[test]
fn test_atom(){
    assert!(eval(&mut Env::new(), read("1")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("()")) == (Expr::Nil));
    assert!(eval(&mut Env::new(), read("")) == (Expr::EOF));
    assert!(eval(&mut Env::new(), read("\"string\"")) == (Expr::Str("string".to_string())));
}

#[test]
fn test_add(){
    assert!(eval(&mut Env::new(), read("(+)")) == (Expr::Int(0)));
    assert!(eval(&mut Env::new(), read("(+ 1)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(+ 1 2)")) == (Expr::Int(3)));
    assert!(eval(&mut Env::new(), read("(+ 1 2 3)")) == (Expr::Int(6)));
}

#[test]
fn test_sub(){
    assert!(eval(&mut Env::new(), read("(-)")) == (Expr::Int(0)));
    assert!(eval(&mut Env::new(), read("(- 1)")) == (Expr::Int(-1)));
    assert!(eval(&mut Env::new(), read("(- 1 2)")) == (Expr::Int(-1)));
    assert!(eval(&mut Env::new(), read("(- 1 2 3)")) == (Expr::Int(-4)));
}

#[test]
fn test_mul(){
    assert!(eval(&mut Env::new(), read("(*)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(* 1)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(* 1 2)")) == (Expr::Int(2)));
    assert!(eval(&mut Env::new(), read("(* 1 2 3)")) == (Expr::Int(6)));
}

#[test]
fn test_div(){
    assert!(eval(&mut Env::new(), read("(/)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(/ 1)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(/ 3 2)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(/ 3 2 1)")) == (Expr::Int(1)));
}

<commit_msg>pass tests<commit_after>use std::rc::Rc;
use std::ops::Deref;

use expr::{Expr,Prim};
use env::Env;
use read::read;

fn f_foldl<F:Fn(&mut Env, Expr, &Expr) -> Expr>(mut env: &mut Env, f: &F, init: Expr, args: &Expr) -> Expr {
    let mut res = init;
    let mut head = args;
    let nil = &Expr::Nil;
    while head != nil {
        match head {
            &Expr::Cons(ref car, ref cdr) => {
                res = f(env, res, car);
                head = cdr;
            }
            _ => panic!("invalid argument {:?} to function", args.clone())
        }
    }
    res
}

fn f_reverse(mut env: &mut Env, args: &Expr) -> Expr {
    f_foldl(env, &|_, acc, x| Expr::Cons(Rc::new(x.clone()), Rc::new(acc)), Expr::Nil, args)
}

fn f_foldr<F:Fn(&mut Env, Expr, &Expr) -> Expr>(mut env: &mut Env, f: &F, init: Expr, args: &Expr) -> Expr {
    match args {
        &Expr::Nil => init,
        &Expr::Cons(ref car, ref cdr) => {
            let v = f_foldr(env, f, init, cdr);
            f(env, v, car)
        }
        _ => panic!("invalid argument {:?} to function", args)
    }
}

fn f_map<F:Fn(&mut Env, Expr) -> Expr>(mut env: &mut Env, f: &F, list: &Expr) -> Expr {
    f_foldr(env, &|env, acc, x| Expr::cons(f(env, x.clone()), acc)
                 , Expr::Nil, list)
}

// since rust's macro cannot treat binop, work around macro is needed.
macro_rules! expr {
    ($e:expr) => {
        $e
    }
}

macro_rules! def_arith_op {
    ($name: ident, $op: tt, $init: expr) => {
        fn $name(mut env: &mut Env, args: Expr) -> Expr {
            let (init, args) = match args {
                Expr::Cons(ref hd, ref tl) => match tl.deref() {
                    tl @ &Expr::Cons(_, _) => (hd.deref().clone(), tl.clone()),
                    tl => ($init, Expr::cons(hd.deref().clone(), tl.clone()))
                },
                args => ($init, args)
            };
            f_foldl(env, &|_, x, y| match (x, y) {
                (Expr::Int(x), &Expr::Int(y)) => Expr::Int(expr!(x $op y)),
                (x, y) => panic!("non int args {:?} and {:?} are given to $op", x, y)
            }, init, &args)

        }
    }
}

def_arith_op!(k_add, +, Expr::Int(0));
def_arith_op!(k_sub, -, Expr::Int(0));
def_arith_op!(k_mul, *, Expr::Int(1));
def_arith_op!(k_div, /, Expr::Int(1));

fn funcall(mut env: &mut Env, f: &Expr, args: Expr) -> Expr {
    match f {
        &Expr::FLambda(ref prim) => match prim {
            &Prim::Add => k_add(env, args),
            &Prim::Sub => k_sub(env, args),
            &Prim::Mul => k_mul(env, args),
            &Prim::Div => k_div(env, args),
            _ => panic!("unknown function ")
        },
        &Expr::Lambda(ref params, ref body) => panic!("not implemented"),
        _ => panic!("not a function ")
    }
}

fn feval(mut env: &mut Env, expr: Expr) -> Expr {
    match expr {
        Expr::Sym(ref sym) => match &sym[..] {
            "+" => Expr::FLambda(Prim::Add),
            "-" => Expr::FLambda(Prim::Sub),
            "*" => Expr::FLambda(Prim::Mul),
            "/" => Expr::FLambda(Prim::Div),
            "concat" => Expr::FLambda(Prim::Concat),
            fun => panic!("function {:?} not found", fun)
        },
        Expr::Lambda(_, _) => expr,
        x => panic!("{:?} is not a function", x)
    }
}

fn eval(mut env: &mut Env, expr: Expr) -> Expr {
    match expr {
        Expr::Nil |
        Expr::EOF |
        Expr::Str(_) |
        Expr::Int(_) |
        Expr::Lambda(_, _) |
        Expr::FLambda(_) => expr,
        Expr::Sym(_) => panic!("symbol evaluation is not supported"),
        Expr::Cons(car, cdr) => {
            let f = feval(env, car.deref().clone());
            let arg = f_map(env, &|env, x| eval(env, x), cdr.deref());
            funcall(env, &f, arg)
        }
    }
}

#[test]
fn test_atom(){
    assert!(eval(&mut Env::new(), read("1")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("()")) == (Expr::Nil));
    assert!(eval(&mut Env::new(), read("")) == (Expr::EOF));
    assert!(eval(&mut Env::new(), read("\"string\"")) == (Expr::Str("string".to_string())));
}

#[test]
fn test_add(){
    assert!(eval(&mut Env::new(), read("(+)")) == (Expr::Int(0)));
    assert!(eval(&mut Env::new(), read("(+ 1)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(+ 1 2)")) == (Expr::Int(3)));
    assert!(eval(&mut Env::new(), read("(+ 1 2 3)")) == (Expr::Int(6)));
}

#[test]
fn test_sub(){
    assert!(eval(&mut Env::new(), read("(-)")) == (Expr::Int(0)));
    assert!(eval(&mut Env::new(), read("(- 1)")) == (Expr::Int(-1)));
    assert!(eval(&mut Env::new(), read("(- 1 2)")) == (Expr::Int(-1)));
    assert!(eval(&mut Env::new(), read("(- 1 2 3)")) == (Expr::Int(-4)));
}

#[test]
fn test_mul(){
    assert!(eval(&mut Env::new(), read("(*)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(* 1)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(* 1 2)")) == (Expr::Int(2)));
    assert!(eval(&mut Env::new(), read("(* 1 2 3)")) == (Expr::Int(6)));
}

#[test]
fn test_div(){
    assert!(eval(&mut Env::new(), read("(/)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(/ 1)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(/ 3 2)")) == (Expr::Int(1)));
    assert!(eval(&mut Env::new(), read("(/ 3 2 1)")) == (Expr::Int(1)));
}

#[test]
fn test_nested_arith() {
    assert!(eval(&mut Env::new(), read("(/ (- (+ 1 (* 2 3)) 3) 2)")) == (Expr::Int(2)));
}
<|endoftext|>


<commit_before>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, TyCtxt};
use std::borrow::Cow;
use syntax_pos::Span;

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

pub fn is_min_const_fn(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    def_id: DefId,
    mir: &'a Mir<'tcx>,
) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in &predicates.predicates {
            match predicate {
                | Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::ConstEvaluatable(..) => continue,
                | Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Projection(_) => {
                    let span = tcx.def_span(current);
                    // we'll hit a `Predicate::Trait` later which will report an error
                    tcx.sess
                        .delay_span_bug(span, "projection without trait bound");
                    continue;
                }
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().sty {
                        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 mir.vars_iter() {
        return Err((
            mir.local_decls[local].source_info.span,
            "local variables in const fn are unstable".into(),
        ));
    }
    for local in &mir.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(),
        mir.local_decls.iter().next().unwrap().source_info.span,
    )?;

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

fn check_ty(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    ty: ty::Ty<'tcx>,
    span: Span,
) -> McfResult {
    for ty in ty.walk() {
        match ty.sty {
            ty::Ref(_, _, hir::Mutability::MutMutable) => 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<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, mir, operand, span)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(tcx, mir, place, span, PlaceMode::Read)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(mir, 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::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err((
                    span,
                    "casting pointers to ints is unstable in const fn".into(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(tcx, mir, operand, span),
            }
        }
        Rvalue::Cast(_, _, _) => Err((
            span,
            "only int casts are allowed in const fn".into(),
        )),
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, mir, lhs, span)?;
            check_operand(tcx, mir, rhs, span)?;
            let ty = lhs.ty(mir, 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(mir, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, mir, operand, span)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, mir, operand, span)?;
            }
            Ok(())
        }
    }
}

enum PlaceMode {
    Assign,
    Read,
}

fn check_statement(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(place, rval) => {
            check_place(tcx, mir, place, span, PlaceMode::Assign)?;
            check_rvalue(tcx, mir, rval, span)
        }

        StatementKind::FakeRead(..) => Err((span, "match in const fn is unstable".into())),

        // just an assignment
        StatementKind::SetDiscriminant { .. } => Ok(()),

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

        // These are all NOPs
        | StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Validate(..)
        | StatementKind::EndRegion(_)
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(tcx, mir, place, span, PlaceMode::Read)
        }
        Operand::Constant(_) => Ok(()),
    }
}

fn check_place(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    place: &Place<'tcx>,
    span: Span,
    mode: PlaceMode,
) -> McfResult {
    match place {
        Place::Local(l) => match mode {
            PlaceMode::Assign => match mir.local_kind(*l) {
                LocalKind::Temp | LocalKind::ReturnPointer => Ok(()),
                LocalKind::Arg | LocalKind::Var => {
                    Err((span, "assignments in const fn are unstable".into()))
                }
            },
            PlaceMode::Read => Ok(()),
        },
        // promoteds are always fine, they are essentially constants
        Place::Promoted(_) => Ok(()),
        Place::Static(_) => Err((span, "cannot access `static` items in const fn".into())),
        Place::Projection(proj) => {
            match proj.elem {
                | ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
                    check_place(tcx, mir, &proj.base, span, mode)
                }
                // slice patterns are unstable
                | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
                    return Err((span, "slice patterns in const fn are unstable".into()))
                }
                | ProjectionElem::Downcast(..) => {
                    Err((span, "`match` or `if let` in `const fn` is unstable".into()))
                }
            }
        }
    }
}

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

        TerminatorKind::Drop { location, .. } => {
            check_place(tcx, mir, location, span, PlaceMode::Read)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, mir, location, span, PlaceMode::Read)?;
            check_operand(tcx, mir, value, span)
        },
        TerminatorKind::SwitchInt { .. } => Err((
            span,
            "`if`, `match`, `&&` and `||` are not stable in const fn".into(),
        )),
        | TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
        } => {
            let fn_ty = func.ty(mir, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.sty {
                if tcx.is_min_const_fn(def_id) {
                    check_operand(tcx, mir, func, span)?;

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

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

        | TerminatorKind::FalseEdges { .. } | TerminatorKind::FalseUnwind { .. } => span_bug!(
            terminator.source_info.span,
            "min_const_fn encountered `{:#?}`",
            terminator
        ),
    }
}
<commit_msg>Explain all casts in detail<commit_after>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, TyCtxt};
use std::borrow::Cow;
use syntax_pos::Span;

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

pub fn is_min_const_fn(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    def_id: DefId,
    mir: &'a Mir<'tcx>,
) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in &predicates.predicates {
            match predicate {
                | Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::ConstEvaluatable(..) => continue,
                | Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Projection(_) => {
                    let span = tcx.def_span(current);
                    // we'll hit a `Predicate::Trait` later which will report an error
                    tcx.sess
                        .delay_span_bug(span, "projection without trait bound");
                    continue;
                }
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().sty {
                        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 mir.vars_iter() {
        return Err((
            mir.local_decls[local].source_info.span,
            "local variables in const fn are unstable".into(),
        ));
    }
    for local in &mir.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(),
        mir.local_decls.iter().next().unwrap().source_info.span,
    )?;

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

fn check_ty(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    ty: ty::Ty<'tcx>,
    span: Span,
) -> McfResult {
    for ty in ty.walk() {
        match ty.sty {
            ty::Ref(_, _, hir::Mutability::MutMutable) => 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<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, mir, operand, span)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(tcx, mir, place, span, PlaceMode::Read)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(mir, 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::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err((
                    span,
                    "casting pointers to ints is unstable in const fn".into(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(tcx, mir, operand, span),
            }
        }
        Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
        Rvalue::Cast(CastKind::ReifyFnPointer, _, _) => Err((
            span,
            "function pointer casts are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::ClosureFnPointer, _, _) => Err((
            span,
            "closures are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::Unsize, _, _) => Err((
            span,
            "unsizing casts are not allowed in const fn".into(),
        )),
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, mir, lhs, span)?;
            check_operand(tcx, mir, rhs, span)?;
            let ty = lhs.ty(mir, 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(mir, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, mir, operand, span)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, mir, operand, span)?;
            }
            Ok(())
        }
    }
}

enum PlaceMode {
    Assign,
    Read,
}

fn check_statement(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(place, rval) => {
            check_place(tcx, mir, place, span, PlaceMode::Assign)?;
            check_rvalue(tcx, mir, rval, span)
        }

        StatementKind::FakeRead(..) => Err((span, "match in const fn is unstable".into())),

        // just an assignment
        StatementKind::SetDiscriminant { .. } => Ok(()),

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

        // These are all NOPs
        | StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Validate(..)
        | StatementKind::EndRegion(_)
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(tcx, mir, place, span, PlaceMode::Read)
        }
        Operand::Constant(_) => Ok(()),
    }
}

fn check_place(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    place: &Place<'tcx>,
    span: Span,
    mode: PlaceMode,
) -> McfResult {
    match place {
        Place::Local(l) => match mode {
            PlaceMode::Assign => match mir.local_kind(*l) {
                LocalKind::Temp | LocalKind::ReturnPointer => Ok(()),
                LocalKind::Arg | LocalKind::Var => {
                    Err((span, "assignments in const fn are unstable".into()))
                }
            },
            PlaceMode::Read => Ok(()),
        },
        // promoteds are always fine, they are essentially constants
        Place::Promoted(_) => Ok(()),
        Place::Static(_) => Err((span, "cannot access `static` items in const fn".into())),
        Place::Projection(proj) => {
            match proj.elem {
                | ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
                    check_place(tcx, mir, &proj.base, span, mode)
                }
                // slice patterns are unstable
                | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
                    return Err((span, "slice patterns in const fn are unstable".into()))
                }
                | ProjectionElem::Downcast(..) => {
                    Err((span, "`match` or `if let` in `const fn` is unstable".into()))
                }
            }
        }
    }
}

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

        TerminatorKind::Drop { location, .. } => {
            check_place(tcx, mir, location, span, PlaceMode::Read)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, mir, location, span, PlaceMode::Read)?;
            check_operand(tcx, mir, value, span)
        },
        TerminatorKind::SwitchInt { .. } => Err((
            span,
            "`if`, `match`, `&&` and `||` are not stable in const fn".into(),
        )),
        | TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
        } => {
            let fn_ty = func.ty(mir, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.sty {
                if tcx.is_min_const_fn(def_id) {
                    check_operand(tcx, mir, func, span)?;

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

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

        | TerminatorKind::FalseEdges { .. } | TerminatorKind::FalseUnwind { .. } => span_bug!(
            terminator.source_info.span,
            "min_const_fn encountered `{:#?}`",
            terminator
        ),
    }
}
<|endoftext|>


<commit_before>use parser::parser::{parse};
use ast::Expr::*;
use ast::UnOp::*;
use ast::BinOp::*;
use ast::Dec::*;
use ast::*;
use state::*;
use runtime_error::RuntimeError;
use std::result;

pub type Result<T> = result::Result<T, RuntimeError>;

macro_rules! step {
  ($s:expr, $e:expr) => (try!($s.step($e)));
}

macro_rules! to_int {
  ($e:expr) => (try!($e.to_int()));
}

macro_rules! to_var {
  ($e:expr) => (try!($e.to_var()));
}

macro_rules! to_bool {
  ($e:expr) => (try!($e.to_bool()));
}

pub struct Repl {
  pub state: State,
}

impl Repl {
  pub fn new() -> Repl {
    Repl {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e.clone() {
      Var(x) => {
        match self.state.get(x.clone()) {
          Some(e) => e,
          None => return Err(RuntimeError::VariableNotFound(x)),
        }
      },
      /**
       * Values are ineligible for step
       */
      Int(_) | Bool(_) | Func(_, _, _) | Undefined => {
        debug!("stepping on a value {:?}", e);
        return Err(RuntimeError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, ref e1) if e1.is_value() => {
        Bool(!to_bool!(e1))
      },
      Uop(Neg, ref e1) if e1.is_value() => {
        Int(-1 * to_int!(e1))
      },
      Bop(And, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_bool!(e1) && to_bool!(e2))
      },
      Bop(Or, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_bool!(e1) || to_bool!(e2))
      },
      Bop(Eq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 == *e2)
      },
      Bop(Ne, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 != *e2)
      },
      Bop(Mod, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        let n1 = to_int!(e1);
        let n2 = to_int!(e2);

        // rust % gives the remainder, not modulus
        let result = ((n1 % n2) + n2) % n2;

        Int(result)
      },
      Bop(Lt, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_int!(e1) < to_int!(e2))
      },
      Bop(Gt, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_int!(e1) > to_int!(e2))
      },
      Bop(Leq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_int!(e1) <= to_int!(e2))
      },
      Bop(Geq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_int!(e1) >= to_int!(e2))
      },
      Bop(Plus, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(to_int!(e1) + to_int!(e2))
      },
      Bop(Minus, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(to_int!(e1) - to_int!(e2))
      },
      Bop(Times, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(to_int!(e1) * to_int!(e2))
      },
      Bop(Div, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(to_int!(e1) / to_int!(e2))
      },
      Bop(Seq, ref v1, ref e2) if v1.is_value() => {
        *e2.clone()
      },
      Bop(Assign, ref v1, ref v2) if v1.is_var() && v2.is_value() => {
        let x = to_var!(v1);
        try!(self.state.assign(x, *v2.clone()));
        debug!("done assigning {:?}", self.state.mem);
        *v2.clone()
      },
      Ternary(ref v1, ref e2, ref e3) if v1.is_value() => {
        match to_bool!(v1) {
          true => *e2.clone(),
          false => *e3.clone(),
        }
      },
      Decl(DConst, ref x, ref v1, ref e2) if v1.is_value() => {
        try!(self.state.alloc_const(to_var!(x), *v1.clone()));
        *e2.clone()
      },
      Decl(DVar, ref x, ref v1, ref e2) if x.is_var() && v1.is_value() => {
        debug!("allocing {:?}", v1);
        try!(self.state.alloc(to_var!(x), *v1.clone()));
        *e2.clone()
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        match **v1 {
          Func(ref name, ref e1, ref xs) => {
            self.state.begin_scope();
            // sub the params
            let exp_result: Result<Expr> = xs.iter().zip(es.iter())
              .fold(Ok(*e1.clone()), |exp, (xn, en)| {
                try!(self.state.alloc(to_var!(xn), en.clone()));
                exp
              });

            let exp = try!(exp_result);

            // sub the fn body for named functions
            let body = match *name {
              None => exp,
              Some(ref s) => {
                try!(self.state.alloc(to_var!(s), *v1.clone()));
                exp
              }
            };
            Scope(Box::new(body))
          },
          _ => return Err(RuntimeError::UnexpectedExpr("expected Func".to_string(), *v1.clone()))
        }
      },
      Scope(ref v1) if v1.is_value() => {
        self.state.end_scope();
        *v1.clone()
      },
      /**
       * Search Cases
       */
      Bop(ref op, ref v1, ref e2) if v1.is_value() => {
        Bop(
          op.clone(),
          Box::new(*v1.clone()),
          Box::new(step!(self, *e2.clone()))
        )
      },
      Bop(Assign, ref v1, ref e2) if v1.is_var() => {
        Bop(
          Assign,
          Box::new(*v1.clone()),
          Box::new(step!(self, *e2.clone()))
        )
      },
      Bop(op, e1, e2) => {
        Bop(op, Box::new(step!(self, *e1)), e2)
      },
      Uop(op, e1) => {
        Uop(op, Box::new(step!(self, *e1)))
      },
      Ternary(e1, e2, e3) => {
        Ternary(Box::new(step!(self, *e1)), e2, e3)
      },
      While(ref v1, ref e1o, _, ref e2o, ref e3) if v1.is_value() => {
        match to_bool!(v1) {
          true => While(Box::new(*e1o.clone()), e1o.clone(), e2o.clone(), e2o.clone(), e3.clone()),
          false => *e3.clone(),
        }
      },
      While(ref e1, ref e1o, ref v2, ref e2o, ref e3) if v2.is_value() => {
        While(Box::new(step!(self, *e1.clone())), e1o.clone(), v2.clone(), e2o.clone(), e3.clone())
      },
      While(e1, e1o, e2, e2o, e3) => {
        While(e1, e1o, Box::new(step!(self, *e2)), e2o, e3)
      },
      Decl(dt, addr, e1, e2) => {
        Decl(dt, Box::new(*addr.clone()), Box::new(step!(self, *e1)), e2)
      },
      FnCall(ref v1, ref args) if v1.is_value() => {
        let mut found_nonvalue = false;

        let stepped_args: Result<Vec<Expr>> = args.iter().map(|e| {
          if !found_nonvalue && !e.is_value() {
            found_nonvalue = true;
            self.step(e.clone())
          } else {
            Ok(e.clone())
          }
        }).collect();

        match stepped_args {
          Ok(args2) => FnCall(v1.clone(), args2),
          Err(e) => return Err(e)
        }
      },
      FnCall(e1, args) => {
        FnCall(Box::new(step!(self, *e1)), args)
      },
      Scope(e1) => {
        Scope(Box::new(step!(self, *e1)))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Result<Expr> {
    let mut e = try!(parse(input));

    let mut num_iterations = 0;

    loop {
      if num_iterations > 1000 {
        return Err(RuntimeError::TooManyIterations(num_iterations))
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return Ok(e.clone());
      } else {
        e = try!(self.step(e.clone()));
      }
    }
  }
}

pub fn boxx(input: &str) -> Result<Expr> {
  let mut repl = Repl::new();
  repl.eval(input)
}
<commit_msg>more iterations<commit_after>use parser::parser::{parse};
use ast::Expr::*;
use ast::UnOp::*;
use ast::BinOp::*;
use ast::Dec::*;
use ast::*;
use state::*;
use runtime_error::RuntimeError;
use std::result;

pub type Result<T> = result::Result<T, RuntimeError>;

macro_rules! step {
  ($s:expr, $e:expr) => (try!($s.step($e)));
}

macro_rules! to_int {
  ($e:expr) => (try!($e.to_int()));
}

macro_rules! to_var {
  ($e:expr) => (try!($e.to_var()));
}

macro_rules! to_bool {
  ($e:expr) => (try!($e.to_bool()));
}

pub struct Repl {
  pub state: State,
}

impl Repl {
  pub fn new() -> Repl {
    Repl {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e.clone() {
      Var(x) => {
        match self.state.get(x.clone()) {
          Some(e) => e,
          None => return Err(RuntimeError::VariableNotFound(x)),
        }
      },
      /**
       * Values are ineligible for step
       */
      Int(_) | Bool(_) | Func(_, _, _) | Undefined => {
        debug!("stepping on a value {:?}", e);
        return Err(RuntimeError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, ref e1) if e1.is_value() => {
        Bool(!to_bool!(e1))
      },
      Uop(Neg, ref e1) if e1.is_value() => {
        Int(-1 * to_int!(e1))
      },
      Bop(And, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_bool!(e1) && to_bool!(e2))
      },
      Bop(Or, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_bool!(e1) || to_bool!(e2))
      },
      Bop(Eq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 == *e2)
      },
      Bop(Ne, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 != *e2)
      },
      Bop(Mod, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        let n1 = to_int!(e1);
        let n2 = to_int!(e2);

        // rust % gives the remainder, not modulus
        let result = ((n1 % n2) + n2) % n2;

        Int(result)
      },
      Bop(Lt, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_int!(e1) < to_int!(e2))
      },
      Bop(Gt, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_int!(e1) > to_int!(e2))
      },
      Bop(Leq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_int!(e1) <= to_int!(e2))
      },
      Bop(Geq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(to_int!(e1) >= to_int!(e2))
      },
      Bop(Plus, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(to_int!(e1) + to_int!(e2))
      },
      Bop(Minus, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(to_int!(e1) - to_int!(e2))
      },
      Bop(Times, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(to_int!(e1) * to_int!(e2))
      },
      Bop(Div, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(to_int!(e1) / to_int!(e2))
      },
      Bop(Seq, ref v1, ref e2) if v1.is_value() => {
        *e2.clone()
      },
      Bop(Assign, ref v1, ref v2) if v1.is_var() && v2.is_value() => {
        let x = to_var!(v1);
        try!(self.state.assign(x, *v2.clone()));
        debug!("done assigning {:?}", self.state.mem);
        *v2.clone()
      },
      Ternary(ref v1, ref e2, ref e3) if v1.is_value() => {
        match to_bool!(v1) {
          true => *e2.clone(),
          false => *e3.clone(),
        }
      },
      Decl(DConst, ref x, ref v1, ref e2) if v1.is_value() => {
        try!(self.state.alloc_const(to_var!(x), *v1.clone()));
        *e2.clone()
      },
      Decl(DVar, ref x, ref v1, ref e2) if x.is_var() && v1.is_value() => {
        debug!("allocing {:?}", v1);
        try!(self.state.alloc(to_var!(x), *v1.clone()));
        *e2.clone()
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        match **v1 {
          Func(ref name, ref e1, ref xs) => {
            self.state.begin_scope();
            // sub the params
            let exp_result: Result<Expr> = xs.iter().zip(es.iter())
              .fold(Ok(*e1.clone()), |exp, (xn, en)| {
                try!(self.state.alloc(to_var!(xn), en.clone()));
                exp
              });

            let exp = try!(exp_result);

            // sub the fn body for named functions
            let body = match *name {
              None => exp,
              Some(ref s) => {
                try!(self.state.alloc(to_var!(s), *v1.clone()));
                exp
              }
            };
            Scope(Box::new(body))
          },
          _ => return Err(RuntimeError::UnexpectedExpr("expected Func".to_string(), *v1.clone()))
        }
      },
      Scope(ref v1) if v1.is_value() => {
        self.state.end_scope();
        *v1.clone()
      },
      /**
       * Search Cases
       */
      Bop(ref op, ref v1, ref e2) if v1.is_value() => {
        Bop(
          op.clone(),
          Box::new(*v1.clone()),
          Box::new(step!(self, *e2.clone()))
        )
      },
      Bop(Assign, ref v1, ref e2) if v1.is_var() => {
        Bop(
          Assign,
          Box::new(*v1.clone()),
          Box::new(step!(self, *e2.clone()))
        )
      },
      Bop(op, e1, e2) => {
        Bop(op, Box::new(step!(self, *e1)), e2)
      },
      Uop(op, e1) => {
        Uop(op, Box::new(step!(self, *e1)))
      },
      Ternary(e1, e2, e3) => {
        Ternary(Box::new(step!(self, *e1)), e2, e3)
      },
      While(ref v1, ref e1o, _, ref e2o, ref e3) if v1.is_value() => {
        match to_bool!(v1) {
          true => While(Box::new(*e1o.clone()), e1o.clone(), e2o.clone(), e2o.clone(), e3.clone()),
          false => *e3.clone(),
        }
      },
      While(ref e1, ref e1o, ref v2, ref e2o, ref e3) if v2.is_value() => {
        While(Box::new(step!(self, *e1.clone())), e1o.clone(), v2.clone(), e2o.clone(), e3.clone())
      },
      While(e1, e1o, e2, e2o, e3) => {
        While(e1, e1o, Box::new(step!(self, *e2)), e2o, e3)
      },
      Decl(dt, addr, e1, e2) => {
        Decl(dt, Box::new(*addr.clone()), Box::new(step!(self, *e1)), e2)
      },
      FnCall(ref v1, ref args) if v1.is_value() => {
        let mut found_nonvalue = false;

        let stepped_args: Result<Vec<Expr>> = args.iter().map(|e| {
          if !found_nonvalue && !e.is_value() {
            found_nonvalue = true;
            self.step(e.clone())
          } else {
            Ok(e.clone())
          }
        }).collect();

        match stepped_args {
          Ok(args2) => FnCall(v1.clone(), args2),
          Err(e) => return Err(e)
        }
      },
      FnCall(e1, args) => {
        FnCall(Box::new(step!(self, *e1)), args)
      },
      Scope(e1) => {
        Scope(Box::new(step!(self, *e1)))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Result<Expr> {
    let mut e = try!(parse(input));

    let mut num_iterations = 0;

    loop {
      if num_iterations > 100000 {
        return Err(RuntimeError::TooManyIterations(num_iterations))
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return Ok(e.clone());
      } else {
        e = try!(self.step(e.clone()));
      }
    }
  }
}

pub fn boxx(input: &str) -> Result<Expr> {
  let mut repl = Repl::new();
  repl.eval(input)
}
<|endoftext|>


<commit_before>use parser::parser::{parse};
use ast::Expr::*;
use ast::UnOp::*;
use ast::BinOp::*;
use ast::Dec::*;
use ast::*;
use state::*;
use interpreter_error::InterpreterError;
use std::result;

pub type Result<T> = result::Result<T, InterpreterError>;

macro_rules! step {
  ($s:expr, $e:expr) => (try!($s.step($e)));
}

pub struct Repl {
  pub state: State,
}

impl Repl {
  pub fn new() -> Repl {
    Repl {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e.clone() {
      Var(x) => {
        self.state.get(x)
      },
      /**
       * Values are ineligible for step
       */
      Int(_) | Bool(_) | Func(_, _, _) | Undefined => {
        debug!("stepping on a value {:?}", e);
        return Err(InterpreterError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, ref e1) if e1.is_bool() => {
        Bool(!e1.to_bool())
      },
      Uop(Neg, ref e1) if e1.is_int() => {
        Int(-1 * e1.to_int())
      },
      Bop(And, ref e1, ref e2) if e1.is_bool() && e2.is_bool() => {
        Bool(e1.to_bool() && e2.to_bool())
      },
      Bop(Or, ref e1, ref e2) if e1.is_bool() && e2.is_bool() => {
        Bool(e1.to_bool() || e2.to_bool())
      },
      Bop(Eq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 == *e2)
      },
      Bop(Ne, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 != *e2)
      },
      Bop(Mod, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        let n1 = e1.to_int();
        let n2 = e2.to_int();

        // rust % gives the remainder, not modulus
        let result = ((n1 % n2) + n2) % n2;

        Int(result)
      },
      Bop(Lt, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Bool(e1.to_int() < e2.to_int())
      },
      Bop(Gt, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Bool(e1.to_int() > e2.to_int())
      },
      Bop(Leq, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Bool(e1.to_int() <= e2.to_int())
      },
      Bop(Geq, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Bool(e1.to_int() >= e2.to_int())
      },
      Bop(Plus, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Int(e1.to_int() + e2.to_int())
      },
      Bop(Minus, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Int(e1.to_int() - e2.to_int())
      },
      Bop(Times, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Int(e1.to_int() * e2.to_int())
      },
      Bop(Div, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Int(e1.to_int() / e2.to_int())
      },
      Bop(Seq, ref v1, ref e2) if v1.is_value() => {
        *e2.clone()
      },
      Bop(Assign, ref v1, ref v2) if v1.is_var() && v2.is_value() => {
        let x = v1.to_var();
        self.state.assign(x, *v2.clone());
        debug!("done assigning {:?}", self.state.mem);
        *v2.clone()
      },
      Ternary(ref v1, ref e2, ref e3) if v1.is_value() => {
        match v1.to_bool() {
          true => *e2.clone(),
          false => *e3.clone(),
        }
      },
      Decl(DConst, ref x, ref v1, ref e2) if v1.is_value() => {
        self.state.alloc_const(x.to_var(), *v1.clone());
        *e2.clone()
      },
      Decl(DVar, ref x, ref v1, ref e2) if x.is_var() && v1.is_value() => {
        debug!("allocing {:?}", v1);
        self.state.alloc(x.to_var(), *v1.clone());
        *e2.clone()
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        match **v1 {
          Func(ref name, ref e1, ref xs) => {
            self.state.begin_scope();
            // sub the params
            let exp = xs.iter().zip(es.iter())
              .fold(*e1.clone(), |exp, (xn, en)| {
                self.state.alloc(xn.to_var(), en.clone());
                exp
              });

            // sub the fn body for named functions
            let body = match *name {
              None => exp,
              Some(ref s) => {
                self.state.alloc(s.to_var(), *v1.clone());
                exp
              }
            };
            Scope(Box::new(body))
          },
          _ => {
            debug!("expected a Func, got {:?}", v1);
            panic!()
          },
        }
      },
      Scope(ref v1) if v1.is_value() => {
        self.state.end_scope();
        *v1.clone()
      },
      /**
       * Search Cases
       */
      Bop(ref op, ref v1, ref e2) if v1.is_value() => {
        Bop(
          op.clone(),
          Box::new(*v1.clone()),
          Box::new(step!(self, *e2.clone()))
        )
      },
      Bop(Assign, ref v1, ref e2) if v1.is_var() => {
        Bop(
          Assign,
          Box::new(*v1.clone()),
          Box::new(step!(self, *e2.clone()))
        )
      },
      Bop(op, e1, e2) => {
        Bop(op, Box::new(step!(self, *e1)), e2)
      },
      Uop(op, e1) => {
        Uop(op, Box::new(step!(self, *e1)))
      },
      Ternary(e1, e2, e3) => {
        Ternary(Box::new(step!(self, *e1)), e2, e3)
      },
      While(ref v1, ref e1o, _, ref e2o, ref e3) if v1.is_value() => {
        match v1.to_bool() {
          true => While(Box::new(*e1o.clone()), e1o.clone(), e2o.clone(), e2o.clone(), e3.clone()),
          false => *e3.clone(),
        }
      },
      While(ref e1, ref e1o, ref v2, ref e2o, ref e3) if v2.is_value() => {
        While(Box::new(step!(self, *e1.clone())), e1o.clone(), v2.clone(), e2o.clone(), e3.clone())
      },
      While(e1, e1o, e2, e2o, e3) => {
        While(e1, e1o, Box::new(step!(self, *e2)), e2o, e3)
      },
      Decl(dt, addr, e1, e2) => {
        Decl(dt, Box::new(*addr.clone()), Box::new(step!(self, *e1)), e2)
      },
      FnCall(ref v1, ref args) if v1.is_value() => {
        let mut found_nonvalue = false;

        let args2 = args.iter().map(|e| {
          if !found_nonvalue && !e.is_value() {
            found_nonvalue = true;
            self.step(e.clone()).unwrap()
          } else {
            e.clone()
          }
        }).collect();

        FnCall(v1.clone(), args2)
      },
      FnCall(e1, args) => {
        FnCall(Box::new(step!(self, *e1)), args)
      },
      Scope(e1) => {
        Scope(Box::new(step!(self, *e1)))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Expr {
    let mut e = parse(input).expect("parser error");

    let mut num_iterations = 0;

    loop {
      if num_iterations > 1000 {
        panic!("too many step iterations");
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return e.clone();
      } else {
        e = self.step(e).unwrap();
      }
    }
  }
}

pub fn boxx(input: &str) -> Expr {
  let mut repl = Repl::new();
  repl.eval(input)
}
<commit_msg>add todos for error handling cases<commit_after>use parser::parser::{parse};
use ast::Expr::*;
use ast::UnOp::*;
use ast::BinOp::*;
use ast::Dec::*;
use ast::*;
use state::*;
use interpreter_error::InterpreterError;
use std::result;

pub type Result<T> = result::Result<T, InterpreterError>;

macro_rules! step {
  ($s:expr, $e:expr) => (try!($s.step($e)));
}

pub struct Repl {
  pub state: State,
}

impl Repl {
  pub fn new() -> Repl {
    Repl {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e.clone() {
      Var(x) => {
        self.state.get(x)
      },
      /**
       * Values are ineligible for step
       */
      Int(_) | Bool(_) | Func(_, _, _) | Undefined => {
        debug!("stepping on a value {:?}", e);
        return Err(InterpreterError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, ref e1) if e1.is_bool() => {
        Bool(!e1.to_bool())
      },
      Uop(Neg, ref e1) if e1.is_int() => {
        Int(-1 * e1.to_int())
      },
      Bop(And, ref e1, ref e2) if e1.is_bool() && e2.is_bool() => {
        Bool(e1.to_bool() && e2.to_bool())
      },
      Bop(Or, ref e1, ref e2) if e1.is_bool() && e2.is_bool() => {
        Bool(e1.to_bool() || e2.to_bool())
      },
      Bop(Eq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 == *e2)
      },
      Bop(Ne, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 != *e2)
      },
      Bop(Mod, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        let n1 = e1.to_int();
        let n2 = e2.to_int();

        // rust % gives the remainder, not modulus
        let result = ((n1 % n2) + n2) % n2;

        Int(result)
      },
      Bop(Lt, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Bool(e1.to_int() < e2.to_int())
      },
      Bop(Gt, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Bool(e1.to_int() > e2.to_int())
      },
      Bop(Leq, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Bool(e1.to_int() <= e2.to_int())
      },
      Bop(Geq, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Bool(e1.to_int() >= e2.to_int())
      },
      Bop(Plus, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Int(e1.to_int() + e2.to_int())
      },
      Bop(Minus, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Int(e1.to_int() - e2.to_int())
      },
      Bop(Times, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Int(e1.to_int() * e2.to_int())
      },
      Bop(Div, ref e1, ref e2) if e1.is_int() && e2.is_int() => {
        Int(e1.to_int() / e2.to_int())
      },
      Bop(Seq, ref v1, ref e2) if v1.is_value() => {
        *e2.clone()
      },
      Bop(Assign, ref v1, ref v2) if v1.is_var() && v2.is_value() => {
        let x = v1.to_var();
        self.state.assign(x, *v2.clone());
        debug!("done assigning {:?}", self.state.mem);
        *v2.clone()
      },
      Ternary(ref v1, ref e2, ref e3) if v1.is_value() => {
        match v1.to_bool() {
          true => *e2.clone(),
          false => *e3.clone(),
        }
      },
      Decl(DConst, ref x, ref v1, ref e2) if v1.is_value() => {
        self.state.alloc_const(x.to_var(), *v1.clone());
        *e2.clone()
      },
      Decl(DVar, ref x, ref v1, ref e2) if x.is_var() && v1.is_value() => {
        debug!("allocing {:?}", v1);
        self.state.alloc(x.to_var(), *v1.clone());
        *e2.clone()
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        match **v1 {
          Func(ref name, ref e1, ref xs) => {
            self.state.begin_scope();
            // sub the params
            let exp = xs.iter().zip(es.iter())
              .fold(*e1.clone(), |exp, (xn, en)| {
                self.state.alloc(xn.to_var(), en.clone());
                exp
              });

            // sub the fn body for named functions
            let body = match *name {
              None => exp,
              Some(ref s) => {
                self.state.alloc(s.to_var(), *v1.clone());
                exp
              }
            };
            Scope(Box::new(body))
          },
          _ => {
            debug!("expected a Func, got {:?}", v1);
            // TODO: return an error value here
            panic!()
          },
        }
      },
      Scope(ref v1) if v1.is_value() => {
        self.state.end_scope();
        *v1.clone()
      },
      /**
       * Search Cases
       */
      Bop(ref op, ref v1, ref e2) if v1.is_value() => {
        Bop(
          op.clone(),
          Box::new(*v1.clone()),
          Box::new(step!(self, *e2.clone()))
        )
      },
      Bop(Assign, ref v1, ref e2) if v1.is_var() => {
        Bop(
          Assign,
          Box::new(*v1.clone()),
          Box::new(step!(self, *e2.clone()))
        )
      },
      Bop(op, e1, e2) => {
        Bop(op, Box::new(step!(self, *e1)), e2)
      },
      Uop(op, e1) => {
        Uop(op, Box::new(step!(self, *e1)))
      },
      Ternary(e1, e2, e3) => {
        Ternary(Box::new(step!(self, *e1)), e2, e3)
      },
      While(ref v1, ref e1o, _, ref e2o, ref e3) if v1.is_value() => {
        match v1.to_bool() {
          true => While(Box::new(*e1o.clone()), e1o.clone(), e2o.clone(), e2o.clone(), e3.clone()),
          false => *e3.clone(),
        }
      },
      While(ref e1, ref e1o, ref v2, ref e2o, ref e3) if v2.is_value() => {
        While(Box::new(step!(self, *e1.clone())), e1o.clone(), v2.clone(), e2o.clone(), e3.clone())
      },
      While(e1, e1o, e2, e2o, e3) => {
        While(e1, e1o, Box::new(step!(self, *e2)), e2o, e3)
      },
      Decl(dt, addr, e1, e2) => {
        Decl(dt, Box::new(*addr.clone()), Box::new(step!(self, *e1)), e2)
      },
      FnCall(ref v1, ref args) if v1.is_value() => {
        let mut found_nonvalue = false;

        let args2 = args.iter().map(|e| {
          if !found_nonvalue && !e.is_value() {
            found_nonvalue = true;
            // TODO: handle error here
            self.step(e.clone()).unwrap()
          } else {
            e.clone()
          }
        }).collect();

        FnCall(v1.clone(), args2)
      },
      FnCall(e1, args) => {
        FnCall(Box::new(step!(self, *e1)), args)
      },
      Scope(e1) => {
        Scope(Box::new(step!(self, *e1)))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Expr {
    let mut e = parse(input).expect("parser error");

    let mut num_iterations = 0;

    loop {
      if num_iterations > 1000 {
        // TODO: return an error value here
        panic!("too many step iterations");
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return e.clone();
      } else {
        // TODO: consider handling error here
        e = self.step(e).unwrap();
      }
    }
  }
}

pub fn boxx(input: &str) -> Expr {
  let mut repl = Repl::new();
  repl.eval(input)
}
<|endoftext|>


<commit_before>/// Provides a convient syntax for fusing conduits.
///
/// # Example
///
/// ```
/// #[macro_use] extern crate plumbum;
/// use plumbum::*;
/// fn main() {
///     use std::iter::FromIterator;
///     use plumbum::{Conduit, Source, Sink};
///
///     let src = fuse!{
///         Source::from_iter(vec![42, 43]),
///         Conduit::transform(|x| 1 + x),
///         Conduit::transform(|x| 2 * x)
///     };
///     let sink = Sink::fold(0, |x, y| x + y);
///
///     assert_eq!(src.connect(sink), 174);
/// }
#[macro_export]
macro_rules! fuse {

    ( $x:expr , $( $y:expr ) , * ) => {
        {
            let mut src = $x;
            $(
                src = $crate::ConduitM::fuse(src, $y);
            )*
            src
        }
    };

}
<commit_msg>make fuse macro right-binding<commit_after>/// Provides a convient syntax for fusing conduits.
///
/// # Example
///
/// ```
/// #[macro_use] extern crate plumbum;
/// use plumbum::*;
/// fn main() {
///     use std::iter::FromIterator;
///     use plumbum::{Conduit, Source, Sink};
///
///     let src = fuse!{
///         Source::from_iter(vec![42, 43]),
///         Conduit::transform(|x| 1 + x),
///         Conduit::transform(|x| 2 * x),
///         Conduit::transform(|x: i32| x.to_string())
///     };
///     let sink = Sink::fold(Vec::new(), |mut v, x| {
///         v.push(x);
///         return v
///     });
///
///     assert_eq!(src.connect(sink), vec!["86","88"]);
/// }
#[macro_export]
macro_rules! fuse {

    ( $x:expr ) => {
        $x
    };

    ( $x:expr , $( $t:tt )* ) => {
        $crate::ConduitM::fuse($x, fuse!{ $( $t )* });
    };

}
<|endoftext|>


<commit_before>//! Module describes ZX Spectrum screen
//! *block* - is 8x1 pxels stripe.
use crate::{
    utils::{screen::*, *},
    zx::{constants::*, machine::ZXMachine, screen::colors::*},
};
use alloc::boxed::Box;

// size of screen buffer in bytes
const BUFFER_LENGTH: usize = CANVAS_HEIGHT * CANVAS_WIDTH * BYTES_PER_PIXEL;

/// Represents how much 8x1 have been **passed**.
#[derive(PartialEq, Eq, Debug)]
pub struct BlocksCount {
    pub lines: usize,
    pub columns: usize,
}

impl BlocksCount {
    /// Constructs new `BlocksCount`
    pub fn new(lines: usize, columns: usize) -> BlocksCount {
        BlocksCount { lines, columns }
    }

    /// Constructs self from clocks count, taking into account machine type
    pub fn from_clocks(clocks: Clocks, machine: ZXMachine) -> BlocksCount {
        // get reference to specs for less words
        let specs = machine.specs();
        let mut lines;
        let mut columns;
        if clocks.count() < specs.clocks_ula_read_origin {
            // zero blocks passed
            lines = 0;
            columns = 0;
        } else {
            // clocks relative to first pixel rendering
            let clocks = clocks.count() - specs.clocks_ula_read_origin;
            // so find passed lines and columns count
            lines = clocks / specs.clocks_line;
            columns = (clocks % specs.clocks_line) / CLOCKS_PER_COL;
            // columns must contain PASSED blocks, so increment it.
            columns += 1;
            // if out of visible canvas line
            if columns > ATTR_COLS {
                lines += 1;
                columns = 0;
            };
            if lines >= CANVAS_HEIGHT {
                lines = 0;
                columns = 0;
            }
        };
        BlocksCount { lines, columns }
    }

    /// Returns count of blocks between positions
    /// # Notes
    /// `prev` must be lover than `self`
    pub fn passed_from(&self, prev: &BlocksCount) -> usize {
        if self.lines < prev.lines {
            ATTR_COLS - prev.columns
        // self.lines * ATTR_COLS + self.columns
        } else if self.lines == prev.lines {
            // if positions on the same line => just use difference in columns.
            self.columns - prev.columns
        } else {
            // add blocks left from start line, blocks on lines between and blocks on end line
            (ATTR_COLS - prev.columns) + (self.lines - prev.lines - 1) * ATTR_COLS + self.columns
        }
    }
}

/// Represents Single memory bank of screen
struct ScreenBank {
    pub attributes: Box<[ZXAttribute; ATTR_COLS * ATTR_ROWS]>,
    pub bitmap: Box<[u8; ATTR_COLS * CANVAS_HEIGHT]>,
}

/// Represents ZXSpectrum emulated mid part of screen (canvas)
pub struct ZXCanvas {
    machine: ZXMachine,
    palette: ZXPalette,
    last_blocks: BlocksCount,
    flash: bool,
    frame_counter: usize,
    // bitmap buffers
    buffer: Box<[u8; BUFFER_LENGTH]>,
    back_buffer: Box<[u8; BUFFER_LENGTH]>,
    // memory
    banks: [ScreenBank; 2],
    active_bank: usize,
    next_bank: usize,
}

impl ZXCanvas {
    /// Constructs new canvas of `machine`
    pub fn new(machine: ZXMachine) -> ZXCanvas {
        ZXCanvas {
            machine,
            palette: ZXPalette::default(),
            last_blocks: BlocksCount::new(0, 0),
            flash: false,
            frame_counter: 0,
            buffer: Box::new([0; BUFFER_LENGTH]),
            back_buffer: Box::new([0; BUFFER_LENGTH]),
            banks: [
                ScreenBank {
                    attributes: Box::new([ZXAttribute::from_byte(0); ATTR_COLS * ATTR_ROWS]),
                    bitmap: Box::new([0; ATTR_COLS * CANVAS_HEIGHT]),
                },
                ScreenBank {
                    attributes: Box::new([ZXAttribute::from_byte(0); ATTR_COLS * ATTR_ROWS]),
                    bitmap: Box::new([0; ATTR_COLS * CANVAS_HEIGHT]),
                },
            ],
            active_bank: 0,
            next_bank: 0,
        }
    }

    /// changes flash switch
    fn switch_flash(&mut self) {
        self.flash = !self.flash;
    }

    /// transforms zx spectrum bank to local index
    fn local_bank(&self, bank: usize) -> Option<usize> {
        match self.machine {
            ZXMachine::Sinclair48K if bank == 0 => Some(0),
            ZXMachine::Sinclair128K if bank == 5 => Some(0),
            ZXMachine::Sinclair128K if bank == 7 => Some(1),
            _ => None,
        }
    }

    /// selects bank of memory
    pub fn switch_bank(&mut self, bank: usize) {
        if let Some(bank) = self.local_bank(bank) {
            self.active_bank = bank;
        }
    }

    /// renders some  8x1 blocks
    /// `clocks` - current  clocks count form frame start.
    /// if clocks < previous call clocks then discard processing
    pub fn process_clocks(&mut self, clocks: Clocks) {
        let blocks = BlocksCount::from_clocks(clocks, self.machine);
        // so, let's count of 8x1 blocks, which passed.
        let count = blocks.passed_from(&self.last_blocks);
        if count > 0 {
            // fill pixels from prev to current
            let prev_block = self.last_blocks.lines * ATTR_COLS + self.last_blocks.columns;
            let curr_block = blocks.lines * ATTR_COLS + blocks.columns;
            // so we know that some blocks have been passed
            // block holds current blocks index
            for block in prev_block..curr_block {
                let bitmap = self.banks[self.active_bank].bitmap[block];
                // one attr per 8x8 area
                let attr_row = block / (ATTR_COLS * 8);
                let attr_col = block % ATTR_COLS;
                let attr = self.banks[self.active_bank].attributes[attr_row * ATTR_COLS + attr_col];
                for pixel in 0..8 {
                    // from most significant bit
                    let state = ((bitmap << pixel) & 0x80) != 0;
                    let color = self
                        .palette
                        .get_rgba(attr.active_color(state, self.flash), attr.brightness);
                    let index = (block * 8 + pixel) * BYTES_PER_PIXEL;
                    self.buffer[index..index + BYTES_PER_PIXEL].clone_from_slice(color);
                }
            }
            // cahnge last block to current
            self.last_blocks = blocks;
        }
    }

    /// starts new frame
    pub fn new_frame(&mut self) {
        // post finished bitmap to second buffer (all not-rendered part will be updated)
        self.back_buffer.clone_from_slice(&(*self.buffer));
        self.last_blocks = BlocksCount::new(0, 0);
        if self.frame_counter % 16 == 0 {
            self.switch_flash();
        }
        self.frame_counter += 1;
    }

    /// Updates data if screen ram
    pub fn update(&mut self, rel_addr: u16, bank: usize, data: u8) {
        if let Some(bank) = self.local_bank(bank) {
            match rel_addr {
                // change bitmap
                0..=BITMAP_MAX_REL => {
                    let line = bitmap_line_rel(rel_addr);
                    let col = bitmap_col_rel(rel_addr);
                    self.banks[bank].bitmap[line * ATTR_COLS + col] = data;
                }
                // change attribute
                ATTR_BASE_REL..=ATTR_MAX_REL => {
                    let row = attr_row_rel(rel_addr);
                    let col = attr_col_rel(rel_addr);
                    self.banks[bank].attributes[row * ATTR_COLS + col] =
                        ZXAttribute::from_byte(data);
                }
                // no screen changes
                _ => {}
            }
        }
    }

    /// Returns reference to canvas main texture
    pub fn texture(&self) -> &[u8] {
        &(*self.buffer)
    }
}
<commit_msg>Fixed screen buffering<commit_after>//! Module describes ZX Spectrum screen
//! *block* - is 8x1 pxels stripe.
use crate::{
    utils::{screen::*, *},
    zx::{constants::*, machine::ZXMachine, screen::colors::*},
};
use alloc::boxed::Box;

// size of screen buffer in bytes
const BUFFER_LENGTH: usize = CANVAS_HEIGHT * CANVAS_WIDTH * BYTES_PER_PIXEL;

/// Represents how much 8x1 have been **passed**.
#[derive(PartialEq, Eq, Debug)]
pub struct BlocksCount {
    pub lines: usize,
    pub columns: usize,
}

impl BlocksCount {
    /// Constructs new `BlocksCount`
    pub fn new(lines: usize, columns: usize) -> BlocksCount {
        BlocksCount { lines, columns }
    }

    /// Constructs self from clocks count, taking into account machine type
    pub fn from_clocks(clocks: Clocks, machine: ZXMachine) -> BlocksCount {
        // get reference to specs for less words
        let specs = machine.specs();
        let mut lines;
        let mut columns;
        if clocks.count() < specs.clocks_ula_read_origin {
            // zero blocks passed
            lines = 0;
            columns = 0;
        } else {
            // clocks relative to first pixel rendering
            let clocks = clocks.count() - specs.clocks_ula_read_origin;
            // so find passed lines and columns count
            lines = clocks / specs.clocks_line;
            columns = (clocks % specs.clocks_line) / CLOCKS_PER_COL;
            // columns must contain PASSED blocks, so increment it.
            columns += 1;
            // if out of visible canvas line
            if columns > ATTR_COLS {
                lines += 1;
                columns = 0;
            };
            if lines >= CANVAS_HEIGHT {
                lines = 0;
                columns = 0;
            }
        };
        BlocksCount { lines, columns }
    }

    /// Returns count of blocks between positions
    /// # Notes
    /// `prev` must be lover than `self`
    pub fn passed_from(&self, prev: &BlocksCount) -> usize {
        if self.lines < prev.lines {
            ATTR_COLS - prev.columns
        // self.lines * ATTR_COLS + self.columns
        } else if self.lines == prev.lines {
            // if positions on the same line => just use difference in columns.
            self.columns - prev.columns
        } else {
            // add blocks left from start line, blocks on lines between and blocks on end line
            (ATTR_COLS - prev.columns) + (self.lines - prev.lines - 1) * ATTR_COLS + self.columns
        }
    }
}

/// Represents Single memory bank of screen
struct ScreenBank {
    pub attributes: Box<[ZXAttribute; ATTR_COLS * ATTR_ROWS]>,
    pub bitmap: Box<[u8; ATTR_COLS * CANVAS_HEIGHT]>,
}

/// Represents ZXSpectrum emulated mid part of screen (canvas)
pub struct ZXCanvas {
    machine: ZXMachine,
    palette: ZXPalette,
    last_blocks: BlocksCount,
    flash: bool,
    frame_counter: usize,
    // bitmap buffers
    buffer: Box<[u8; BUFFER_LENGTH]>,
    back_buffer: Box<[u8; BUFFER_LENGTH]>,
    // memory
    banks: [ScreenBank; 2],
    active_bank: usize,
    next_bank: usize,
}

impl ZXCanvas {
    /// Constructs new canvas of `machine`
    pub fn new(machine: ZXMachine) -> ZXCanvas {
        ZXCanvas {
            machine,
            palette: ZXPalette::default(),
            last_blocks: BlocksCount::new(0, 0),
            flash: false,
            frame_counter: 0,
            buffer: Box::new([0; BUFFER_LENGTH]),
            back_buffer: Box::new([0; BUFFER_LENGTH]),
            banks: [
                ScreenBank {
                    attributes: Box::new([ZXAttribute::from_byte(0); ATTR_COLS * ATTR_ROWS]),
                    bitmap: Box::new([0; ATTR_COLS * CANVAS_HEIGHT]),
                },
                ScreenBank {
                    attributes: Box::new([ZXAttribute::from_byte(0); ATTR_COLS * ATTR_ROWS]),
                    bitmap: Box::new([0; ATTR_COLS * CANVAS_HEIGHT]),
                },
            ],
            active_bank: 0,
            next_bank: 0,
        }
    }

    /// changes flash switch
    fn switch_flash(&mut self) {
        self.flash = !self.flash;
    }

    /// transforms zx spectrum bank to local index
    fn local_bank(&self, bank: usize) -> Option<usize> {
        match self.machine {
            ZXMachine::Sinclair48K if bank == 0 => Some(0),
            ZXMachine::Sinclair128K if bank == 5 => Some(0),
            ZXMachine::Sinclair128K if bank == 7 => Some(1),
            _ => None,
        }
    }

    /// selects bank of memory
    pub fn switch_bank(&mut self, bank: usize) {
        if let Some(bank) = self.local_bank(bank) {
            self.active_bank = bank;
        }
    }

    /// renders some  8x1 blocks
    /// `clocks` - current  clocks count form frame start.
    /// if clocks < previous call clocks then discard processing
    pub fn process_clocks(&mut self, clocks: Clocks) {
        let blocks = BlocksCount::from_clocks(clocks, self.machine);
        // so, let's count of 8x1 blocks, which passed.
        let count = blocks.passed_from(&self.last_blocks);
        if count > 0 {
            // fill pixels from prev to current
            let prev_block = self.last_blocks.lines * ATTR_COLS + self.last_blocks.columns;
            let curr_block = blocks.lines * ATTR_COLS + blocks.columns;
            // so we know that some blocks have been passed
            // block holds current blocks index
            for block in prev_block..curr_block {
                let bitmap = self.banks[self.active_bank].bitmap[block];
                // one attr per 8x8 area
                let attr_row = block / (ATTR_COLS * 8);
                let attr_col = block % ATTR_COLS;
                let attr = self.banks[self.active_bank].attributes[attr_row * ATTR_COLS + attr_col];
                for pixel in 0..8 {
                    // from most significant bit
                    let state = ((bitmap << pixel) & 0x80) != 0;
                    let color = self
                        .palette
                        .get_rgba(attr.active_color(state, self.flash), attr.brightness);
                    let index = (block * 8 + pixel) * BYTES_PER_PIXEL;
                    self.back_buffer[index..index + BYTES_PER_PIXEL].clone_from_slice(color);
                }
            }
            // cahnge last block to current
            self.last_blocks = blocks;
        }
    }

    /// starts new frame
    pub fn new_frame(&mut self) {
        // post finished bitmap to second buffer (all not-rendered part will be updated)
        self.buffer.clone_from_slice(&(*self.back_buffer));
        self.last_blocks = BlocksCount::new(0, 0);
        if self.frame_counter % 16 == 0 {
            self.switch_flash();
        }
        self.frame_counter += 1;
    }

    /// Updates data if screen ram
    pub fn update(&mut self, rel_addr: u16, bank: usize, data: u8) {
        if let Some(bank) = self.local_bank(bank) {
            match rel_addr {
                // change bitmap
                0..=BITMAP_MAX_REL => {
                    let line = bitmap_line_rel(rel_addr);
                    let col = bitmap_col_rel(rel_addr);
                    self.banks[bank].bitmap[line * ATTR_COLS + col] = data;
                }
                // change attribute
                ATTR_BASE_REL..=ATTR_MAX_REL => {
                    let row = attr_row_rel(rel_addr);
                    let col = attr_col_rel(rel_addr);
                    self.banks[bank].attributes[row * ATTR_COLS + col] =
                        ZXAttribute::from_byte(data);
                }
                // no screen changes
                _ => {}
            }
        }
    }

    /// Returns reference to canvas main texture
    pub fn texture(&self) -> &[u8] {
        &(*self.buffer)
    }
}
<|endoftext|>