cas_parser/parser/ast/
unary.rs

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