Skip to content
Snippets Groups Projects
Commit 064d907d authored by Per Lindgren's avatar Per Lindgren
Browse files

precedence climbing wip

parent 09056afa
No related branches found
No related tags found
No related merge requests found
......@@ -26,6 +26,30 @@
"kind": "build",
"isDefault": true
}
},
{
"type": "shell",
"label": "cargo run --example precedence",
"command": "cargo run --example precedence",
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "shell",
"label": "cargo run --example main2",
"command": "cargo run --example main2",
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
\ No newline at end of file
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);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment