use std::borrow::Cow; use std::collections::HashSet; 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::error_position; use nom::multi::{fold_many0, many0, separated_list0}; use nom::sequence::{pair, preceded, terminated, tuple}; use super::{ char_lit, identifier, not_ws, num_lit, path_or_identifier, str_lit, ws, Level, PathOrIdentifier, }; use crate::{ErrorContext, ParseResult}; macro_rules! expr_prec_layer { ( $name:ident, $inner:ident, $op:expr ) => { fn $name(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; let (i, left) = Self::$inner(i, level)?; let (i, right) = many0(pair( ws(tag($op)), |i| Self::$inner(i, level), ))(i)?; Ok(( i, right.into_iter().fold(left, |left, (op, right)| { Self::BinOp(op, Box::new(left), Box::new(right)) }), )) } }; ( $name:ident, $inner:ident, $( $op:expr ),+ ) => { fn $name(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; let (i, left) = Self::$inner(i, level)?; let (i, right) = many0(pair( ws(alt(($( tag($op) ),+,))), |i| Self::$inner(i, level), ))(i)?; Ok(( i, right.into_iter().fold(left, |left, (op, right)| { Self::BinOp(op, Box::new(left), Box::new(right)) }), )) } } } #[derive(Clone, Debug, PartialEq)] pub enum Expr<'a> { BoolLit(&'a str), NumLit(&'a str), StrLit(&'a str), CharLit(&'a str), Var(&'a str), Path(Vec<&'a str>), Array(Vec>), Attr(Box>, &'a str), Index(Box>, Box>), Filter(&'a str, Vec>), NamedArgument(&'a str, Box>), Unary(&'a str, Box>), BinOp(&'a str, Box>, Box>), Range(&'a str, Option>>, Option>>), Group(Box>), Tuple(Vec>), Call(Box>, Vec>), RustMacro(Vec<&'a str>, &'a str), Try(Box>), } impl<'a> Expr<'a> { pub(super) fn arguments( i: &'a str, level: Level, is_template_macro: bool, ) -> ParseResult<'a, Vec> { let (_, level) = level.nest(i)?; let mut named_arguments = HashSet::new(); let start = i; preceded( ws(char('(')), cut(terminated( separated_list0( char(','), ws(move |i| { // Needed to prevent borrowing it twice between this closure and the one // calling `Self::named_arguments`. let named_arguments = &mut named_arguments; let has_named_arguments = !named_arguments.is_empty(); let (i, expr) = alt(( move |i| { Self::named_argument( i, level, named_arguments, start, is_template_macro, ) }, move |i| Self::parse(i, level), ))(i)?; if has_named_arguments && !matches!(expr, Self::NamedArgument(_, _)) { Err(nom::Err::Failure(ErrorContext { input: start, message: Some(Cow::Borrowed( "named arguments must always be passed last", )), })) } else { Ok((i, expr)) } }), ), tuple((opt(ws(char(','))), char(')'))), )), )(i) } fn named_argument( i: &'a str, level: Level, named_arguments: &mut HashSet<&'a str>, start: &'a str, is_template_macro: bool, ) -> ParseResult<'a, Self> { if !is_template_macro { // If this is not a template macro, we don't want to parse named arguments so // we instead return an error which will allow to continue the parsing. return Err(nom::Err::Error(error_position!(i, ErrorKind::Alt))); } let (_, level) = level.nest(i)?; let (i, (argument, _, value)) = tuple((identifier, ws(char('=')), move |i| Self::parse(i, level)))(i)?; if named_arguments.insert(argument) { Ok((i, Self::NamedArgument(argument, Box::new(value)))) } else { Err(nom::Err::Failure(ErrorContext { input: start, message: Some(Cow::Owned(format!( "named argument `{argument}` was passed more than once" ))), })) } } pub(super) fn parse(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; let range_right = move |i| { pair( ws(alt((tag("..="), tag("..")))), opt(move |i| Self::or(i, level)), )(i) }; alt(( map(range_right, |(op, right)| { Self::Range(op, None, right.map(Box::new)) }), map( pair(move |i| Self::or(i, level), opt(range_right)), |(left, right)| match right { Some((op, right)) => Self::Range(op, Some(Box::new(left)), right.map(Box::new)), None => left, }, ), ))(i) } expr_prec_layer!(or, and, "||"); expr_prec_layer!(and, compare, "&&"); expr_prec_layer!(compare, bor, "==", "!=", ">=", ">", "<=", "<"); expr_prec_layer!(bor, bxor, "|"); expr_prec_layer!(bxor, band, "^"); expr_prec_layer!(band, shifts, "&"); expr_prec_layer!(shifts, addsub, ">>", "<<"); expr_prec_layer!(addsub, muldivmod, "+", "-"); expr_prec_layer!(muldivmod, filtered, "*", "/", "%"); fn filtered(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; #[allow(clippy::type_complexity)] fn filter(i: &str, level: Level) -> ParseResult<'_, (&str, Option>>)> { let (i, (_, fname, args)) = tuple(( char('|'), ws(identifier), opt(|i| Expr::arguments(i, level, false)), ))(i)?; Ok((i, (fname, args))) } let (i, (obj, filters)) = tuple((|i| Self::prefix(i, level), many0(|i| filter(i, level))))(i)?; let mut res = obj; for (fname, args) in filters { res = Self::Filter(fname, { let mut args = match args { Some(inner) => inner, None => Vec::new(), }; args.insert(0, res); args }); } Ok((i, res)) } fn prefix(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; let (i, (ops, mut expr)) = pair(many0(ws(alt((tag("!"), tag("-"))))), |i| { Suffix::parse(i, level) })(i)?; for op in ops.iter().rev() { expr = Self::Unary(op, Box::new(expr)); } Ok((i, expr)) } fn single(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; alt(( Self::num, Self::str, Self::char, Self::path_var_bool, move |i| Self::array(i, level), move |i| Self::group(i, level), ))(i) } fn group(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; let (i, expr) = preceded(ws(char('(')), opt(|i| Self::parse(i, level)))(i)?; let expr = match expr { Some(expr) => expr, None => { let (i, _) = char(')')(i)?; return Ok((i, Self::Tuple(vec![]))); } }; let (i, comma) = ws(opt(peek(char(','))))(i)?; if comma.is_none() { let (i, _) = char(')')(i)?; return Ok((i, Self::Group(Box::new(expr)))); } let mut exprs = vec![expr]; let (i, _) = fold_many0( preceded(char(','), ws(|i| Self::parse(i, level))), || (), |_, expr| { exprs.push(expr); }, )(i)?; let (i, _) = pair(ws(opt(char(','))), char(')'))(i)?; Ok((i, Self::Tuple(exprs))) } fn array(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; preceded( ws(char('[')), cut(terminated( map( separated_list0(char(','), ws(move |i| Self::parse(i, level))), Self::Array, ), char(']'), )), )(i) } fn path_var_bool(i: &'a str) -> ParseResult<'a, Self> { map(path_or_identifier, |v| match v { PathOrIdentifier::Path(v) => Self::Path(v), PathOrIdentifier::Identifier(v @ "true") => Self::BoolLit(v), PathOrIdentifier::Identifier(v @ "false") => Self::BoolLit(v), PathOrIdentifier::Identifier(v) => Self::Var(v), })(i) } fn str(i: &'a str) -> ParseResult<'a, Self> { map(str_lit, Self::StrLit)(i) } fn num(i: &'a str) -> ParseResult<'a, Self> { map(num_lit, Self::NumLit)(i) } fn char(i: &'a str) -> ParseResult<'a, Self> { map(char_lit, Self::CharLit)(i) } } enum Suffix<'a> { Attr(&'a str), Index(Expr<'a>), Call(Vec>), // The value is the arguments of the macro call. MacroCall(&'a str), Try, } impl<'a> Suffix<'a> { fn parse(i: &'a str, level: Level) -> ParseResult<'a, Expr<'a>> { let (_, level) = level.nest(i)?; let (mut i, mut expr) = Expr::single(i, level)?; loop { let (j, suffix) = opt(alt(( Self::attr, |i| Self::index(i, level), |i| Self::call(i, level), Self::r#try, Self::r#macro, )))(i)?; match suffix { Some(Self::Attr(attr)) => expr = Expr::Attr(expr.into(), attr), Some(Self::Index(index)) => expr = Expr::Index(expr.into(), index.into()), Some(Self::Call(args)) => expr = Expr::Call(expr.into(), args), Some(Self::Try) => expr = Expr::Try(expr.into()), Some(Self::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)) } fn r#macro(i: &'a str) -> ParseResult<'a, Self> { fn nested_parenthesis(input: &str) -> ParseResult<'_, ()> { let mut nested = 0; let mut last = 0; let mut in_str = false; let mut escaped = false; for (i, c) in input.char_indices() { if !(c == '(' || c == ')') || !in_str { match c { '(' => nested += 1, ')' => { if nested == 0 { last = i; break; } nested -= 1; } '"' => { if in_str { if !escaped { in_str = false; } } else { in_str = true; } } '\\' => { escaped = !escaped; } _ => (), } } if escaped && c != '\\' { escaped = false; } } if nested == 0 { Ok((&input[last..], ())) } else { Err(nom::Err::Error(error_position!( input, ErrorKind::SeparatedNonEmptyList ))) } } preceded( pair(ws(char('!')), char('(')), cut(terminated( map(recognize(nested_parenthesis), Self::MacroCall), char(')'), )), )(i) } fn attr(i: &'a str) -> ParseResult<'a, Self> { map( preceded( ws(pair(char('.'), not(char('.')))), cut(alt((num_lit, identifier))), ), Self::Attr, )(i) } fn index(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; map( preceded( ws(char('[')), cut(terminated(ws(move |i| Expr::parse(i, level)), char(']'))), ), Self::Index, )(i) } fn call(i: &'a str, level: Level) -> ParseResult<'a, Self> { let (_, level) = level.nest(i)?; map(move |i| Expr::arguments(i, level, false), Self::Call)(i) } fn r#try(i: &'a str) -> ParseResult<'a, Self> { map(preceded(take_till(not_ws), char('?')), |_| Self::Try)(i) } }