diff --git a/examples/main4.rs b/examples/main4.rs new file mode 100644 index 0000000000000000000000000000000000000000..b5d0653b78812b98b8eb9296a5d503206b93cd4a --- /dev/null +++ b/examples/main4.rs @@ -0,0 +1,240 @@ +extern crate nom; + +use nom::{ + branch::alt, + bytes::complete::tag, + character::complete::{digit1, multispace0}, + combinator::map, + 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>), +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Token { + Num(i32), + Op(Op), +} + +pub fn parse_i32(i: &str) -> IResult<&str, Token> { + map(digit1, |digit_str: &str| { + Token::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, Vec<Token>> { + preceded( + multispace0, + alt(( + map( + tuple((parse_i32, preceded(multispace0, parse_op), parse_expr)), + |(l, op, r)| { + let mut v = Vec::new(); + v.push(l); + v.push(Token::Op(op)); + v.extend(r); + v + }, + ), + map(parse_i32, |i| { + let mut v = Vec::new(); + v.push(i); + v + }), + )), + )(i) +} + +fn math_expr(e: &Expr) -> String { + match e { + Expr::Num(i) => format!("{}", i), + Expr::BinOp(l, op, r) => { + format!("({:?}, {}, {})", op, math_expr(l), 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.pow(rv as u32), + } + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +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), + } +} + +// boilerplate +// compute_expr(min_prec): +// result = compute_atom() + +// while cur token is a binary operator with precedence >= min_prec: +// prec, assoc = precedence and associativity of current token +// if assoc is left: +// next_min_prec = prec + 1 +// else: +// next_min_prec = prec +// rhs = compute_expr(next_min_prec) +// result = compute operator(result, rhs) + +// return result + +fn token_to_expr(t: Token) -> Expr { + match t { + Token::Num(i) => Expr::Num(i), + _ => panic!(), + } +} + +fn climb(mut v: Vec<Token>, min_prec: u8) -> (Expr, Vec<Token>) { + println!("in climb {:?}, {}", v, min_prec); + let t = v.pop().unwrap(); + let mut result = token_to_expr(t); + loop { + match v.pop() { + Some(Token::Num(_)) => { + println!("break num"); + break; + } + Some(Token::Op(op)) => { + println!("result {:?}, op {:?}, v:{:?}", result, op, v); + let (prec, assoc) = climb_op(&op); + if prec < min_prec { + println!("break prec"); + break; + } else { + println!("push"); + let next_min_prec = + if assoc == Ass::Left { 1 + prec } else { prec }; + let (rhs, v_rest) = climb(v.clone(), next_min_prec); + v = v_rest; + println!("return from call, rhs {:?}, v {:?}", rhs, v); + println!("current result {:?}", result); + result = Expr::BinOp(Box::new(result), op, Box::new(rhs)); + println!("new result {:?}", result); + } + } + + _ => { + println!("reaced end"); + break; + } // reached end + } + } + (result, v) +} + +fn test_eq(s: &str, v: i32) { + let mut p = parse_expr(s).unwrap().1; + println!("{:?}", p); + p.reverse(); + let e = climb(p, 0); + println!("{:?}", e); + + println!("e = {}, v = {}", math_eval(&e.0), v); +} + +fn main() { + test_eq("1 + 2", 1 + 2); + test_eq("1 + 2 * 3", 1 + 2 * 3); + test_eq("3 * 4 + 5", 3 * 4 + 5); + + // // climb_test("2*5+10+10", 2*5+10+10); + // // climb_test("2*5+10*11-1", 2*5+10*11-1); + // // climb_test("2*5+10*11-2+12", 2*5+10*11-1+12); + // // climb_test("1+2*3-4+5", 1 + 2 * 3 - 4 + 5); + // climb_test("1", 1); + // climb_test("1+2", 1 + 2); +} + +// // #[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); +// // } + +// // #[test] +// // fn climb4() { +// // test_eq("2^5", 2i32.pow(5)); +// // } + +// // #[test] +// // fn climb5() { +// // test_eq("2*3+4+5", 2 * 3 + 4 + 5); +// // } + +// // #[test] +// // fn climb6() { +// // test_eq("2*3-4*5-2", 2 * 3 - 4 * 5 - 2); +// // } + +// // #[test] +// // fn climb_err() { +// // test_eq("2 + 2 ^ 5 -3", 2 + 2i32.pow(5 - 3)); +// // } + +// fn climb_test(s: &str, v: i32) { +// let p = parse_expr(s).unwrap().1; +// println!("{:?}", &p); +// println!("math {}\n", math_expr(&p)); +// let r = climb(p, 0); +// println!("r {:?}", &r); +// println!("math r {}", math_expr(&r)); +// println!("eval r {} = {} ", math_eval(&r), v); +// }