//! Corresponds to `ast::expr`. use chumsky::prelude::*; use crate::ast::{BinOp, Expr, FuncDef, Lit, Space, TableConstr, TableDestr, Var}; use crate::span::HasSpan; use super::basic::{EParser, Error}; fn atom_paren( space: EParser, expr: EParser, ) -> impl Parser + Clone { just('(') .ignore_then(space.clone()) .then(expr) .then(space) .then_ignore(just(')')) .map_with_span(|((s0, inner), s1), span| Expr::Paren { s0, inner: inner.boxed(), s1, span, }) } pub fn atom( space: EParser, lit: EParser, var: EParser, table_constr: EParser, table_destr: EParser, func_def: EParser, expr: EParser, ) -> EParser { let lit = lit.map(Expr::Lit); let var = var.map(Expr::Var); let table_constr = table_constr.map(Expr::TableConstr); let table_destr = table_destr.map(Expr::TableDestr); let func_def = func_def.map(Expr::FuncDef); let paren = atom_paren(space, expr); lit.or(paren) .or(table_destr) .or(table_constr) .or(func_def) .or(var) .boxed() } fn left_assoc( space: EParser, op: impl Parser + 'static, over: impl Parser + Clone + 'static, ) -> EParser { let op_over = space .clone() .then(op) .then(space) .then(over.clone()) .map(|(((s0, op), s1), right)| (s0, op, s1, right)); over.then(op_over.repeated()) .foldl(|left, (s0, op, s1, right)| Expr::BinOp { span: left.span().join(right.span()), left: left.boxed(), s0, op, s1, right: right.boxed(), }) .boxed() } fn right_assoc( space: EParser, op: impl Parser + 'static, over: impl Parser + Clone + 'static, ) -> BoxedParser<'static, char, Expr, Error> { let over_op = over .clone() .then(space.clone()) .then(op) .then(space) .map(|(((left, s0), op), s1)| (left, s0, op, s1)); over_op .repeated() .then(over) .foldr(|(left, s0, op, s1), right| Expr::BinOp { span: left.span().join(right.span()), left: left.boxed(), s0, op, s1, right: right.boxed(), }) .boxed() } pub fn expr(space: EParser, prefixed: EParser) -> EParser { // * / % let op_prec_4 = (just('*').to(BinOp::Mul)) .or(just('/').to(BinOp::Div)) .or(just('%').to(BinOp::Mod)); // + - let op_prec_3 = (just('+').to(BinOp::Add)).or(just('-').to(BinOp::Sub)); // == != > >= < <= let op_prec_2 = (just("==").to(BinOp::Eq)) .or(just("!=").to(BinOp::Neq)) .or(just('>').to(BinOp::Gt)) .or(just(">=").to(BinOp::Ge)) .or(just('<').to(BinOp::Lt)) .or(just("<=").to(BinOp::Le)); // and let op_prec_1 = text::keyword("and").to(BinOp::And); // or let op_prec_0 = text::keyword("or").to(BinOp::Or); right_assoc( space.clone(), op_prec_0, right_assoc( space.clone(), op_prec_1, left_assoc( space.clone(), op_prec_2, left_assoc( space.clone(), op_prec_3, left_assoc(space, op_prec_4, prefixed), ), ), ), ) }