Skip to main content

mago_type_syntax/ast/
callable.rs

1use serde::Serialize;
2use strum::Display;
3
4use mago_span::HasSpan;
5use mago_span::Span;
6use mago_syntax_core::ast::Sequence;
7
8use crate::ast::Type;
9use crate::ast::VariableType;
10use crate::ast::keyword::Keyword;
11
12#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
13#[serde(tag = "type", content = "value")]
14pub enum CallableTypeKind {
15    Callable,
16    PureCallable,
17    Closure,
18    PureClosure,
19}
20
21#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
22pub struct CallableType<'arena> {
23    pub kind: CallableTypeKind,
24    pub keyword: Keyword<'arena>,
25    pub specification: Option<CallableTypeSpecification<'arena>>,
26}
27
28#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
29pub struct CallableTypeSpecification<'arena> {
30    pub parameters: CallableTypeParameters<'arena>,
31    pub return_type: Option<CallableTypeReturnType<'arena>>,
32}
33
34#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
35pub struct CallableTypeParameters<'arena> {
36    pub left_parenthesis: Span,
37    pub entries: Sequence<'arena, CallableTypeParameter<'arena>>,
38    pub right_parenthesis: Span,
39}
40
41#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
42pub struct CallableTypeParameter<'arena> {
43    pub parameter_type: Option<Type<'arena>>,
44    pub ampersand: Option<Span>,
45    pub equals: Option<Span>,
46    pub ellipsis: Option<Span>,
47    pub variable: Option<VariableType<'arena>>,
48    pub comma: Option<Span>,
49}
50
51#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
52pub struct CallableTypeReturnType<'arena> {
53    pub colon: Span,
54    pub return_type: &'arena Type<'arena>,
55}
56
57impl CallableTypeKind {
58    #[inline]
59    #[must_use]
60    pub fn is_pure(&self) -> bool {
61        matches!(self, CallableTypeKind::PureCallable | CallableTypeKind::PureClosure)
62    }
63
64    #[inline]
65    #[must_use]
66    pub fn is_closure(&self) -> bool {
67        matches!(self, CallableTypeKind::Closure | CallableTypeKind::PureClosure)
68    }
69}
70
71impl CallableTypeParameter<'_> {
72    #[inline]
73    #[must_use]
74    pub const fn is_variadic(&self) -> bool {
75        self.ellipsis.is_some()
76    }
77
78    #[inline]
79    #[must_use]
80    pub const fn is_optional(&self) -> bool {
81        self.equals.is_some()
82    }
83
84    #[inline]
85    #[must_use]
86    pub const fn is_by_reference(&self) -> bool {
87        self.ampersand.is_some()
88    }
89}
90
91impl HasSpan for CallableType<'_> {
92    fn span(&self) -> Span {
93        match &self.specification {
94            Some(specification) => self.keyword.span.join(specification.span()),
95            None => self.keyword.span,
96        }
97    }
98}
99
100impl HasSpan for CallableTypeSpecification<'_> {
101    fn span(&self) -> Span {
102        match &self.return_type {
103            Some(return_type) => self.parameters.span().join(return_type.span()),
104            None => self.parameters.span(),
105        }
106    }
107}
108
109impl HasSpan for CallableTypeParameters<'_> {
110    fn span(&self) -> Span {
111        self.left_parenthesis.join(self.right_parenthesis)
112    }
113}
114
115impl HasSpan for CallableTypeParameter<'_> {
116    fn span(&self) -> Span {
117        let start = match &self.parameter_type {
118            Some(parameter_type) => parameter_type.span(),
119            None => self
120                .ampersand
121                .or(self.equals)
122                .or(self.ellipsis)
123                .or(self.variable.as_ref().map(mago_span::HasSpan::span))
124                .or(self.comma)
125                .unwrap_or_else(Span::zero),
126        };
127
128        let end = self
129            .comma
130            .or(self.variable.as_ref().map(mago_span::HasSpan::span))
131            .or(self.ellipsis)
132            .or(self.equals)
133            .or(self.ampersand)
134            .unwrap_or(start);
135
136        start.join(end)
137    }
138}
139
140impl HasSpan for CallableTypeReturnType<'_> {
141    fn span(&self) -> Span {
142        self.colon.join(self.return_type.span())
143    }
144}
145
146impl std::fmt::Display for CallableTypeReturnType<'_> {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        write!(f, ": {}", self.return_type)
149    }
150}
151
152impl std::fmt::Display for CallableTypeParameter<'_> {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        if let Some(parameter_type) = &self.parameter_type {
155            write!(f, "{parameter_type}")?;
156        }
157
158        if self.ampersand.is_some() {
159            write!(f, " &")?;
160        }
161
162        if self.equals.is_some() {
163            write!(f, "=")?;
164        } else if self.ellipsis.is_some() {
165            write!(f, "...")?;
166        } else {
167            // No default marker: parameter is required and not variadic.
168        }
169
170        if let Some(variable) = &self.variable {
171            write!(f, " {variable}")?;
172        }
173
174        Ok(())
175    }
176}
177
178impl std::fmt::Display for CallableTypeParameters<'_> {
179    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180        write!(f, "(")?;
181        for (i, entry) in self.entries.iter().enumerate() {
182            if i > 0 {
183                write!(f, ", ")?;
184            }
185            write!(f, "{entry}")?;
186        }
187        write!(f, ")")?;
188        Ok(())
189    }
190}
191
192impl std::fmt::Display for CallableTypeSpecification<'_> {
193    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194        write!(f, "{}", self.parameters)?;
195        if let Some(return_type) = &self.return_type {
196            write!(f, "{return_type}")?;
197        }
198        Ok(())
199    }
200}
201
202impl std::fmt::Display for CallableType<'_> {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        write!(f, "{}", self.keyword)?;
205        if let Some(specification) = &self.specification {
206            write!(f, "{specification}")?;
207        }
208        Ok(())
209    }
210}