use nom::branch::alt;
use nom::character::complete::{char, multispace0};
use nom::combinator::map;
use nom::sequence::{delimited, preceded, tuple};
use nom::{Finish, IResult};
use crate::parse::{Expression, Node, Operator};
fn literal_num(input: &str) -> IResult<&str, Expression> {
map(
preceded(multispace0, nom::number::complete::double),
Expression::Literal,
)(input)
}
fn op_add(input: &str) -> IResult<&str, Operator> {
map(
preceded(multispace0, alt((char('+'), char('-')))),
|op: char| match op {
'+' => Operator::Add,
'-' => Operator::Sub,
_ => unreachable!(),
},
)(input)
}
fn op_mul(input: &str) -> IResult<&str, Operator> {
map(
preceded(multispace0, alt((char('*'), char('/')))),
|op: char| match op {
'*' => Operator::Mul,
'/' => Operator::Div,
_ => unreachable!(),
},
)(input)
}
fn nest(input: &str) -> IResult<&str, Expression> {
delimited(
preceded(multispace0, char('(')),
add,
preceded(multispace0, char(')')),
)(input)
}
fn lit_or_nest(input: &str) -> IResult<&str, Expression> {
alt((literal_num, nest))(input)
}
fn mul(input: &str) -> IResult<&str, Expression> {
alt((
map(
tuple((lit_or_nest, op_mul, mul)),
|(left, operator, right)| Expression::ast(Node::new(left, operator, right)),
),
lit_or_nest,
))(input)
}
fn add(input: &str) -> IResult<&str, Expression> {
alt((
map(tuple((mul, op_add, add)), |(left, operator, right)| {
Expression::ast(Node::new(left, operator, right))
}),
mul,
))(input)
}
pub(crate) fn parse_line(input: &str) -> Result<(&str, Expression), nom::error::Error<&str>> {
add(input).finish()
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! lit {
($lit:expr) => {
Expression::from($lit)
};
}
macro_rules! node {
($left:expr, $op:expr, $right:expr) => {
Expression::ast(Node::new(
Expression::from($left),
Operator::try_from($op).unwrap(),
Expression::from($right),
))
};
}
#[test]
fn parse_literal() {
assert_eq!(literal_num("4"), Ok(("", lit!(4.))));
assert_eq!(literal_num(" 4"), Ok(("", lit!(4.))));
assert_eq!(literal_num("4 "), Ok((" ", lit!(4.))));
assert_eq!(literal_num("-4"), Ok(("", lit!(-4.))));
}
#[test]
fn parse_operator() {
assert_eq!(op_add("+"), Ok(("", Operator::Add)));
assert_eq!(op_add(" +"), Ok(("", Operator::Add)));
assert_eq!(op_add(" + "), Ok((" ", Operator::Add)));
assert_eq!(op_add("-"), Ok(("", Operator::Sub)));
assert_eq!(op_add(" -"), Ok(("", Operator::Sub)));
assert_eq!(op_add(" - "), Ok((" ", Operator::Sub)));
assert_eq!(op_mul("*"), Ok(("", Operator::Mul)));
assert_eq!(op_mul(" *"), Ok(("", Operator::Mul)));
assert_eq!(op_mul(" * "), Ok((" ", Operator::Mul)));
assert_eq!(op_mul("/"), Ok(("", Operator::Div)));
assert_eq!(op_mul(" /"), Ok(("", Operator::Div)));
assert_eq!(op_mul(" / "), Ok((" ", Operator::Div)));
}
#[test]
fn parse_nest() {
assert_eq!(nest("(100)"), Ok(("", lit!(100.))));
assert_eq!(nest(" ( 100 )"), Ok(("", lit!(100.))));
assert_eq!(nest(" ( 100 ) "), Ok((" ", lit!(100.))));
}
#[test]
fn parse_mul() {
assert_eq!(mul("10 * 20"), Ok(("", node!(10, '*', 20))));
assert_eq!(mul("10*20"), Ok(("", node!(10, '*', 20))));
assert_eq!(mul("10*20 "), Ok((" ", node!(10, '*', 20))));
assert_eq!(mul("2 * 3 * 4"), Ok(("", node!(2, '*', node!(3, '*', 4)))));
assert_eq!(
mul("2 * 3 * 4 * 5"),
Ok(("", node!(2, '*', node!(3, '*', node!(4, '*', 5)))))
);
}
#[test]
fn parse_expression() {
assert_eq!(add("1 + 2 + 3"), Ok(("", node!(1, '+', node!(2, '+', 3)))),);
assert_eq!(
add("1 + 2 + 3 + 4"),
Ok(("", node!(1, '+', node!(2, '+', node!(3, '+', 4))))),
);
assert_eq!(add("100"), Ok(("", lit!(100))));
assert_eq!(add("2 * 3"), Ok(("", node!(2, '*', 3))));
assert_eq!(add("2 * 3 + 4"), Ok(("", node!(node!(2, '*', 3), '+', 4))));
assert_eq!(
add("2 * 3 * 4 + 5"),
Ok(("", node!(node!(2, '*', node!(3, '*', 4)), '+', 5)))
);
assert_eq!(
add("2 / 3 / 4 - 5"),
Ok(("", node!(node!(2, '/', node!(3, '/', 4)), '-', 5)))
);
assert_eq!(
add("(1 + 2) * (4 + 5)"),
Ok(("", node!(node!(1, '+', 2), '*', node!(4, '+', 5))))
);
}
}