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 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 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 #[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 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 pub fn with_thread(&mut self) -> &mut Self {
185 self.with_specifier_replacing(ThreadLinkage {
186 span: Span::MISSING,
187 })
188 }
189
190 pub fn with_export(&mut self) -> &mut Self {
194 self.with_specifier_replacing(ExportLinkage {
195 span: Span::MISSING,
196 })
197 }
198 #[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 #[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 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 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 #[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 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 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#[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#[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}