use cas_error::Error;
use crate::{
parser::{
ast::{binary::Binary, expr::{Expr, Primary}},
error::NonFatal,
fmt::Latex,
token::op::{Associativity, UnaryOp},
Parser,
ParseResult,
},
return_if_ok,
};
use std::{fmt, ops::Range};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
fn try_parse_unary_op(input: &mut Parser, associativity: Associativity) -> Result<UnaryOp, Vec<Error>> {
input.try_parse_then::<UnaryOp, _>(|op, input| {
if op.associativity() == associativity {
ParseResult::Ok(())
} else {
ParseResult::Unrecoverable(vec![input.error(NonFatal)])
}
}).forward_errors(&mut Vec::new())
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Unary {
pub operand: Box<Expr>,
pub op: UnaryOp,
pub span: Range<usize>,
}
impl Unary {
pub fn span(&self) -> Range<usize> {
self.span.clone()
}
pub fn parse_right(input: &mut Parser, recoverable_errors: &mut Vec<Error>) -> Result<Self, Vec<Error>> {
let op = try_parse_unary_op(input, Associativity::Right)?;
let op_precedence = op.precedence();
let start_span = op.span.start;
let operand = {
let lhs = Unary::parse_or_lower(input, recoverable_errors)?;
Binary::parse_expr(input, recoverable_errors, lhs, op_precedence)?.0
};
let end_span = operand.span().end;
Ok(Self {
operand: Box::new(operand),
op,
span: start_span..end_span,
})
}
pub fn parse_left_or_operand(input: &mut Parser, recoverable_errors: &mut Vec<Error>) -> Result<Expr, Vec<Error>> {
let operand = input.try_parse::<Primary>().forward_errors(recoverable_errors)?;
let start_span = operand.span().start;
let op = match try_parse_unary_op(input, Associativity::Left) {
Ok(op) => op,
Err(_) => return Ok(operand.into()),
};
let mut result = Self {
operand: Box::new(operand.into()),
op,
span: start_span..input.prev_token().unwrap().span.end,
};
while let Ok(next_op) = try_parse_unary_op(input, Associativity::Left) {
result = Self {
operand: Box::new(Expr::Unary(result)),
op: next_op,
span: start_span..input.prev_token().unwrap().span.end,
};
}
Ok(Expr::Unary(result))
}
pub fn parse_or_lower(
input: &mut Parser,
recoverable_errors: &mut Vec<Error>
) -> Result<Expr, Vec<Error>> {
let _ = return_if_ok!(Self::parse_right(input, recoverable_errors).map(Expr::Unary));
Self::parse_left_or_operand(input, recoverable_errors)
}
}
impl std::fmt::Display for Unary {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.op.associativity() {
Associativity::Left => {
self.operand.fmt(f)?;
self.op.fmt(f)
},
Associativity::Right => {
self.op.fmt(f)?;
self.operand.fmt(f)
},
}
}
}
impl Latex for Unary {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.op.associativity() {
Associativity::Left => {
self.operand.fmt_latex(f)?;
self.op.fmt_latex(f)
},
Associativity::Right => {
self.op.fmt_latex(f)?;
self.operand.fmt_latex(f)
},
}
}
}