cas_parser/parser/ast/
if_expr.rs

1use cas_error::Error;
2use crate::parser::{
3    ast::expr::{Atom, Expr},
4    error::MissingIfBranch,
5    fmt::Latex,
6    garbage::Garbage,
7    keyword::{Else, If as IfToken},
8    Parse,
9    Parser,
10};
11use std::{fmt, ops::Range};
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16/// An `if` expression, such as `if true 1 else 2`.
17#[derive(Debug, Clone, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19pub struct If {
20    /// The condition of the `if` expression.
21    pub condition: Box<Expr>,
22
23    /// The expression to evaluate if the condition is true.
24    pub then_expr: Box<Expr>,
25
26    /// The expression to evaluate if the condition is false.
27    pub else_expr: Option<Box<Expr>>,
28
29    /// The region of the source code that this literal was parsed from.
30    pub span: Range<usize>,
31
32    /// The span of the `if` keyword.
33    pub if_span: Range<usize>,
34
35    /// The span of the `else` keyword.
36    pub else_span: Option<Range<usize>>,
37}
38
39impl If {
40    /// Returns the span of the `if` expression.
41    pub fn span(&self) -> Range<usize> {
42        self.span.clone()
43    }
44}
45
46impl<'source> Parse<'source> for If {
47    fn std_parse(
48        input: &mut Parser<'source>,
49        recoverable_errors: &mut Vec<Error>
50    ) -> Result<Self, Vec<Error>> {
51        let if_token = input.try_parse::<IfToken>().forward_errors(recoverable_errors)?;
52        let condition = input.try_parse().forward_errors(recoverable_errors)?;
53        let then_expr = input.try_parse_with_state::<_, Atom>(|input| {
54            input.allow_then = true;
55        })
56            .map(Expr::from)
57            .forward_errors(recoverable_errors)?;
58        let (else_token, else_expr) = 'else_branch: {
59            let Ok(else_token) = input.try_parse::<Else>().forward_errors(recoverable_errors) else {
60                break 'else_branch (None, None);
61            };
62            input.try_parse::<Expr>()
63                .forward_errors(recoverable_errors)
64                .map(|expr| (Some(else_token), Some(expr)))
65                .unwrap_or_else(|_| {
66                    recoverable_errors.push(Error::new(
67                        vec![if_token.span.clone(), input.span()],
68                        MissingIfBranch {
69                            keyword: "else",
70                        },
71                    ));
72                    Garbage::garbage()
73                })
74        };
75        let span = if let Some(else_expr) = &else_expr {
76            if_token.span.start..else_expr.span().end
77        } else {
78            if_token.span.start..then_expr.span().end
79        };
80
81        Ok(Self {
82            condition: Box::new(condition),
83            then_expr: Box::new(then_expr),
84            else_expr: else_expr.map(Box::new),
85            span,
86            if_span: if_token.span,
87            else_span: else_token.map(|token| token.span),
88        })
89    }
90}
91
92impl std::fmt::Display for If {
93    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
94        write!(f, "if ")?;
95        self.condition.fmt(f)?;
96        self.then_expr.fmt(f)?;
97        if let Some(else_expr) = &self.else_expr {
98            write!(f, " else ")?;
99            else_expr.fmt(f)?;
100        }
101        Ok(())
102    }
103}
104
105impl Latex for If {
106    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
107        write!(f, "\\text{{if }}")?;
108        self.condition.fmt_latex(f)?;
109        self.then_expr.fmt_latex(f)?;
110        if let Some(else_expr) = &self.else_expr {
111            write!(f, "\\text{{ else }}")?;
112            else_expr.fmt_latex(f)?;
113        }
114        Ok(())
115    }
116}