use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{alphanumeric1 as alphanumeric, digit1 as digit},
combinator::{map, map_res},
multi::separated_list0,
sequence::delimited,
IResult, Parser,
};
use nom_language::precedence::{binary_op, precedence, unary_op, Assoc, Operation};
#[derive(Debug)]
pub enum Expr {
Num(i64),
Iden(String),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
Call(Box<Expr>, Vec<Expr>),
Tern(Box<Expr>, Box<Expr>, Box<Expr>),
}
enum PrefixOp {
Identity, Negate, }
enum PostfixOp {
Call(Vec<Expr>), }
enum BinaryOp {
Addition, Subtraction, Multiplication, Division, Ternary(Expr), }
fn function_call(i: &str) -> IResult<&str, PostfixOp> {
map(
delimited(
tag("("),
separated_list0(tag(","), expression),
tag(")"),
),
|v: Vec<Expr>| PostfixOp::Call(v),
)
.parse(i)
}
fn ternary_operator(i: &str) -> IResult<&str, BinaryOp> {
map(delimited(tag("?"), expression, tag(":")), |e: Expr| {
BinaryOp::Ternary(e)
})
.parse(i)
}
fn expression(i: &str) -> IResult<&str, Expr> {
precedence(
alt((
unary_op(2, map(tag("+"), |_| PrefixOp::Identity)),
unary_op(2, map(tag("-"), |_| PrefixOp::Negate)),
)),
unary_op(1, function_call),
alt((
binary_op(
3,
Assoc::Left,
alt((
map(tag("*"), |_| BinaryOp::Multiplication),
map(tag("/"), |_| BinaryOp::Division),
)),
),
binary_op(
4,
Assoc::Left,
alt((
map(tag("+"), |_| BinaryOp::Addition),
map(tag("-"), |_| BinaryOp::Subtraction),
)),
),
binary_op(5, Assoc::Right, ternary_operator),
)),
alt((
map_res(digit, |s: &str| match s.parse::<i64>() {
Ok(s) => Ok(Expr::Num(s)),
Err(e) => Err(e),
}),
map(alphanumeric, |s: &str| Expr::Iden(s.to_string())),
delimited(tag("("), expression, tag(")")),
)),
|op: Operation<PrefixOp, PostfixOp, BinaryOp, Expr>| -> Result<Expr, ()> {
use nom_language::precedence::Operation::*;
use BinaryOp::*;
use PostfixOp::*;
use PrefixOp::*;
match op {
Prefix(Identity, e) => Ok(e),
Prefix(Negate, e) => Ok(Expr::Mul(Expr::Num(-1).into(), e.into())),
Postfix(e, Call(p)) => Ok(Expr::Call(e.into(), p)),
Binary(lhs, Ternary(e), rhs) => Ok(Expr::Tern(lhs.into(), e.into(), rhs.into())),
Binary(lhs, Multiplication, rhs) => Ok(Expr::Mul(lhs.into(), rhs.into())),
Binary(lhs, Division, rhs) => Ok(Expr::Div(lhs.into(), rhs.into())),
Binary(lhs, Addition, rhs) => Ok(Expr::Add(lhs.into(), rhs.into())),
Binary(lhs, Subtraction, rhs) => Ok(Expr::Sub(lhs.into(), rhs.into())),
}
},
)(i)
}
#[test]
fn expression_test() {
assert_eq!(
expression("-2*max(2,3)-2").map(|(i, x)| (i, format!("{:?}", x))),
Ok((
"",
String::from("Sub(Mul(Mul(Num(-1), Num(2)), Call(Iden(\"max\"), [Num(2), Num(3)])), Num(2))")
))
);
assert_eq!(
expression("a?2+c:-2*2").map(|(i, x)| (i, format!("{:?}", x))),
Ok((
"",
String::from(
"Tern(Iden(\"a\"), Add(Num(2), Iden(\"c\")), Mul(Mul(Num(-1), Num(2)), Num(2)))"
)
))
);
}