Skip to content
Snippets Groups Projects
Select Git revision
  • 064d907d6d21dd16c1f165a5891104dbedc9aa87
  • master default protected
  • home_exam
  • wip
4 results

main2.rs

Blame
  • Forked from Per Lindgren / D7050E
    Source project has a limited visibility.
    main2.rs 6.97 KiB
    extern crate nom;
    
    use nom::combinator::map_res;
    use nom::{
        branch::alt,
        bytes::complete::tag,
        character::complete::{digit1, multispace0},
        combinator::map,
        error::{context, VerboseError, VerboseErrorKind},
        sequence::{preceded, tuple},
        IResult,
    };
    
    #[derive(Debug, Clone, Copy, PartialEq)]
    pub enum Op {
        Add,
        Sub,
        Mul,
        Div,
        Pow,
    }
    
    #[derive(Debug, Clone, PartialEq)]
    pub enum Expr {
        Num(i32),
        BinOp(Box<Expr>, Op, Box<Expr>),
    }
    
    pub fn parse_i32(i: &str) -> IResult<&str, Expr> {
        map(digit1, |digit_str: &str| {
            Expr::Num(digit_str.parse::<i32>().unwrap())
        })(i)
    }
    
    fn parse_op(i: &str) -> IResult<&str, Op> {
        alt((
            map(tag("+"), |_| Op::Add),
            map(tag("-"), |_| Op::Sub),
            map(tag("*"), |_| Op::Mul),
            map(tag("/"), |_| Op::Div),
            map(tag("^"), |_| Op::Pow),
        ))(i)
    }
    
    fn parse_expr(i: &str) -> IResult<&str, Expr> {
        preceded(
            multispace0,
            alt((
                map(
                    tuple((parse_i32, preceded(multispace0, parse_op), parse_expr)),
                    |(l, op, r)| Expr::BinOp(Box::new(l), op, Box::new(r)),
                ),
                parse_i32,
            )),
        )(i)
    }
    
    fn math_expr(e: &Expr) -> String {
        match e {
            Expr::Num(i) => format!("{}", i),
            Expr::BinOp(l, op, r) => format!("({} {:?} {})", math_expr(l), op, math_expr(r)),
        }
    }
    
    fn math_eval(e: &Expr) -> i32 {
        match e {
            Expr::Num(i) => *i,
            Expr::BinOp(l, op, r) => {
                let lv = math_eval(l);
                let rv = math_eval(r);
                match op {
                    Op::Add => lv + rv,
                    Op::Sub => lv - rv,
                    Op::Mul => lv * rv,
                    Op::Div => lv / rv,
                    Op::Pow => lv ^ rv,
                }
            }
        }
    }
    
    enum Ass {
        Left,
        Right,
    }
    
    fn climb_op(op: &Op) -> (u8, Ass) {
        match op {
            Op::Add => (1, Ass::Left),
            Op::Sub => (1, Ass::Left),
            Op::Mul => (2, Ass::Left),
            Op::Div => (2, Ass::Left),
            Op::Pow => (3, Ass::Right),
        }
    }
    
    fn test_clone(e: Expr) -> Expr {
        Expr::BinOp(Box::new(e.clone()), Op::Add, Box::new(e))
    }
    
    // fn climb(e: Expr) -> Expr {
    //     println!("climb {:?}", &e);
    //     match e.clone() {
    //         Expr::Num(_) => e,
    //         Expr::BinOp(l, op, r) => {
    //             let (prec, ass) = climb_op(&op);
    
    //             // lookahead
    //             let r_new = climb(*r);
    //             match r_new.clone() {
    //                 Expr::Num(_) => Expr::BinOp(Box::new(*l), op, Box::new(r_new)),
    //                 Expr::BinOp(r_l, r_op, r_r) => {
    //                     let (r_prec, r_ass) = climb_op(&r_op);
    //                     println!(
    //                         "-- l: {:?}, r: {:?}, r_prec {}, prec {}
    //                     ",
    //                         r_l, r_r, prec, r_prec
    //                     );
    //                     if r_prec
    //                         < prec
    //                             + match r_ass {
    //                                 Ass::Left => 1,
    //                                 Ass::Right => 0,
    //                             }
    //                     {
    //                         // swap
    //                         println!("swap");
    //                         let new_l = Expr::BinOp(Box::new(*l), op, Box::new(*r_l));
    //                         let new_top = Expr::BinOp(Box::new(new_l), r_op, Box::new(*r_r));
    
    //                         new_top
    //                     } else {
    //                         Expr::BinOp(Box::new(*l), op, Box::new(r_new))
    //                     }
    //                 }
    //             }
    //         }
    //     }
    // }
    
    fn climb(e: Expr) -> Expr {
        println!("climb {:?}", &e);
        match e.clone() {
            Expr::Num(_) => e,
            Expr::BinOp(l, op, r) => {
                let (prec, ass) = climb_op(&op);
    
                // lookahead
                let e = match *r.clone() {
                    Expr::Num(_) => e,
                    Expr::BinOp(r_l, r_op, r_r) => {
                        let (r_prec, r_ass) = climb_op(&r_op);
                        println!(
                            "-- l: {:?}, r: {:?}, r_prec {}, prec {}
                        ",
                            r_l, r_r, prec, r_prec
                        );
                        if r_prec
                            < prec
                                + match r_ass {
                                    Ass::Left => 1,
                                    Ass::Right => 0,
                                }
                        {
                            // swap
                            println!("swap");
                            let new_l = Expr::BinOp(Box::new(*l), op, Box::new(*r_l));
                            let new_top = Expr::BinOp(Box::new(new_l), r_op, Box::new(*r_r));
    
                            climb(new_top)
                        } else {
                            e
                        }
                    }
                };
                
                match e {
                    Expr::Num(_) => e,
                    Expr::BinOp(l, op, r) => Expr::BinOp(l, op, Box::new(climb(*r)))
                }
            }
        }
    }
    
    fn test_eq(s:&str, v:i32) {
        assert_eq!(math_eval(&climb(parse_expr(s).unwrap().1)), v);    
    }
    
    #[test]
    fn climb1() {
        test_eq("1-2+3", 1-2+3);
    }
    
    #[test]
    fn climb2() {
        test_eq("1*2+3", 1*2+3);
    }
    
    #[test]
    fn climb3() {
        test_eq("1*2+3*4-5", 1*2+3*4-5);
    }
    
    fn main() {
        let p = parse_expr("3*2+5").unwrap().1;
        println!("{:?}", &p);
        println!("math {}", math_expr(&p));
        let r = climb(p);
        println!("r {:?}", &r);
        println!("math r {}", math_expr(&r));
        println!("eval r {} = {} ", math_eval(&r), 3 * 2 + 5);
    
        println!();
        let p = parse_expr("3+2*5").unwrap().1;
        println!("{:?}", &p);
        println!("math {}", math_expr(&p));
        let r = climb(p);
        println!("r {:?}", &r);
        println!("math r {}", math_expr(&r));
        println!("eval r {} = {} ", math_eval(&r), 3 + 2 * 5);
    
        println!();
        let p = parse_expr("3+2*5+27").unwrap().1;
        println!("{:?}", &p);
        println!("math {}", math_expr(&p));
        let r = climb(p);
        println!("r {:?}", &r);
        println!("math r {}", math_expr(&r));
        println!("eval r {} = {} ", math_eval(&r), 3 + 2 * 5 + 27);
    
        println!();
        let p = parse_expr("2*5+11*27+13").unwrap().1;
        println!("{:?}", &p);
        println!("math {}", math_expr(&p));
        let r = climb(p);
        println!("r {:?}", &r);
        println!("math r {}", math_expr(&r));
        println!("eval r {} = {} ", math_eval(&r), 2 * 5 + 11 * 27 + 13);
    
        println!();
        let p = parse_expr("1-2-3").unwrap().1;
        println!("{:?}", &p);
        println!("math {}", math_expr(&p));
        let r = climb(p);
        println!("r {:?}", &r);
        println!("math r {}", math_expr(&r));
        println!("eval r {} = {} ", math_eval(&r), 1 - 2 - 3);
    
        let i = "1-2-3-4";
        println!("\n{}", i);
        let p = parse_expr(i).unwrap().1;
        println!("{:?}", &p);
        println!("math {}", math_expr(&p));
        println!("eval r {} = {} ", math_eval(&p), 1 - 2 - 3 - 4);
        let r = climb(p);
        println!("r {:?}", &r);
        println!("math r {}", math_expr(&r));
        println!("eval r {} = {} ", math_eval(&r), ((1 - 2) - 3) - 4);
    }