qbe_parser/ast/
linkage.rs

1use crate::ast::{AstString, Span, StringLiteral};
2use crate::lexer::{TokenParser, keyword};
3use crate::parse::{Parse, impl_fromstr_via_parse, maybe_newline};
4use crate::utils::IterExt;
5use arrayvec::ArrayVec;
6use chumsky::prelude::*;
7use std::fmt::{self, Display, Formatter};
8
9#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
10pub struct LinkageSection {
11    pub span: Span,
12    pub name: StringLiteral,
13    pub flags: Option<StringLiteral>,
14}
15impl Display for LinkageSection {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "section {}", self.name)?;
18        if let Some(ref flags) = self.flags {
19            write!(f, " {flags}")?;
20        }
21        Ok(())
22    }
23}
24impl Parse for LinkageSection {
25    const DESC: &'static str = "linkage section";
26
27    fn parser<'a>() -> impl TokenParser<'a, Self> {
28        keyword!(section)
29            .parser()
30            .ignore_then(StringLiteral::parser())
31            .then(StringLiteral::parser().or_not())
32            .map_with(|(name, flags), extra| LinkageSection {
33                name,
34                flags,
35                span: extra.span(),
36            })
37    }
38}
39#[derive(Clone, Debug, Eq, PartialEq, Hash, Default)]
40#[non_exhaustive]
41pub struct Linkage {
42    span: Span,
43    // with 3 entries, linear search is fast
44    // We want to preserve insertion order,
45    // and this avoids needing an IndexMap
46    specifiers: ArrayVec<LinkageSpecifier, 3>,
47}
48impl Parse for Linkage {
49    const DESC: &'static str = "linkage";
50    fn parser<'a>() -> impl TokenParser<'a, Self> {
51        LinkageSpecifier::parser()
52            .then_ignore(maybe_newline())
53            .repeated()
54            .collect::<Vec<LinkageSpecifier>>()
55            .try_map(|specifiers, span| {
56                Linkage::from_specifiers(span, specifiers).map_err(|e| Rich::custom(span, e))
57            })
58            .labelled(Self::DESC)
59    }
60}
61impl_fromstr_via_parse!(Linkage);
62macro_rules! linkage_extract_item {
63    ($this:expr => $variant:ident) => {{
64        match $this.get(LinkageSpecifierKind::$variant) {
65            Some(LinkageSpecifier::$variant(value)) => Some(value),
66            Some(other) => unreachable!("{:?}", other.kind()),
67            None => None,
68        }
69    }};
70}
71impl Linkage {
72    pub fn span(&self) -> Span {
73        self.span
74    }
75    pub fn is_empty(&self) -> bool {
76        self.specifiers.is_empty()
77    }
78    pub fn is_export(&self) -> bool {
79        self.has_specifier(LinkageSpecifierKind::Export)
80    }
81    pub fn is_thread(&self) -> bool {
82        self.has_specifier(LinkageSpecifierKind::Thread)
83    }
84    pub fn export(&self) -> Option<&'_ ExportLinkage> {
85        linkage_extract_item!(self => Export)
86    }
87    pub fn thread(&self) -> Option<&'_ ThreadLinkage> {
88        linkage_extract_item!(self => Thread)
89    }
90    pub fn section(&self) -> Option<&'_ LinkageSection> {
91        linkage_extract_item!(self => Section)
92    }
93    pub fn from_specifiers(
94        span: Span,
95        specifiers: impl IntoIterator<Item = LinkageSpecifier>,
96    ) -> Result<Self, DuplicateSpecifierError> {
97        let mut result = Linkage {
98            span,
99            specifiers: ArrayVec::new(),
100        };
101        for spec in specifiers {
102            let kind = spec.kind();
103            if result.has_specifier(kind) {
104                return Err(DuplicateSpecifierError { kind });
105            } else {
106                result.specifiers.push(spec);
107            }
108        }
109        Ok(result)
110    }
111    #[inline]
112    fn get(&self, kind: LinkageSpecifierKind) -> Option<&LinkageSpecifier> {
113        // Emulate filter + Itertools::exactly_one
114        let mut res = None;
115        for entry in &self.specifiers {
116            if entry.kind() == kind {
117                assert!(res.is_none(), "Internal Error: Duplicate {kind:?} entries");
118                res = Some(entry);
119            }
120        }
121        res
122    }
123    #[inline]
124    pub fn has_specifier(&self, kind: LinkageSpecifierKind) -> bool {
125        self.get(kind).is_some()
126    }
127    #[inline]
128    pub fn specifier_kinds(&self) -> impl Iterator<Item = LinkageSpecifierKind> + '_ {
129        self.specifiers.iter().map(LinkageSpecifier::kind)
130    }
131    #[inline]
132    pub fn specifiers(&self) -> impl Iterator<Item = &'_ LinkageSpecifier> + '_ {
133        self.specifiers.iter()
134    }
135    pub fn builder() -> LinkageBuilder {
136        LinkageBuilder::default()
137    }
138}
139impl Display for Linkage {
140    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
141        write!(f, "{}", self.specifiers.iter().format(" "))
142    }
143}
144impl From<Linkage> for LinkageBuilder {
145    fn from(linkage: Linkage) -> Self {
146        LinkageBuilder { linkage }
147    }
148}
149#[derive(Default)]
150pub struct LinkageBuilder {
151    linkage: Linkage,
152}
153impl LinkageBuilder {
154    /// Add a specifier to the linkage, panicking if already specified.
155    ///
156    /// # Panics
157    /// If the specifier conflicts with an already existing specifier, this will panic.
158    /// If this is not desired,
159    /// use [`Self::with_specifier_replacing`] or [`Self::try_with_specifier`].
160    #[track_caller]
161    pub fn with_specifier(&mut self, specifier: impl Into<LinkageSpecifier>) -> &mut Self {
162        let specifier = specifier.into();
163        if let Some(existing) = self.linkage.get(specifier.kind()) {
164            panic!("Specifier `{specifier}` conflicts with existing specifier `{existing}`")
165        } else {
166            self.linkage.specifiers.push(specifier);
167            self
168        }
169    }
170    /// Add a specifier to the linkage.
171    /// If a matching specifier already exists, it will replace it.
172    ///
173    /// Thin wrapper around [`Self::replace_specifier`], which discards the old specifier.
174    pub fn with_specifier_replacing(
175        &mut self,
176        specifier: impl Into<LinkageSpecifier>,
177    ) -> &mut Self {
178        self.replace_specifier(specifier);
179        self
180    }
181    /// Marks the linkage as [`thread`](ThreadLinkage) if not already marked as such.
182    ///
183    /// Does nothing if that linkage has already been specified.
184    pub fn with_thread(&mut self) -> &mut Self {
185        self.with_specifier_replacing(ThreadLinkage {
186            span: Span::MISSING,
187        })
188    }
189
190    /// Marks the linkage as [`export`](ExportLinkage) if not already marked as such.
191    ///
192    /// Does nothing if that linkage has already been specified.
193    pub fn with_export(&mut self) -> &mut Self {
194        self.with_specifier_replacing(ExportLinkage {
195            span: Span::MISSING,
196        })
197    }
198    /// Add a [`LinkageSection`] with just a name (no flags).
199    ///
200    /// # Panics
201    /// Will panic if a section has already been specified.
202    #[track_caller]
203    pub fn with_simple_section(&mut self, name: impl Into<AstString>) -> &mut Self {
204        self.with_specifier(LinkageSection {
205            span: Span::MISSING,
206            name: StringLiteral::unspanned(name),
207            flags: None,
208        })
209    }
210    /// Add a [`LinkageSection`] with both a name and flags.
211    ///
212    /// # Panics
213    /// If a section has already been specified, this will panic
214    #[track_caller]
215    pub fn with_section_and_flags(
216        &mut self,
217        name: impl Into<AstString>,
218        flags: impl Into<AstString>,
219    ) -> &mut Self {
220        self.with_specifier(LinkageSection {
221            span: Span::MISSING,
222            name: StringLiteral::unspanned(name),
223            flags: Some(StringLiteral::unspanned(flags)),
224        })
225    }
226    /// Try to add a specifier to the linkage,
227    /// returning an error if it conflicts with an existing specifier.
228    pub fn try_with_specifier(
229        &mut self,
230        specifier: impl Into<LinkageSpecifier>,
231    ) -> Result<&mut Self, DuplicateSpecifierError> {
232        let specifier = specifier.into();
233        if self.linkage.has_specifier(specifier.kind()) {
234            Err(DuplicateSpecifierError {
235                kind: specifier.kind(),
236            })
237        } else {
238            self.linkage.specifiers.push(specifier);
239            Ok(self)
240        }
241    }
242    /// Add a specifier to the linkage,
243    /// overriding any conflicting specifier.
244    ///
245    /// Returns the old specifier if present
246    pub fn replace_specifier(
247        &mut self,
248        specifier: impl Into<LinkageSpecifier>,
249    ) -> Option<LinkageSpecifier> {
250        let specifier = specifier.into();
251        let index = self
252            .linkage
253            .specifiers
254            .iter()
255            .position(|item| item.kind() == specifier.kind());
256        match index {
257            Some(index) => Some(std::mem::replace(
258                &mut self.linkage.specifiers[index],
259                specifier,
260            )),
261            None => {
262                self.linkage.specifiers.push(specifier);
263                None
264            }
265        }
266    }
267    #[inline]
268    pub fn with_span(&mut self, span: Span) -> &mut Self {
269        self.linkage.span = span;
270        self
271    }
272    pub fn build(&mut self) -> Linkage {
273        self.linkage.clone()
274    }
275}
276
277#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
278#[error("Linkage contains duplicate `{kind}` specifiers")]
279pub struct DuplicateSpecifierError {
280    kind: LinkageSpecifierKind,
281}
282macro_rules! declare_specifiers {
283    (enum LinkageSpecifier {
284        $($variant:ident($inner:ty)),+ $(,)?
285    }) => {
286        /// The kind of [`LinkageSpecifier`].
287        #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
288        #[non_exhaustive]
289        #[repr(usize)]
290        pub enum LinkageSpecifierKind {
291            $($variant,)*
292        }
293        impl LinkageSpecifierKind {
294            /// The number of different kinds.
295            pub const COUNT: usize = declare_specifiers!(@count $($variant),*);
296            #[inline]
297            pub fn as_str(self) -> &'static str {
298                match self {
299                    $(LinkageSpecifierKind::$variant => paste3::paste!(stringify!([<$variant:lower>])),)*
300                }
301            }
302            #[inline]
303            pub fn index(self) -> usize {
304                self as usize
305            }
306            #[inline]
307            pub fn from_index(idx: usize) -> Option<LinkageSpecifierKind> {
308                if idx < Self::COUNT {
309                    // SAFETY: Performed the appropriate bounds check
310                    Some(unsafe { std::mem::transmute::<usize, Self>(idx) })
311                } else {
312                    None
313                }
314            }
315        }
316        impl Display for LinkageSpecifierKind {
317            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
318                f.write_str(self.as_str())
319            }
320        }
321        #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
322        #[non_exhaustive]
323        pub enum LinkageSpecifier {
324            $($variant($inner),)*
325        }
326        impl LinkageSpecifier {
327            #[inline]
328            pub fn kind(&self) -> LinkageSpecifierKind {
329                match self {
330                    $(Self::$variant(_) => LinkageSpecifierKind::$variant,)*
331                }
332            }
333            #[inline]
334            pub fn span(&self) -> Span {
335                match self {
336                    $(Self::$variant(inner) => inner.span,)*
337                }
338            }
339        }
340        impl Display for LinkageSpecifier {
341            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
342                match self {
343                    $(Self::$variant(inner) => write!(f, "{inner}"),)*
344                }
345            }
346        }
347        impl Parse for LinkageSpecifier {
348            const DESC: &'static str = "linkage specifier";
349            fn parser<'a>() -> impl TokenParser<'a, Self> {
350                choice((
351                    $(<$inner as Parse>::parser().map(LinkageSpecifier::$variant)),*
352                )).labelled(Self::DESC)
353            }
354        }
355        $(impl From<$inner> for LinkageSpecifier {
356            #[inline]
357            fn from(v: $inner) -> Self {
358                Self::$variant(v)
359            }
360        })*
361    };
362    (@count) => (0);
363    (@count $first:ident $(, $item:ident)* $(,)?) => {
364        1 + declare_specifiers!(@count $($item),*)
365    }
366}
367declare_specifiers!(
368    enum LinkageSpecifier {
369        Export(ExportLinkage),
370        Thread(ThreadLinkage),
371        Section(LinkageSection),
372    }
373);
374
375/// Specifies `export` [linkage](Linkage).
376#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
377pub struct ExportLinkage {
378    pub span: Span,
379}
380impl Display for ExportLinkage {
381    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
382        f.write_str("export")
383    }
384}
385impl Parse for ExportLinkage {
386    const DESC: &'static str = "export linkage spec";
387    fn parser<'a>() -> impl TokenParser<'a, Self> {
388        keyword!(export).parser().map(|span| ExportLinkage { span })
389    }
390}
391/// Specifies `thread` [linkage](Linkage).
392#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
393pub struct ThreadLinkage {
394    pub span: Span,
395}
396impl Display for ThreadLinkage {
397    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
398        f.write_str("thread")
399    }
400}
401impl Parse for ThreadLinkage {
402    const DESC: &'static str = "thread linkage spec";
403    fn parser<'a>() -> impl TokenParser<'a, Self> {
404        keyword!(thread).parser().map(|span| ThreadLinkage { span })
405    }
406}
407
408#[cfg(test)]
409mod test {
410    use super::*;
411    use similar_asserts::assert_eq;
412
413    fn export() -> LinkageSpecifier {
414        ExportLinkage {
415            span: Span::MISSING,
416        }
417        .into()
418    }
419
420    fn thread() -> LinkageSpecifier {
421        ThreadLinkage {
422            span: Span::MISSING,
423        }
424        .into()
425    }
426
427    fn builder() -> LinkageBuilder {
428        LinkageBuilder::default()
429    }
430
431    fn linkage<const N: usize>(sections: [LinkageSpecifier; N]) -> Linkage {
432        assert!(sections.len() <= LinkageSpecifierKind::COUNT);
433        Linkage::from_specifiers(Span::MISSING, sections).unwrap()
434    }
435
436    #[test]
437    fn parse_linkage() {
438        assert_eq!("".parse::<Linkage>().unwrap(), linkage([]));
439        assert_eq!("export".parse::<Linkage>().unwrap(), linkage([export()]),);
440        assert_eq!(
441            "export thread".parse::<Linkage>().unwrap(),
442            linkage([export(), thread()]),
443        );
444        assert_eq!(
445            "thread\nexport".parse::<Linkage>().unwrap(),
446            linkage([thread(), export()]),
447        );
448        assert_eq!(
449            "export thread section \"foo\"".parse::<Linkage>().unwrap(),
450            builder()
451                .with_export()
452                .with_thread()
453                .with_simple_section("foo")
454                .build()
455        );
456        assert_eq!(
457            "export thread section \"foo\" \"flags\""
458                .parse::<Linkage>()
459                .unwrap(),
460            builder()
461                .with_export()
462                .with_thread()
463                .with_section_and_flags("foo", "flags")
464                .build(),
465        );
466    }
467
468    #[test]
469    fn print_linkage() {
470        assert_eq!("", Linkage::default().to_string());
471        assert_eq!("export", linkage([export()]).to_string());
472        assert_eq!("export thread", linkage([export(), thread()]).to_string());
473        assert_eq!("thread export", linkage([thread(), export()]).to_string());
474        assert_eq!(
475            "export thread section \"foo\"",
476            builder()
477                .with_export()
478                .with_thread()
479                .with_simple_section("foo")
480                .build()
481                .to_string()
482        );
483        assert_eq!(
484            "export thread section \"foo\" \"flags\"",
485            builder()
486                .with_export()
487                .with_thread()
488                .with_section_and_flags("foo", "flags")
489                .build()
490                .to_string()
491        );
492    }
493}