use super::tokens::{token, Token, TokenKind as tk, TokenSpan}; use super::{IResult, Span}; use nom::{ branch::alt, combinator::{map, opt}, multi::separated_list0, sequence::{delimited, preceded, separated_pair, tuple}, }; #[derive(Debug, Clone)] pub struct Call<'a> { pub name: Span<'a>, pub args: Vec>, } #[derive(Debug, Clone)] pub enum BinOpKind { And, Or, Xor, } #[derive(Debug, Clone)] pub enum UnOpKind { Not, BitNot, } #[derive(Debug, Clone)] pub struct BinOp<'a> { pub a: Expression<'a>, pub b: Expression<'a>, pub kind: BinOpKind, } #[derive(Debug, Clone)] pub struct UnOp<'a> { pub a: Expression<'a>, pub kind: UnOpKind, } #[derive(Debug, Clone)] pub enum Expression<'a> { Path(&'a str), Literal(Token<'a>), UnOp(Box>), BinOp(Box>), Call(Box>), } /// expressions that can't be subdivided further fn atom(input: TokenSpan) -> IResult { alt(( map(call_item, |call| Expression::Call(Box::new(call))), map(token(tk::Ident), |it| { Expression::Path(it.span().fragment()) }), map(token(tk::Number), Expression::Literal), delimited(token(tk::LParen), expression, token(tk::RParen)), ))(input) } fn unop_kind(input: TokenSpan) -> IResult { alt(( map(token(tk::Not), |_| UnOpKind::Not), map(token(tk::BitNot), |_| UnOpKind::BitNot), ))(input) } /// unary expressions e.g `~expr`, `!expr` fn unary(input: TokenSpan) -> IResult { alt(( map(tuple((unop_kind, unary)), |(kind, expr)| { Expression::UnOp(Box::new(UnOp { a: expr, kind })) }), atom, ))(input) } fn bitop_kind(input: TokenSpan) -> IResult { alt(( map(token(tk::BitXor), |_| BinOpKind::Xor), map(token(tk::BitOr), |_| BinOpKind::Or), map(token(tk::BitAnd), |_| BinOpKind::And), ))(input) } /// bit and, or, xor e.g. a ^ b & c /// TODO: make precedence rules for bit ops fn bitop(input: TokenSpan) -> IResult { // special care is given to avoid parsing `unary` twice, as that would // make this parser quadratic map( tuple((unary, opt(tuple((bitop_kind, bitop))))), |(a, rest)| { if let Some((kind, b)) = rest { Expression::BinOp(Box::new(BinOp { a, b, kind })) } else { a } }, )(input) } pub fn call_item(input: TokenSpan) -> IResult { map( tuple(( token(tk::Ident), delimited( token(tk::LParen), separated_list0(token(tk::Comma), expression), token(tk::RParen), ), )), |(name, args)| Call { name: name.span(), args, }, )(input) } /// parser combinators can not parse left-recursive grammars. To work around this, we split /// expressions into a recursive and non-recursive portion. /// Parsers reachable from this point must call expression_nonrecurse instead pub fn expression(input: TokenSpan) -> IResult { bitop(input) } #[cfg(test)] mod test { use super::*; use crate::parser::tokens::{lex, Token, TokenSpan}; use crate::parser::Span; use nom::combinator::all_consuming; fn tok(input: &'static str) -> Vec { let input = Span::from(input); lex(input).unwrap().1 } fn fullexpr(input: TokenSpan) -> IResult { all_consuming(expression)(input) } #[test] fn test_atoms() { fullexpr(TokenSpan::new(&tok("a"))).unwrap(); fullexpr(TokenSpan::new(&tok("(a)"))).unwrap(); fullexpr(TokenSpan::new(&tok( "(((((((((((((((((((a)))))))))))))))))))", ))) .unwrap(); } #[test] fn test_unary() { fullexpr(TokenSpan::new(&tok("asdf"))).unwrap(); fullexpr(TokenSpan::new(&tok("~(asdf)"))).unwrap(); fullexpr(TokenSpan::new(&tok("!asdf"))).unwrap(); fullexpr(TokenSpan::new(&tok("~!(asdf)"))).unwrap(); } #[test] fn test_bitop() { fullexpr(TokenSpan::new(&tok("a | b"))).unwrap(); fullexpr(TokenSpan::new(&tok("a ^ b"))).unwrap(); fullexpr(TokenSpan::new(&tok("~(a ^ b)"))).unwrap(); fullexpr(TokenSpan::new(&tok("a ^ !b"))).unwrap(); fullexpr(TokenSpan::new(&tok("a & !b & c"))).unwrap(); } #[test] fn test_call() { fullexpr(TokenSpan::new(&tok("a()"))).unwrap(); } }