use std::str;
use nom::branch::alt;
use nom::bytes::complete::{tag, take_till};
use nom::character::complete::char;
use nom::combinator::{cut, map, not, opt, peek, recognize};
use nom::error::ErrorKind;
use nom::multi::{fold_many0, many0, separated_list0, separated_list1};
use nom::sequence::{delimited, pair, preceded, terminated, tuple};
use nom::{error_position, IResult};
use super::{
bool_lit, char_lit, identifier, nested_parenthesis, not_ws, num_lit, path, str_lit, ws,
};
#[derive(Debug, PartialEq)]
pub(crate) enum Expr<'a> {
BoolLit(&'a str),
NumLit(&'a str),
StrLit(&'a str),
CharLit(&'a str),
Var(&'a str),
Path(Vec<&'a str>),
Array(Vec<Expr<'a>>),
Attr(Box<Expr<'a>>, &'a str),
Index(Box<Expr<'a>>, Box<Expr<'a>>),
Filter(&'a str, Vec<Expr<'a>>),
Unary(&'a str, Box<Expr<'a>>),
BinOp(&'a str, Box<Expr<'a>>, Box<Expr<'a>>),
Range(&'a str, Option<Box<Expr<'a>>>, Option<Box<Expr<'a>>>),
Group(Box<Expr<'a>>),
Tuple(Vec<Expr<'a>>),
Call(Box<Expr<'a>>, Vec<Expr<'a>>),
RustMacro(Vec<&'a str>, &'a str),
Try(Box<Expr<'a>>),
}
impl Expr<'_> {
pub(super) fn parse(i: &str) -> IResult<&str, Expr<'_>> {
expr_any(i)
}
pub(super) fn parse_arguments(i: &str) -> IResult<&str, Vec<Expr<'_>>> {
arguments(i)
}
}
fn expr_bool_lit(i: &str) -> IResult<&str, Expr<'_>> {
map(bool_lit, Expr::BoolLit)(i)
}
fn expr_num_lit(i: &str) -> IResult<&str, Expr<'_>> {
map(num_lit, Expr::NumLit)(i)
}
fn expr_array_lit(i: &str) -> IResult<&str, Expr<'_>> {
delimited(
ws(char('[')),
map(separated_list1(ws(char(',')), expr_any), Expr::Array),
ws(char(']')),
)(i)
}
fn expr_str_lit(i: &str) -> IResult<&str, Expr<'_>> {
map(str_lit, Expr::StrLit)(i)
}
fn expr_char_lit(i: &str) -> IResult<&str, Expr<'_>> {
map(char_lit, Expr::CharLit)(i)
}
fn expr_var(i: &str) -> IResult<&str, Expr<'_>> {
map(identifier, Expr::Var)(i)
}
fn expr_path(i: &str) -> IResult<&str, Expr<'_>> {
let (i, path) = path(i)?;
Ok((i, Expr::Path(path)))
}
fn expr_group(i: &str) -> IResult<&str, Expr<'_>> {
let (i, expr) = preceded(ws(char('(')), opt(expr_any))(i)?;
let expr = match expr {
Some(expr) => expr,
None => {
let (i, _) = char(')')(i)?;
return Ok((i, Expr::Tuple(vec![])));
}
};
let (i, comma) = ws(opt(peek(char(','))))(i)?;
if comma.is_none() {
let (i, _) = char(')')(i)?;
return Ok((i, Expr::Group(Box::new(expr))));
}
let mut exprs = vec![expr];
let (i, _) = fold_many0(
preceded(char(','), ws(expr_any)),
|| (),
|_, expr| {
exprs.push(expr);
},
)(i)?;
let (i, _) = pair(ws(opt(char(','))), char(')'))(i)?;
Ok((i, Expr::Tuple(exprs)))
}
fn expr_single(i: &str) -> IResult<&str, Expr<'_>> {
alt((
expr_bool_lit,
expr_num_lit,
expr_str_lit,
expr_char_lit,
expr_path,
expr_array_lit,
expr_var,
expr_group,
))(i)
}
enum Suffix<'a> {
Attr(&'a str),
Index(Expr<'a>),
Call(Vec<Expr<'a>>),
// The value is the arguments of the macro call.
MacroCall(&'a str),
Try,
}
fn expr_attr(i: &str) -> IResult<&str, Suffix<'_>> {
map(
preceded(
ws(pair(char('.'), not(char('.')))),
cut(alt((num_lit, identifier))),
),
Suffix::Attr,
)(i)
}
fn expr_index(i: &str) -> IResult<&str, Suffix<'_>> {
map(
preceded(ws(char('[')), cut(terminated(expr_any, ws(char(']'))))),
Suffix::Index,
)(i)
}
fn expr_call(i: &str) -> IResult<&str, Suffix<'_>> {
map(arguments, Suffix::Call)(i)
}
fn expr_macro(i: &str) -> IResult<&str, Suffix<'_>> {
preceded(
pair(ws(char('!')), char('(')),
cut(terminated(
map(recognize(nested_parenthesis), Suffix::MacroCall),
char(')'),
)),
)(i)
}
fn expr_try(i: &str) -> IResult<&str, Suffix<'_>> {
map(preceded(take_till(not_ws), char('?')), |_| Suffix::Try)(i)
}
fn filter(i: &str) -> IResult<&str, (&str, Option<Vec<Expr<'_>>>)> {
let (i, (_, fname, args)) = tuple((char('|'), ws(identifier), opt(arguments)))(i)?;
Ok((i, (fname, args)))
}
fn expr_filtered(i: &str) -> IResult<&str, Expr<'_>> {
let (i, (obj, filters)) = tuple((expr_prefix, many0(filter)))(i)?;
let mut res = obj;
for (fname, args) in filters {
res = Expr::Filter(fname, {
let mut args = match args {
Some(inner) => inner,
None => Vec::new(),
};
args.insert(0, res);
args
});
}
Ok((i, res))
}
fn expr_prefix(i: &str) -> IResult<&str, Expr<'_>> {
let (i, (ops, mut expr)) = pair(many0(ws(alt((tag("!"), tag("-"))))), expr_suffix)(i)?;
for op in ops.iter().rev() {
expr = Expr::Unary(op, Box::new(expr));
}
Ok((i, expr))
}
fn expr_suffix(i: &str) -> IResult<&str, Expr<'_>> {
let (mut i, mut expr) = expr_single(i)?;
loop {
let (j, suffix) = opt(alt((
expr_attr, expr_index, expr_call, expr_try, expr_macro,
)))(i)?;
match suffix {
Some(Suffix::Attr(attr)) => expr = Expr::Attr(expr.into(), attr),
Some(Suffix::Index(index)) => expr = Expr::Index(expr.into(), index.into()),
Some(Suffix::Call(args)) => expr = Expr::Call(expr.into(), args),
Some(Suffix::Try) => expr = Expr::Try(expr.into()),
Some(Suffix::MacroCall(args)) => match expr {
Expr::Path(path) => expr = Expr::RustMacro(path, args),
Expr::Var(name) => expr = Expr::RustMacro(vec![name], args),
_ => return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))),
},
None => break,
}
i = j;
}
Ok((i, expr))
}
macro_rules! expr_prec_layer {
( $name:ident, $inner:ident, $op:expr ) => {
fn $name(i: &str) -> IResult<&str, Expr<'_>> {
let (i, left) = $inner(i)?;
let (i, right) = many0(pair(
ws(tag($op)),
$inner,
))(i)?;
Ok((
i,
right.into_iter().fold(left, |left, (op, right)| {
Expr::BinOp(op, Box::new(left), Box::new(right))
}),
))
}
};
( $name:ident, $inner:ident, $( $op:expr ),+ ) => {
fn $name(i: &str) -> IResult<&str, Expr<'_>> {
let (i, left) = $inner(i)?;
let (i, right) = many0(pair(
ws(alt(($( tag($op) ),+,))),
$inner,
))(i)?;
Ok((
i,
right.into_iter().fold(left, |left, (op, right)| {
Expr::BinOp(op, Box::new(left), Box::new(right))
}),
))
}
}
}
expr_prec_layer!(expr_muldivmod, expr_filtered, "*", "/", "%");
expr_prec_layer!(expr_addsub, expr_muldivmod, "+", "-");
expr_prec_layer!(expr_shifts, expr_addsub, ">>", "<<");
expr_prec_layer!(expr_band, expr_shifts, "&");
expr_prec_layer!(expr_bxor, expr_band, "^");
expr_prec_layer!(expr_bor, expr_bxor, "|");
expr_prec_layer!(expr_compare, expr_bor, "==", "!=", ">=", ">", "<=", "<");
expr_prec_layer!(expr_and, expr_compare, "&&");
expr_prec_layer!(expr_or, expr_and, "||");
fn expr_any(i: &str) -> IResult<&str, Expr<'_>> {
let range_right = |i| pair(ws(alt((tag("..="), tag("..")))), opt(expr_or))(i);
alt((
map(range_right, |(op, right)| {
Expr::Range(op, None, right.map(Box::new))
}),
map(
pair(expr_or, opt(range_right)),
|(left, right)| match right {
Some((op, right)) => Expr::Range(op, Some(Box::new(left)), right.map(Box::new)),
None => left,
},
),
))(i)
}
fn arguments(i: &str) -> IResult<&str, Vec<Expr<'_>>> {
delimited(
ws(char('(')),
separated_list0(char(','), ws(expr_any)),
ws(char(')')),
)(i)
}