diff --git a/src/calc.pest b/src/calc.pest index 142f9f1..439eaf5 100644 --- a/src/calc.pest +++ b/src/calc.pest @@ -1,15 +1,24 @@ +atom = _{ int | ident } int = @{ (ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) | "0" } ident = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } -uniop = @{ "+" | "-" } -add = @{ "+" | "-" } -mul = @{ "*" | "/" | "%" } -pow = @{ "^" } -expr = { term3 ~ (add ~ term3)* } -term3 = { term2 ~ (mul ~ term2)* } -term2 = { term1 ~ (pow ~ term2)* } + +uniop = _{ plus | neg } +plus = { "+" } +neg = { "-" } + +binop = _{ add | sub | mul | div | rem | pow } +add = { "+" } +sub = { "-" } +mul = { "*" } +div = { "/" } +rem = { "%" } +pow = { "^" } + +func = { ident ~ "(" ~ args ~ ")" } +args = _{ expr ~ ("," ~ expr)* | "" } + +expr = { term1 ~ (binop ~ term1)* } term1 = { uniop? ~ term0 } term0 = { func | "(" ~ expr ~ ")" | atom } -atom = { int | ident } -func = { ident ~ "(" ~ args ~ ")" } -args = { expr ~ ("," ~ expr)* | "" } + WHITESPACE = _{ " " | "\t" | NEWLINE } diff --git a/src/main.rs b/src/main.rs index 4e8c8e9..c56bc23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -88,21 +88,10 @@ fn ast_func(func: pest::iterators::Pair) -> Ast { let func_name = iter.next().unwrap(); assert_eq!(func_name.as_rule(), Rule::ident); let name = func_name.as_str().to_owned(); - let args = iter.next().unwrap(); - assert_eq!(args.as_rule(), Rule::args); - let args = args.into_inner().map(ast_expr).collect::>(); + let args = iter.map(ast_expr).collect::>(); Ast::Func { name, args } } -fn ast_atom(atom: pest::iterators::Pair) -> Ast { - let t = atom.into_inner().next().unwrap(); - match t.as_rule() { - Rule::int => Ast::Num(t.as_str().parse::().unwrap()), - Rule::ident => Ast::Ident(t.as_str().to_owned()), - _ => unreachable!(), - } -} - fn ast_term0(term0: pest::iterators::Pair) -> Ast { let t = term0.into_inner().next().unwrap(); match t.as_rule() { @@ -111,7 +100,8 @@ fn ast_term0(term0: pest::iterators::Pair) -> Ast { println!("{:?}", t); ast_expr(t) } - Rule::atom => ast_atom(t), + Rule::int => Ast::Num(t.as_str().parse::().unwrap()), + Rule::ident => Ast::Ident(t.as_str().to_owned()), _ => unreachable!(), } } @@ -119,91 +109,48 @@ fn ast_term0(term0: pest::iterators::Pair) -> Ast { fn ast_term1(term1: pest::iterators::Pair) -> Ast { let mut iter = term1.into_inner(); let first = iter.next().unwrap(); - if first.as_rule() == Rule::uniop { - let op = match first.as_str() { - "+" => UniOp::Plus, - "-" => UniOp::Neg, - _ => unreachable!(), - }; - let second = iter.next().unwrap(); - let expr = Box::new(ast_term0(second)); - Ast::UniOp { op, expr } - } else { - assert_eq!(first.as_rule(), Rule::term0); + if first.as_rule() == Rule::term0 { ast_term0(first) - } -} - -fn ast_term2(term2: pest::iterators::Pair) -> Ast { - let mut iter = term2.into_inner(); - let first = iter.next().unwrap(); - assert_eq!(first.as_rule(), Rule::term1); - let mut ast = ast_term1(first); - if let Some(op) = iter.next() { - assert_eq!(op.as_rule(), Rule::pow); - let op = match op.as_str() { - "^" => BinOp::Pow, + } else { + let op = match first.as_rule() { + Rule::plus => UniOp::Plus, + Rule::neg => UniOp::Neg, _ => unreachable!(), }; - let right = iter.next().unwrap(); - assert_eq!(right.as_rule(), Rule::term2); - let right = Box::new(ast_term2(right)); - ast = Ast::BinOp { + Ast::UniOp { op, - left: Box::new(ast), - right, - }; + expr: Box::new(ast_term0(iter.next().unwrap())), + } } - dbg!(ast) } -fn ast_term3(term3: pest::iterators::Pair) -> Ast { - let mut iter = term3.into_inner(); - let first = iter.next().unwrap(); - assert_eq!(first.as_rule(), Rule::term2); - let mut ast = ast_term2(first); - while let Some(op) = iter.next() { - assert_eq!(op.as_rule(), Rule::mul); - let op = match op.as_str() { - "*" => BinOp::Mul, - "/" => BinOp::Div, - "%" => BinOp::Rem, - _ => unreachable!(), - }; - let right = iter.next().unwrap(); - assert_eq!(right.as_rule(), Rule::term2); - let right = Box::new(ast_term2(right)); - ast = Ast::BinOp { - op, - left: Box::new(ast), - right, - }; +fn binop(left: Ast, op: pest::iterators::Pair, right: Ast) -> Ast { + let op = match op.as_rule() { + Rule::add => BinOp::Add, + Rule::sub => BinOp::Sub, + Rule::mul => BinOp::Mul, + Rule::div => BinOp::Div, + Rule::rem => BinOp::Rem, + Rule::pow => BinOp::Pow, + _ => unreachable!(), + }; + Ast::BinOp { + op, + left: Box::new(left), + right: Box::new(right), } - dbg!(ast) } -fn ast_expr(expr: pest::iterators::Pair) -> Ast { - let mut iter = expr.into_inner(); - let first = iter.next().unwrap(); - assert_eq!(first.as_rule(), Rule::term3); - let mut ast = ast_term3(first); - while let Some(op) = iter.next() { - assert_eq!(op.as_rule(), Rule::add); - let op = match op.as_str() { - "+" => BinOp::Add, - "-" => BinOp::Sub, - _ => unreachable!(), - }; - let right = iter.next().unwrap(); - assert_eq!(right.as_rule(), Rule::term3); - let right = Box::new(ast_term3(right)); - ast = Ast::BinOp { - op, - left: Box::new(ast), - right, - }; - } - dbg!(ast) +fn ast_expr(pair: pest::iterators::Pair) -> Ast { + use pest::prec_climber::{Assoc, Operator, PrecClimber}; + let climber = PrecClimber::new(vec![ + Operator::new(Rule::add, Assoc::Left) | Operator::new(Rule::sub, Assoc::Left), + Operator::new(Rule::mul, Assoc::Left) + | Operator::new(Rule::div, Assoc::Left) + | Operator::new(Rule::rem, Assoc::Left), + Operator::new(Rule::pow, Assoc::Right), + ]); + climber.climb(pair.into_inner(), ast_term1, binop) } fn make_ast(parsed: pest::iterators::Pairs) -> Vec {