qbe_parser/ast/
data.rs

1use crate::ast::linkage::Linkage;
2use crate::ast::types::{AlignSpec, ExtendedType};
3use crate::ast::{FloatLiteral, GlobalName, Number, NumericLiteral, Span, StringLiteral};
4use crate::lexer::{Token, TokenParser, keyword, operator};
5use crate::parse::{Parse, impl_fromstr_via_parse, maybe_newline};
6use std::fmt::{self, Display, Formatter, Write};
7
8use crate::print::{IndentedPrinter, impl_display_via_print};
9use chumsky::prelude::*;
10
11#[derive(Debug, Eq, PartialEq, Hash, Clone)]
12pub struct DataDef {
13    pub span: Span,
14    pub linkage: Linkage,
15    pub name: GlobalName,
16    pub align: Option<AlignSpec>,
17    pub fields: Vec<DataField>,
18}
19impl DataDef {
20    // dummy method for enum getter
21    pub(crate) fn span(&self) -> Span {
22        self.span
23    }
24    fn print(&self, out: &mut IndentedPrinter) -> fmt::Result {
25        if self.linkage != Linkage::default() {
26            write!(out, "{} ", self.linkage)?;
27        }
28        write!(out, "data {} = ", self.name)?;
29        if let Some(ref align) = self.align {
30            write!(out, "{align} ")?;
31        }
32        out.write_char('{')?;
33        out.indented(|out| {
34            out.maybe_writeln()?;
35            out.print_separated_with(",\n", &self.fields, |field, out| write!(out, "{field}"))
36        })?;
37        out.maybe_writeln()?;
38        out.write_char('}')
39    }
40}
41impl_display_via_print!(DataDef);
42
43impl Parse for DataDef {
44    const DESC: &'static str = "data definition";
45    #[allow(clippy::unused_unit, reason = "part of select macro")]
46    fn parser<'a>() -> impl TokenParser<'a, Self> {
47        let body = DataField::parser()
48            .padded_by(maybe_newline())
49            .separated_by(operator!(,).parser())
50            .allow_trailing()
51            .collect::<Vec<_>>()
52            .delimited_by(just(Token::OpenBrace), just(Token::CloseBrace));
53        Linkage::parser()
54            .then_ignore(keyword!(data).parser().padded_by(maybe_newline()))
55            .then(GlobalName::parser().padded_by(maybe_newline()))
56            .then_ignore(operator!(=).parser().padded_by(maybe_newline()))
57            .then(AlignSpec::parser().padded_by(maybe_newline()).or_not())
58            .then(body)
59            .map_with(|(((linkage, name), align), body), extra| DataDef {
60                span: extra.span(),
61                linkage,
62                name,
63                align,
64                fields: body,
65            })
66            .labelled(Self::DESC)
67    }
68}
69
70/// A field in a [`DataDef`]
71#[derive(Debug, Eq, PartialEq, Hash, Clone)]
72#[non_exhaustive]
73pub enum DataField {
74    Regular {
75        span: Span,
76        ty: ExtendedType,
77        items: Vec<DataItem>,
78    },
79    ZeroInitialize {
80        span: Span,
81        count: NumericLiteral<u64>,
82    },
83}
84impl Parse for DataField {
85    const DESC: &'static str = "data field";
86    fn parser<'a>() -> impl TokenParser<'a, Self> {
87        let regular_field = ExtendedType::parser()
88            .then(
89                DataItem::parser()
90                    .repeated()
91                    .at_least(1)
92                    .collect::<Vec<_>>(),
93            )
94            .map_with(|(ty, items), extra| DataField::Regular {
95                ty,
96                span: extra.span(),
97                items,
98            })
99            .labelled("regular field");
100        let zero_init = operator!(z)
101            .parser()
102            .ignore_then(Token::number())
103            .map_with(|count, extra| DataField::ZeroInitialize {
104                span: extra.span(),
105                count,
106            })
107            .labelled("zero initialized field");
108        zero_init.or(regular_field).labelled(Self::DESC)
109    }
110}
111impl Display for DataField {
112    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
113        match self {
114            DataField::Regular { span: _, ty, items } => {
115                write!(f, "{ty}")?;
116                for val in items {
117                    write!(f, " {val}")?;
118                }
119                Ok(())
120            }
121            DataField::ZeroInitialize { span: _, count } => {
122                write!(f, "z {count}")
123            }
124        }
125    }
126}
127/// Represents the offset in a [`DataItem::SymbolRefWithOffset`].
128///
129/// This is an opaque type in case negative offsets are later added.
130/// For now, the IR definition restricts it to nonnegative numbers.
131#[derive(Debug, Eq, PartialEq, Hash, Clone, Ord, PartialOrd)]
132pub struct SymbolOffset(NumericLiteral<u64>);
133impl SymbolOffset {
134    pub fn unspanned<T: Number>(value: T) -> Self
135    where
136        Self: From<NumericLiteral<T>>,
137    {
138        NumericLiteral::unspanned(value).into()
139    }
140}
141impl From<NumericLiteral<u64>> for SymbolOffset {
142    fn from(value: NumericLiteral<u64>) -> Self {
143        SymbolOffset(value)
144    }
145}
146impl Parse for SymbolOffset {
147    const DESC: &'static str = "symbol offset";
148
149    fn parser<'a>() -> impl TokenParser<'a, Self> {
150        Token::number().map(SymbolOffset)
151    }
152}
153impl Display for SymbolOffset {
154    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
155        Display::fmt(&self.0, f)
156    }
157}
158#[derive(Debug, Eq, PartialEq, Hash, Clone)]
159#[non_exhaustive]
160pub enum DataItem {
161    /// A reference to a symbol with an explicit offset.
162    ///
163    /// If the offset is not present,
164    /// this will instead be parsed as a [`Constant::SymbolRef`].
165    SymbolRefWithOffset {
166        span: Span,
167        name: GlobalName,
168        offset: SymbolOffset,
169    },
170    String(StringLiteral),
171    Constant(Constant),
172}
173impl Parse for DataItem {
174    const DESC: &'static str = "data item";
175
176    fn parser<'a>() -> impl TokenParser<'a, Self> {
177        let offset_symbol_ref = GlobalName::parser()
178            .then_ignore(operator!(+).parser().padded_by(maybe_newline()))
179            .then(SymbolOffset::parser())
180            .map_with(|(name, offset), extra| DataItem::SymbolRefWithOffset {
181                span: extra.span(),
182                name,
183                offset,
184            })
185            .labelled("symbol and offset");
186        choice((
187            offset_symbol_ref,
188            StringLiteral::parser().map(DataItem::String),
189            Constant::parser().map(DataItem::Constant),
190        ))
191        .labelled(Self::DESC)
192    }
193}
194impl Display for DataItem {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        match self {
197            DataItem::SymbolRefWithOffset {
198                span: _,
199                name,
200                offset,
201            } => {
202                write!(f, "{name} + {offset}")
203            }
204            DataItem::String(lit) => write!(f, "{lit}"),
205            DataItem::Constant(value) => write!(f, "{value}"),
206        }
207    }
208}
209
210#[derive(PartialEq, Eq, Clone, Debug, Hash)]
211#[non_exhaustive]
212pub enum Constant {
213    Integer(NumericLiteral<i128>),
214    Float(FloatLiteral),
215    SymbolRef(GlobalName),
216}
217impl Parse for Constant {
218    const DESC: &'static str = "constant";
219    fn parser<'a>() -> impl TokenParser<'a, Self> {
220        select! {
221            Token::GlobalName(name) => Constant::SymbolRef(name),
222            Token::Float(literal) => Constant::Float(literal),
223            Token::Number(value) => Constant::Integer(value.map_value(i128::from)),
224            Token::Integer(value) => Constant::Integer(value),
225        }
226        .labelled(Self::DESC)
227    }
228}
229impl_fromstr_via_parse!(Constant);
230impl Display for Constant {
231    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232        match self {
233            Constant::Integer(i) => write!(f, "{i}"),
234            Constant::Float(lit) => write!(f, "{lit}"),
235            Constant::SymbolRef(symbol) => write!(f, "{symbol}"),
236        }
237    }
238}
239impl From<FloatLiteral> for Constant {
240    fn from(value: FloatLiteral) -> Self {
241        Constant::Float(value)
242    }
243}
244macro_rules! impl_from_primint {
245    ($($target:ty),+ $(,)?) => {
246        $(impl From<$target> for Constant {
247            fn from(value: $target) -> Self {
248                (value as i128).into()
249            }
250        })*
251    };
252}
253impl_from_primint!(i32, i64, isize, u32, u64, usize);
254impl From<i128> for Constant {
255    fn from(value: i128) -> Self {
256        NumericLiteral::unspanned(value).into()
257    }
258}
259impl From<NumericLiteral<i128>> for Constant {
260    fn from(value: NumericLiteral<i128>) -> Self {
261        Constant::Integer(value)
262    }
263}
264
265#[cfg(test)]
266mod test {
267    use super::*;
268    use crate::parse::test_parse_print;
269    use similar_asserts::assert_eq;
270
271    fn integer(x: i128) -> Constant {
272        Constant::Integer(NumericLiteral::unspanned(x))
273    }
274
275    macro_rules! test_constants {
276        ($($text:literal => $value:expr),+ $(,)?) => {
277            test_parse_print! {
278                parse_constant, print_constant for Constant {
279                    $($text => $value),*
280                }
281            }
282            test_parse_print! {
283                parse_constant_data_item, print_constant_data_item for DataItem {
284                    $($text => DataItem::Constant($value)),*
285                }
286            }
287        };
288    }
289    test_constants! {
290        "128" => integer(128),
291        "-128" => integer(-128),
292        "d_8.7" => Constant::Float(FloatLiteral::double_unspanned(8.7)),
293        "s_8.7" => Constant::Float(FloatLiteral::single_unspanned(8.7)),
294        "$foo" => Constant::SymbolRef(GlobalName::unspanned("foo")),
295    }
296    test_parse_print! {
297        // NOTE: tests for DataItem::Constant already handled above
298        parse_data_item, print_data_item for DataItem {
299            "$foo + 3" => DataItem::SymbolRefWithOffset {
300                span: Span::MISSING,
301                name: GlobalName::unspanned("foo"),
302                offset: SymbolOffset::unspanned(3),
303            },
304            // must parse as regular constant, not as SymbolWithOffset
305            "$foo" => DataItem::Constant(Constant::SymbolRef(GlobalName::unspanned("foo"))),
306            "\"foo\"" => DataItem::String(StringLiteral::unspanned("foo")),
307        }
308    }
309    test_parse_print! {
310        parse_data_field, print_data_field for DataField {
311            "w 12 $foo \"bar\"" => DataField::Regular {
312                ty: ExtendedType::Word,
313                span: Span::MISSING,
314                items: vec![
315                    DataItem::Constant(integer(12)),
316                    DataItem::Constant(Constant::SymbolRef(GlobalName::unspanned("foo"))),
317                    DataItem::String(StringLiteral::unspanned("bar")),
318                ]
319            },
320            "z 80" => DataField::ZeroInitialize {
321                span: Span::MISSING,
322                count: NumericLiteral::unspanned(80),
323            }
324        }
325    }
326    test_parse_print! {
327        parse_data_def, print_data_def for DataDef {
328            "export data $example = align 16 {
329                w 12 $foo,
330                z 80,
331                l \"bar\"
332            }" => DataDef {
333                linkage: Linkage::builder().with_export().build(),
334                span: Span::MISSING,
335                name: GlobalName::unspanned("example"),
336                align: Some(AlignSpec::unspanned(16)),
337                fields: vec![
338                   DataField::Regular {
339                        ty: ExtendedType::Word,
340                        span: Span::MISSING,
341                        items: vec![
342                            DataItem::Constant(integer(12)),
343                            DataItem::Constant(Constant::SymbolRef(GlobalName::unspanned("foo"))),
344                        ]
345                    },
346                    DataField::ZeroInitialize {
347                        span: Span::MISSING,
348                        count: NumericLiteral::unspanned(80),
349                    },
350                    DataField::Regular {
351                        ty: ExtendedType::Long,
352                        span: Span::MISSING,
353                        items: vec![DataItem::String(StringLiteral::unspanned("bar"))]
354                    }
355                ]
356            }
357        }
358    }
359}