blob: 0d04c9bb0f683855780f6a1ee645addb473823eb [file] [log] [blame]
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};
// Elements of the abstract syntax tree (ast) that represents an expression.
#[derive(Debug)]
pub enum Expr {
// A number literal.
Num(i64),
// An identifier.
Iden(String),
// Arithmetic operations. Each have a left hand side (lhs) and a right hand side (rhs).
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
// The function call operation. Left is the expression the function is called on, right is the list of parameters.
Call(Box<Expr>, Vec<Expr>),
// The ternary operator, the expressions from left to right are: The condition, the true case, the false case.
Tern(Box<Expr>, Box<Expr>, Box<Expr>),
}
// Prefix operators.
enum PrefixOp {
Identity, // +
Negate, // -
}
// Postfix operators.
enum PostfixOp {
// The function call operator. In addition to its own representation "()" it carries additional information that we need to keep here.
// Specifically the vector of expressions that make up the parameters.
Call(Vec<Expr>), // ()
}
// Binary operators.
enum BinaryOp {
Addition, // +
Subtraction, // -
Multiplication, // *
Division, // /
// The ternary operator can contain a single expression.
Ternary(Expr), // ?:
}
// Parser for function calls.
fn function_call(i: &str) -> IResult<&str, PostfixOp> {
map(
delimited(
tag("("),
// Subexpressions are evaluated by recursing back into the expression parser.
separated_list0(tag(","), expression),
tag(")"),
),
|v: Vec<Expr>| PostfixOp::Call(v),
)
.parse(i)
}
// The ternary operator is actually just a binary operator that contains another expression. So it can be
// handled similarly to the function call operator except its in a binary position and can only contain
// a single expression.
//
// For example the expression "a<b ? a : b" is handled similarly to the function call operator, the
// "?" is treated like an opening bracket and the ":" is treated like a closing bracket.
//
// For the outer expression the result looks like "a<b ?: b". Where "?:" is a single operator. The
// subexpression is contained within the operator in the same way that the function call operator
// contains subexpressions.
fn ternary_operator(i: &str) -> IResult<&str, BinaryOp> {
map(delimited(tag("?"), expression, tag(":")), |e: Expr| {
BinaryOp::Ternary(e)
})
.parse(i)
}
// The actual expression parser .
fn expression(i: &str) -> IResult<&str, Expr> {
precedence(
alt((
unary_op(2, map(tag("+"), |_| PrefixOp::Identity)),
unary_op(2, map(tag("-"), |_| PrefixOp::Negate)),
)),
// Function calls are implemented as postfix unary operators.
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),
)),
),
// Ternary operators are just binary operators with a subexpression.
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 {
// The identity operator (prefix +) is ignored.
Prefix(Identity, e) => Ok(e),
// Unary minus gets evaluated to the same representation as a multiplication with -1.
Prefix(Negate, e) => Ok(Expr::Mul(Expr::Num(-1).into(), e.into())),
// The list of parameters are taken from the operator and placed into the ast.
Postfix(e, Call(p)) => Ok(Expr::Call(e.into(), p)),
// Meaning is assigned to the expressions of the ternary operator during evaluation.
// The lhs becomes the condition, the contained expression is the true case, rhs the false case.
Binary(lhs, Ternary(e), rhs) => Ok(Expr::Tern(lhs.into(), e.into(), rhs.into())),
// Raw operators get turned into their respective ast nodes.
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)))"
)
))
);
}