qbe_parser/ast/
types.rs

1use std::fmt::{self, Debug, Display, Formatter, Write};
2use std::str::FromStr;
3
4use crate::ast::{NumericLiteral, Span, Spanned, TypeName};
5use crate::lexer::ShortTypeSpec;
6use crate::lexer::tokens::InvalidShortTypeSpecError;
7use crate::print::IndentedPrinter;
8
9mod parse;
10#[cfg(test)]
11mod test;
12
13/// Specifies the alignment of a type.
14#[derive(Debug, Eq, PartialEq, Hash, Clone)]
15pub struct AlignSpec {
16    pub span: Span,
17    pub value: NumericLiteral<u64>,
18}
19impl AlignSpec {
20    pub fn unspanned(value: u64) -> Self {
21        AlignSpec {
22            span: Span::MISSING,
23            value: NumericLiteral::unspanned(value),
24        }
25    }
26}
27impl Display for AlignSpec {
28    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
29        write!(f, "align {}", self.value)
30    }
31}
32
33macro_rules! simple_type {
34    (
35        $(#[$tp_attr:meta])*
36        $v:vis enum $target:ident {
37            $($variant:ident),+ $(,)?
38        }
39    ) => {
40        #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
41        $(#[$tp_attr])*
42        $v enum $target {
43            $($variant),*
44        }
45        impl $target {
46            #[inline] // should reduce to just addition
47            pub fn to_short_spec(&self) -> ShortTypeSpec {
48                match self {
49                    $(Self::$variant => ShortTypeSpec::$variant,)*
50                }
51            }
52            fn type_desc() -> &'static str {
53                let snake = paste3::paste!(stringify!([<$target:snake>]));
54                snake.strip_suffix("_type").expect(snake)
55            }
56        }
57        impl Display for $target {
58            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
59                Display::fmt(&self.to_short_spec(), f)
60            }
61        }
62        paste3::paste! {
63            #[derive(Debug, thiserror::Error, Copy, Clone)]
64            #[error("Expected a {} type, but got \"{spec}\"", $target::type_desc())]
65            pub struct [<Invalid $target Error>] {
66                spec: ShortTypeSpec
67            }
68            impl TryFrom<ShortTypeSpec> for $target {
69                type Error = [<Invalid $target:camel Error>];
70                #[inline] // should reduce to a bounds check + addition
71                fn try_from(spec: ShortTypeSpec) -> Result<Self, Self::Error> {
72                    match spec {
73                        $(ShortTypeSpec::$variant => Ok(Self::$variant),)*
74                        _ => return Err(Self::Error { spec })
75                    }
76                }
77            }
78            #[derive(Debug, thiserror::Error, Clone)]
79            #[error("Expected a {} type, but got {text:?}", $target::type_desc())]
80            pub struct [<$target ParseError>] {
81                text: String,
82            }
83            impl From<[<Invalid $target Error>]> for [<$target ParseError>] {
84                #[cold]
85                fn from(cause: [<Invalid $target Error>]) -> Self {
86                    Self { text: cause.spec.text().into() }
87                }
88            }
89            impl FromStr for $target {
90                type Err = [<$target ParseError>];
91                fn from_str(s: &str) -> Result<Self, Self::Err> {
92                    Ok(ShortTypeSpec::from_str(s)
93                        .map_err(#[cold] |InvalidShortTypeSpecError| Self::Err { text: s.into() })?
94                        .try_into()?)
95                }
96            }
97        }
98        #[cfg(test)]
99        paste3::paste! {
100            mod [<test_ $target:snake>] {
101                #[test]
102                fn desc() {
103                    super::$target::type_desc();
104                }
105            }
106        }
107    };
108}
109
110simple_type!(
111    pub enum BaseType {
112        Word,
113        Long,
114        Single,
115        Double,
116    }
117);
118impl BaseType {
119    pub fn to_extended_type(&self) -> ExtendedType {
120        ExtendedType::from(*self)
121    }
122}
123impl TryFrom<ExtendedType> for BaseType {
124    type Error = InvalidBaseTypeError;
125
126    #[inline] // should reduce to just a bounds check
127    fn try_from(value: ExtendedType) -> Result<Self, Self::Error> {
128        value.to_short_spec().try_into()
129    }
130}
131simple_type!(
132    pub enum ExtendedType {
133        // basic
134        Word,
135        Long,
136        Single,
137        Double,
138        // extended
139        Half,
140        Byte,
141    }
142);
143impl From<BaseType> for ExtendedType {
144    #[inline] // should reduce to just addition
145    fn from(value: BaseType) -> Self {
146        value.to_short_spec().try_into().unwrap()
147    }
148}
149simple_type!(
150    pub enum SubWordType {
151        SignedByte,
152        UnsignedByte,
153        SignedHalf,
154        UnsignedHalf,
155    }
156);
157impl SubWordType {
158    /// Erase the sign information, then return the corresponding [`ExtendedType`].
159    #[inline]
160    pub fn erase_sign_info(&self) -> ExtendedType {
161        match self {
162            SubWordType::SignedByte | SubWordType::UnsignedByte => ExtendedType::Byte,
163            SubWordType::SignedHalf | SubWordType::UnsignedHalf => ExtendedType::Half,
164        }
165    }
166}
167
168macro_rules! maybe_named_type {
169    (
170        $(#[$ty_attr:meta])*
171        $v:vis enum $target:ident {
172            Named(TypeName),
173            $($simple_variant:ident($simple_type:ty, Span)),+ $(,)?
174        }
175    ) => {
176        $(#[$ty_attr])*
177        #[derive(Debug, Clone)]
178        $v enum $target {
179            Named(TypeName),
180            $($simple_variant($simple_type, Span)),*
181        }
182        impl $target {
183            pub fn span(&self) -> Span {
184                match *self {
185                    $($target::$simple_variant(_, span) => span,)*
186                    $target::Named(ref name) => name.span(),
187                }
188            }
189        }
190        impl Display for $target {
191            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
192                match self {
193                    $target::Named(name) => Display::fmt(name, f),
194                    $($target::$simple_variant(simple, _) => Display::fmt(simple, f),)*
195                }
196            }
197        }
198        $(impl From<$simple_type> for $target {
199            fn from(value: $simple_type) -> Self {
200                $target::$simple_variant(value, Span::MISSING)
201            }
202        }
203        impl From<Spanned<$simple_type>> for $target {
204            fn from(value: Spanned<$simple_type>) -> Self {
205                $target::$simple_variant(value.value, value.span)
206            }
207        })*
208        impl From<TypeName> for $target {
209            #[inline]
210            fn from(value: TypeName) -> Self {
211                $target::Named(value)
212            }
213        }
214    };
215}
216
217maybe_named_type! {
218    /// Refers to an ABI type, which is either a [`BaseType`], a [`SubWordType`], or a [`TypeName`].
219    #[derive(PartialEq, Eq, Hash)]
220    pub enum AbiType {
221        Named(TypeName),
222        Base(BaseType, Span),
223        SubWord(SubWordType, Span),
224    }
225}
226maybe_named_type! {
227    /// Refers to the type of a field.
228    #[derive(PartialEq, Eq, Hash)]
229    pub enum FieldType  {
230        Named(TypeName),
231        Extended(ExtendedType, Span),
232    }
233}
234impl From<BaseType> for FieldType {
235    fn from(value: BaseType) -> Self {
236        value.to_extended_type().into()
237    }
238}
239impl From<Spanned<BaseType>> for FieldType {
240    fn from(value: Spanned<BaseType>) -> Self {
241        Spanned::map(value, ExtendedType::from).into()
242    }
243}
244#[derive(Debug, Eq, PartialEq, Hash, Clone)]
245pub struct TypeDef {
246    pub name: TypeName,
247    pub align: Option<AlignSpec>,
248    pub span: Span,
249    pub body: TypeDefBody,
250}
251impl TypeDef {
252    // dummy method for enum getter
253    pub(super) fn span(&self) -> Span {
254        self.span
255    }
256    fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
257        write!(out, "type {} = ", self.name)?;
258        if let Some(ref align) = self.align {
259            write!(out, "{align} ")?;
260        }
261        self.body.print(out)
262    }
263    pub fn validate(&self) -> Result<(), Vec<InvalidTypeDefReason>> {
264        let mut errors = Vec::new();
265        match self.body {
266            TypeDefBody::Struct(_) => {}
267            TypeDefBody::Union(ref body) => {
268                if body.variants.is_empty() {
269                    errors.push(InvalidTypeDefReason::UnionTypeEmpty);
270                }
271            }
272            TypeDefBody::Opaque(_) => {
273                if self.align.is_none() {
274                    errors.push(InvalidTypeDefReason::OpaqueMissingAlignment);
275                }
276            }
277        }
278        if errors.is_empty() {
279            Ok(())
280        } else {
281            Err(errors)
282        }
283    }
284}
285impl Display for TypeDef {
286    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
287        self.print(&mut IndentedPrinter::new(f))
288    }
289}
290/// An error that occurs calling [`InvalidTypeDefReason`].
291#[derive(Debug, thiserror::Error)]
292#[non_exhaustive]
293pub enum InvalidTypeDefReason {
294    #[error("Opaque type missing alignment")]
295    OpaqueMissingAlignment,
296    #[error("Union type has no variants")]
297    UnionTypeEmpty,
298}
299#[derive(Debug, Eq, PartialEq, Hash, Clone)]
300#[non_exhaustive]
301pub enum TypeDefBody {
302    Struct(StructBody),
303    Union(UnionBody),
304    Opaque(OpaqueBody),
305}
306impl TypeDefBody {
307    fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
308        match self {
309            TypeDefBody::Struct(body) => body.print(out),
310            TypeDefBody::Union(body) => body.print(out),
311            TypeDefBody::Opaque(body) => body.print(out),
312        }
313    }
314}
315impl Display for TypeDefBody {
316    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
317        self.print(&mut IndentedPrinter::new(f))
318    }
319}
320#[derive(Debug, Eq, PartialEq, Hash, Clone)]
321pub struct OpaqueBody {
322    pub span: Span,
323    pub size: NumericLiteral<u64>,
324}
325impl OpaqueBody {
326    fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
327        // This one is easy to print (no indentation/newlines)
328        write!(out, "{self}")
329    }
330}
331impl Display for OpaqueBody {
332    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
333        write!(f, "{{ {} }}", self.size)
334    }
335}
336#[derive(Debug, Eq, PartialEq, Hash, Clone)]
337pub struct UnionBody {
338    pub span: Span,
339    pub variants: Vec<StructBody>,
340}
341impl UnionBody {
342    fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
343        writeln!(out, "{{")?;
344        out.indented(|out| {
345            for variant in &self.variants {
346                out.maybe_writeln()?;
347                variant.print(out)?;
348            }
349            out.maybe_writeln()
350        })?;
351        write!(out, "}}")
352    }
353}
354impl Display for UnionBody {
355    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
356        self.print(&mut IndentedPrinter::new(f))
357    }
358}
359#[derive(Debug, Eq, PartialEq, Hash, Clone)]
360pub struct StructBody {
361    pub span: Span,
362    pub fields: Vec<FieldDef>,
363}
364impl StructBody {
365    fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
366        writeln!(out, "{{")?;
367        out.indented(|out| {
368            out.print_separated_with(",", &self.fields, |field, out| {
369                out.maybe_writeln()?;
370                write!(out, "{field}")
371            })
372        })?;
373        write!(out, "\n}}")
374    }
375}
376#[derive(Debug, Eq, PartialEq, Hash, Clone)]
377pub struct FieldDef {
378    pub span: Span,
379    pub ty: FieldType,
380    /// The number of times this field should be repeated.
381    pub repeated: Option<NumericLiteral<u64>>,
382}
383impl Display for FieldDef {
384    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
385        write!(f, "{}", self.ty)?;
386        if let Some(ref repeated) = self.repeated {
387            write!(f, " {repeated}")?;
388        }
389        Ok(())
390    }
391}