cas_parser/parser/ast/
unary.rs

1use cas_error::Error;
2use crate::{
3    parser::{
4        ast::{binary::Binary, expr::{Expr, Primary}},
5        error::NonFatal,
6        fmt::Latex,
7        token::op::{Associativity, UnaryOp},
8        Parser,
9        ParseResult,
10    },
11    return_if_ok,
12};
13use std::{fmt, ops::Range};
14
15#[cfg(feature = "serde")]
16use serde::{Deserialize, Serialize};
17
18/// Attempt to parse a unary operator with the correct associativity. Returns a non-fatal error if
19/// the operator is not of the correct associativity.
20fn try_parse_unary_op(input: &mut Parser, associativity: Associativity) -> Result<UnaryOp, Vec<Error>> {
21    input.try_parse_then::<UnaryOp, _>(|op, input| {
22        if op.associativity() == associativity {
23            ParseResult::Ok(())
24        } else {
25            ParseResult::Unrecoverable(vec![input.error(NonFatal)])
26        }
27    }).forward_errors(&mut Vec::new())
28}
29
30/// A unary expression, such as `2!`. Unary expressions can include nested expressions.
31///
32/// [`Parse`]: crate::parser::Parse
33#[derive(Debug, Clone, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35pub struct Unary {
36    /// The operand of the unary expression (left or right, depending on the associativity).
37    pub operand: Box<Expr>,
38
39    /// The operator of the unary expression.
40    pub op: UnaryOp,
41
42    /// The region of the source code that this unary expression was parsed from.
43    pub span: Range<usize>,
44}
45
46impl Unary {
47    /// Returns the span of the unary expression.
48    pub fn span(&self) -> Range<usize> {
49        self.span.clone()
50    }
51
52    /// Parse a unary expression with right-associativity.
53    pub fn parse_right(input: &mut Parser, recoverable_errors: &mut Vec<Error>) -> Result<Self, Vec<Error>> {
54        let op = try_parse_unary_op(input, Associativity::Right)?;
55        let op_precedence = op.precedence();
56        let start_span = op.span.start;
57        let operand = {
58            let lhs = Unary::parse_or_lower(input, recoverable_errors)?;
59            Binary::parse_expr(input, recoverable_errors, lhs, op_precedence)?.0
60        };
61        let end_span = operand.span().end;
62        Ok(Self {
63            operand: Box::new(operand),
64            op,
65            span: start_span..end_span,
66        })
67    }
68
69    /// Parse a unary expression with left-associativity.
70    ///
71    /// By the nature of left-associative operators, we must parse the operand first. This can
72    /// result in enormous backtracking if the operator is not present. To avoid this, this
73    /// function returns the parsed operand as an [`Primary`] if it does determine that there
74    /// is no operator present.
75    pub fn parse_left_or_operand(input: &mut Parser, recoverable_errors: &mut Vec<Error>) -> Result<Expr, Vec<Error>> {
76        let operand = input.try_parse::<Primary>().forward_errors(recoverable_errors)?;
77        let start_span = operand.span().start;
78
79        // one operator must be present
80        let op = match try_parse_unary_op(input, Associativity::Left) {
81            Ok(op) => op,
82            Err(_) => return Ok(operand.into()),
83        };
84        let mut result = Self {
85            operand: Box::new(operand.into()),
86            op,
87            span: start_span..input.prev_token().unwrap().span.end,
88        };
89
90        // iteratively find any other left-associative operators
91        while let Ok(next_op) = try_parse_unary_op(input, Associativity::Left) {
92            result = Self {
93                operand: Box::new(Expr::Unary(result)),
94                op: next_op,
95                span: start_span..input.prev_token().unwrap().span.end,
96            };
97        }
98
99        Ok(Expr::Unary(result))
100    }
101
102    /// Parses a unary expression, or lower precedence expressions.
103    pub fn parse_or_lower(
104        input: &mut Parser,
105        recoverable_errors: &mut Vec<Error>
106    ) -> Result<Expr, Vec<Error>> {
107        let _ = return_if_ok!(Self::parse_right(input, recoverable_errors).map(Expr::Unary));
108        Self::parse_left_or_operand(input, recoverable_errors)
109    }
110}
111
112impl std::fmt::Display for Unary {
113    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
114        match self.op.associativity() {
115            Associativity::Left => {
116                self.operand.fmt(f)?;
117                self.op.fmt(f)
118            },
119            Associativity::Right => {
120                self.op.fmt(f)?;
121                self.operand.fmt(f)
122            },
123        }
124    }
125}
126
127impl Latex for Unary {
128    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        match self.op.associativity() {
130            Associativity::Left => {
131                self.operand.fmt_latex(f)?;
132                self.op.fmt_latex(f)
133            },
134            Associativity::Right => {
135                self.op.fmt_latex(f)?;
136                self.operand.fmt_latex(f)
137            },
138        }
139    }
140}