Skip to main content

mago_type_syntax/
token.rs

1use serde::Deserialize;
2use serde::Serialize;
3use strum::Display;
4
5use mago_span::Span;
6
7/// Type parsing precedence levels.
8///
9/// Lower ordinal values = lower precedence = binds more loosely.
10/// Higher ordinal values = higher precedence = binds more tightly.
11///
12/// For example, in `Closure(): int|string`:
13/// - With `Lowest` precedence: parses as `Union(Closure(): int, string)` (correct PHPStan/Psalm behavior)
14/// - Callable return types use `Callable` precedence, which stops before `|`, `&`, and `is`
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
16pub enum TypePrecedence {
17    /// Lowest precedence - parses everything including unions, intersections, conditionals
18    Lowest,
19    /// Conditional types: `T is U ? V : W`
20    Conditional,
21    /// Union types: `T|U`
22    Union,
23    /// Intersection types: `T&U`
24    Intersection,
25    /// Postfix operations: `T[]`, `T[K]`
26    Postfix,
27    /// Callable return type context - stops before `|`, `&`, `is`
28    Callable,
29}
30
31#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord, Display)]
32pub enum TypeTokenKind {
33    Int,
34    Integer,
35    String,
36    Float,
37    Real,
38    Double,
39    Bool,
40    Boolean,
41    False,
42    True,
43    Object,
44    Callable,
45    Array,
46    NonEmptyArray,
47    NonEmptyString,
48    NonEmptyLowercaseString,
49    NonFalsyString,
50    LowercaseString,
51    TruthyString,
52    Iterable,
53    Null,
54    Mixed,
55    NonEmptyMixed,
56    NumericString,
57    ClassString,
58    InterfaceString,
59    TraitString,
60    EnumString,
61    StringableObject,
62    PureCallable,
63    PureClosure,
64    UnspecifiedLiteralString,
65    UnspecifiedLiteralInt,
66    UnspecifiedLiteralFloat,
67    NonEmptyUnspecifiedLiteralString,
68    Resource,
69    Void,
70    Scalar,
71    Numeric,
72    NoReturn,
73    NeverReturn,
74    NeverReturns,
75    Never,
76    Nothing,
77    ArrayKey,
78    List,
79    NonEmptyList,
80    OpenResource,
81    ClosedResource,
82    AssociativeArray,
83    KeyOf,
84    ValueOf,
85    IntMask,
86    IntMaskOf,
87    Min,
88    Max,
89    PropertiesOf,
90    PublicPropertiesOf,
91    PrivatePropertiesOf,
92    ProtectedPropertiesOf,
93    PositiveInt,
94    NegativeInt,
95    NonPositiveInt,
96    NonNegativeInt,
97    As,
98    Is,
99    Not,
100    Identifier,
101    QualifiedIdentifier,
102    FullyQualifiedIdentifier,
103    Plus,
104    Minus,
105    LessThan,
106    GreaterThan,
107    Pipe,
108    Ampersand,
109    Question,
110    Exclamation,
111    Comma,
112    Colon,
113    ColonColon,
114    LeftBrace,
115    RightBrace,
116    LeftBracket,
117    RightBracket,
118    LeftParenthesis,
119    RightParenthesis,
120    Equals,
121    Ellipsis,
122    PartialLiteralString,
123    LiteralString,
124    LiteralInteger,
125    LiteralFloat,
126    Variable,
127    Whitespace,
128    SingleLineComment,
129    Asterisk,
130}
131
132#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
133pub struct TypeToken<'input> {
134    pub kind: TypeTokenKind,
135    pub value: &'input str,
136    pub span: Span,
137}
138
139impl TypeTokenKind {
140    #[inline]
141    #[must_use]
142    pub const fn is_trivia(&self) -> bool {
143        matches!(self, Self::SingleLineComment | Self::Whitespace)
144    }
145
146    #[inline]
147    #[must_use]
148    pub const fn is_simple_identifier(&self) -> bool {
149        matches!(self, Self::Identifier)
150    }
151
152    #[inline]
153    #[must_use]
154    pub const fn is_identifier(&self) -> bool {
155        matches!(self, Self::Identifier | Self::QualifiedIdentifier | Self::FullyQualifiedIdentifier)
156    }
157
158    #[inline]
159    #[must_use]
160    pub const fn is_keyword(&self) -> bool {
161        matches!(
162            self,
163            Self::Int
164                | Self::Integer
165                | Self::Double
166                | Self::String
167                | Self::Float
168                | Self::Real
169                | Self::Bool
170                | Self::Boolean
171                | Self::False
172                | Self::True
173                | Self::Object
174                | Self::Callable
175                | Self::Array
176                | Self::NonEmptyArray
177                | Self::NonEmptyString
178                | Self::NonEmptyLowercaseString
179                | Self::LowercaseString
180                | Self::TruthyString
181                | Self::NonFalsyString
182                | Self::Iterable
183                | Self::Null
184                | Self::Mixed
185                | Self::NonEmptyMixed
186                | Self::NumericString
187                | Self::ClassString
188                | Self::InterfaceString
189                | Self::TraitString
190                | Self::EnumString
191                | Self::StringableObject
192                | Self::PureCallable
193                | Self::PureClosure
194                | Self::UnspecifiedLiteralString
195                | Self::UnspecifiedLiteralFloat
196                | Self::NonEmptyUnspecifiedLiteralString
197                | Self::Resource
198                | Self::Void
199                | Self::Scalar
200                | Self::Numeric
201                | Self::NoReturn
202                | Self::NeverReturn
203                | Self::NeverReturns
204                | Self::Never
205                | Self::Nothing
206                | Self::ArrayKey
207                | Self::List
208                | Self::NonEmptyList
209                | Self::OpenResource
210                | Self::ClosedResource
211                | Self::AssociativeArray
212                | Self::Is
213                | Self::As
214                | Self::Not
215                | Self::KeyOf
216                | Self::ValueOf
217                | Self::IntMask
218                | Self::IntMaskOf
219                | Self::Min
220                | Self::Max
221                | Self::UnspecifiedLiteralInt
222                | Self::PropertiesOf
223                | Self::PublicPropertiesOf
224                | Self::PrivatePropertiesOf
225                | Self::ProtectedPropertiesOf
226                | Self::PositiveInt
227                | Self::NegativeInt
228                | Self::NonPositiveInt
229                | Self::NonNegativeInt
230        )
231    }
232
233    #[inline]
234    #[must_use]
235    pub const fn is_array_like(&self) -> bool {
236        matches!(self, Self::Array | Self::NonEmptyArray | Self::AssociativeArray | Self::List | Self::NonEmptyList)
237    }
238}