arithmetic_parser/ast/
lvalue.rs

1//! Lvalues for arithmetic expressions.
2
3use core::fmt;
4
5use crate::{alloc::Vec, spans::Spanned};
6
7/// Length of an assigned lvalue.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum LvalueLen {
11    /// Exact length.
12    Exact(usize),
13    /// Minimum length.
14    AtLeast(usize),
15}
16
17impl LvalueLen {
18    /// Checks if this length matches the provided length of the rvalue.
19    pub fn matches(self, value: usize) -> bool {
20        match self {
21            Self::Exact(len) => value == len,
22            Self::AtLeast(len) => value >= len,
23        }
24    }
25}
26
27impl fmt::Display for LvalueLen {
28    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Self::Exact(len) => write!(formatter, "{len}"),
31            Self::AtLeast(len) => write!(formatter, "at least {len}"),
32        }
33    }
34}
35
36impl From<usize> for LvalueLen {
37    fn from(value: usize) -> Self {
38        Self::Exact(value)
39    }
40}
41
42/// Tuple destructuring, such as `(a, b, ..., c)`.
43#[derive(Debug, Clone, PartialEq)]
44pub struct Destructure<'a, T> {
45    /// Start part of the destructuring, e.g, `a` and `b` in `(a, b, ..., c)`.
46    pub start: Vec<SpannedLvalue<'a, T>>,
47    /// Middle part of the destructuring, e.g., `rest` in `(a, b, ...rest, _)`.
48    pub middle: Option<Spanned<'a, DestructureRest<'a, T>>>,
49    /// End part of the destructuring, e.g., `c` in `(a, b, ..., c)`.
50    pub end: Vec<SpannedLvalue<'a, T>>,
51}
52
53impl<T> Destructure<'_, T> {
54    /// Returns the length of destructured elements.
55    pub fn len(&self) -> LvalueLen {
56        if self.middle.is_some() {
57            LvalueLen::AtLeast(self.start.len() + self.end.len())
58        } else {
59            LvalueLen::Exact(self.start.len())
60        }
61    }
62
63    /// Checks if the destructuring is empty.
64    pub fn is_empty(&self) -> bool {
65        self.start.is_empty()
66    }
67}
68
69/// Rest syntax, such as `...rest` in `(a, ...rest, b)`.
70#[derive(Debug, Clone, PartialEq)]
71pub enum DestructureRest<'a, T> {
72    /// Unnamed rest syntax, i.e., `...`.
73    Unnamed,
74    /// Named rest syntax, e.g., `...rest`.
75    Named {
76        /// Variable span, e.g., `rest`.
77        variable: Spanned<'a>,
78        /// Type annotation of the value.
79        ty: Option<Spanned<'a, T>>,
80    },
81}
82
83impl<'a, T> DestructureRest<'a, T> {
84    /// Tries to convert this rest declaration into an lvalue. Return `None` if the rest declaration
85    /// is unnamed.
86    pub fn to_lvalue(&self) -> Option<SpannedLvalue<'a, T>> {
87        match self {
88            Self::Named { variable, .. } => {
89                Some(variable.copy_with_extra(Lvalue::Variable { ty: None }))
90            }
91            Self::Unnamed => None,
92        }
93    }
94}
95
96/// Object destructuring, such as `{ x, y: new_y }`.
97#[derive(Debug, Clone, PartialEq)]
98#[non_exhaustive]
99pub struct ObjectDestructure<'a, T> {
100    /// Fields mentioned in the destructuring.
101    pub fields: Vec<ObjectDestructureField<'a, T>>,
102}
103
104/// Single field in [`ObjectDestructure`], such as `x` and `y: new_y` in `{ x, y: new_y }`.
105///
106/// In addition to the "ordinary" `field: lvalue` syntax for a field with binding,
107/// an alternative one is supported: `field -> lvalue`. This makes the case
108/// of a field with type annotation easier to recognize (for humans); `field -> lvalue: Type` is
109/// arguably more readable than `field: lvalue: Type` (although the latter is still valid syntax).
110#[derive(Debug, Clone, PartialEq)]
111pub struct ObjectDestructureField<'a, T> {
112    /// Field name, such as `xs` in `xs: (x, ...tail)`.
113    pub field_name: Spanned<'a>,
114    /// Binding for the field, such as `(x, ...tail)` in `xs: (x, ...tail)`.
115    pub binding: Option<SpannedLvalue<'a, T>>,
116}
117
118/// Assignable value.
119#[derive(Debug, Clone, PartialEq)]
120#[non_exhaustive]
121pub enum Lvalue<'a, T> {
122    /// Simple variable, e.g., `x`.
123    Variable {
124        /// Type annotation of the value.
125        ty: Option<Spanned<'a, T>>,
126    },
127    /// Tuple destructuring, e.g., `(x, y)`.
128    Tuple(Destructure<'a, T>),
129    /// Object destructuring, e.g., `{ x, y }`.
130    Object(ObjectDestructure<'a, T>),
131}
132
133impl<T> Lvalue<'_, T> {
134    /// Returns type of this lvalue.
135    pub fn ty(&self) -> LvalueType {
136        match self {
137            Self::Variable { .. } => LvalueType::Variable,
138            Self::Tuple(_) => LvalueType::Tuple,
139            Self::Object(_) => LvalueType::Object,
140        }
141    }
142}
143
144/// [`Lvalue`] with the associated code span.
145pub type SpannedLvalue<'a, T> = Spanned<'a, Lvalue<'a, T>>;
146
147/// Type of an [`Lvalue`].
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
149#[non_exhaustive]
150pub enum LvalueType {
151    /// Simple variable, e.g., `x`.
152    Variable,
153    /// Tuple destructuring, e.g., `(x, y)`.
154    Tuple,
155    /// Object destructuring, e.g., `{ x, y }`.
156    Object,
157}
158
159impl fmt::Display for LvalueType {
160    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
161        formatter.write_str(match self {
162            Self::Variable => "simple variable",
163            Self::Tuple => "tuple destructuring",
164            Self::Object => "object destructuring",
165        })
166    }
167}